Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions workflows/cve-fixer/.claude/commands/cve.find.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,12 +148,12 @@ Report: artifacts/cve-fixer/find/cve-issues-20260226-145018.md

# Append subcomponent filter if provided
if [ -n "$SUBCOMPONENT" ] && [ -n "$MAPPING_FILE" ] && [ -f "$MAPPING_FILE" ]; then
# Reverse lookup: find ALL containers whose primary repo has matching subcomponent
# Reverse lookup: find ALL containers on repos with matching subcomponent (new schema)
PSCOMPONENTS=$(jq -r --arg comp "$COMPONENT_NAME" --arg sub "$SUBCOMPONENT" '
.components[$comp] as $c |
$c.container_to_repo_mapping | to_entries[] |
select($c.repositories[.value].subcomponent == $sub) |
"pscomponent:" + .key
.components[$comp].repos[] |
select(.subcomponent == $sub) |
.containers[]? |
"pscomponent:" + .
' "$MAPPING_FILE")

if [ -n "$PSCOMPONENTS" ]; then
Expand Down
77 changes: 40 additions & 37 deletions workflows/cve-fixer/.claude/commands/cve.fix.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,22 +132,21 @@ Summary:
**3.1: Use container to scope repos (preferred)**

If a `CONTAINER` was extracted in Step 1:
- Look up `CONTAINER` in `container_to_repo_mapping` for the component
- **If container not found in mapping**:
- Search all repos in `.components[COMPONENT].repos[]` for one whose `.containers[]` includes `CONTAINER`
- **If container not found**:
- Log a warning: "⚠️ Container [CONTAINER] not in mapping — may be a new container not yet registered. Processing all component repos."
- Fall back to processing all repos in the component (scan in Step 5 filters irrelevant ones)
- **If container found**: gives the **primary repo** (e.g., `opendatahub-io/workload-variant-autoscaler`)
- Check if the primary repo has a `subcomponent` field in the `repositories` section
- **If container found**: note which repo it belongs to, read its `subcomponent` field
- **If `subcomponent` is defined**: collect all repos in the component with the same `subcomponent` value — this is the chain (upstream + midstream + downstream)
- **If `subcomponent` is not defined**: process ALL repositories in the component (safe fallback — the CVE scan in Step 5 will filter out repos where the CVE doesn't exist)
- **If `subcomponent` is not defined**: process ALL repos in the component (safe fallback — the CVE scan in Step 5 will filter out repos where the CVE doesn't exist)
- **This ensures only the repos relevant to that specific container get PRs** — not repos belonging to other subcomponents

Example: `rhoai/odh-workload-variant-autoscaler-controller-rhel9` → primary repo `opendatahub-io/workload-variant-autoscaler` → `subcomponent: autoscaler` → only process `llm-d/llm-d-workload-variant-autoscaler`, `opendatahub-io/workload-variant-autoscaler`, `red-hat-data-services/workload-variant-autoscaler`.
Example: `rhoai/odh-workload-variant-autoscaler-controller-rhel9` found in repo with `subcomponent: autoscaler` → only process `llm-d/llm-d-workload-variant-autoscaler`, `opendatahub-io/workload-variant-autoscaler`, `red-hat-data-services/workload-variant-autoscaler`.

**3.2: Fallback — use all repos**

If no `CONTAINER` was extracted (summary doesn't match expected pattern):
- Process ALL repositories listed under the component
- Process all entries in `.components[COMPONENT].repos[]`
- The CVE scan in Step 5 acts as the safety net — it will skip repos where the CVE doesn't exist
- Log a warning: "⚠️ Could not extract container from summary — processing all component repos"

Expand Down Expand Up @@ -1173,12 +1172,13 @@ the fix requires additional changes beyond a version bump."
- Risk assessment table
- Links to CVE advisories
- **Jira issue references**: List the extracted Jira issue IDs as plain text WITHOUT hyperlinks
- ✅ Correct: `Resolves: RHOAIENG-17794, RHOAIENG-16619, RHOAIENG-16616`
- ❌ Wrong: `Resolves: [RHOAIENG-17794](https://redhat.atlassian.net/browse/RHOAIENG-17794)`
- ❌ Wrong: `Multiple RHOAIENG issues for CVE-2024-21538 across different release branches`
- Do NOT create markdown links for Jira issues
- Do NOT use generic descriptions - list the ACTUAL issue IDs
- Just list the issue IDs separated by commas
- ✅ Correct (plain): `Resolves: PROJ-12345`
- ✅ Correct (linked): `Resolves: [PROJ-12345](https://redhat.atlassian.net/browse/PROJ-12345)`
- ✅ Multiple issues: `Resolves: PROJ-12345, PROJ-12346` (when the same CVE has multiple tickets)
- ❌ Wrong: generic description with no IDs
- ❌ Wrong: omitting Jira IDs entirely
- Always include the actual issue IDs — the dashboard scans PR bodies to correlate
PRs with CVEs, so missing IDs break tracking
- **CREATE** the PR using GitHub CLI (with fallback to GitHub API):
```bash
# Prepare PR body
Expand Down Expand Up @@ -1237,13 +1237,22 @@ This PR fixes **CVE-YYYY-XXXXX** by upgrading <package> from X.X.X to Y.Y.Y.
---

🤖 Generated by CVE Fixer Workflow
<!-- cve-fixer-workflow -->
EOF
)

PR_URL=$(gh pr create \
--base <target-branch> \
--title "Security: Fix CVE-YYYY-XXXXX (<package-name>)" \
--body "$PR_BODY" \
--label "cve-fixer-automated" 2>/dev/null || \
gh pr create \
--base <target-branch> \
--title "Security: Fix CVE-YYYY-XXXXX (<package-name>)" \
--body "$PR_BODY")
# Note: gh pr create --label exits non-zero if the label doesn't exist.
# The fallback (without --label) ensures PR is always created even if labelling fails.
# 2>/dev/null suppresses the label-not-found error from the first attempt.

Comment on lines +1245 to 1256
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Comment contradicts the code path.

The inline note says "--label silently fails if the label doesn't exist," but the whole reason a fallback gh pr create without --label exists is that gh pr create --label <missing> returns non-zero and aborts PR creation. If it truly silently failed, the fallback wouldn't be needed. Also, 2>/dev/null on the first call swallows every stderr (auth errors, network, validation), so the fallback can retry for unrelated reasons and mask real failures.

Suggested wording + stderr handling
-       PR_URL=$(gh pr create \
-         --base <target-branch> \
-         --title "Security: Fix CVE-YYYY-XXXXX (<package-name>)" \
-         --body "$PR_BODY" \
-         --label "cve-fixer-automated" 2>/dev/null || \
-         gh pr create \
-         --base <target-branch> \
-         --title "Security: Fix CVE-YYYY-XXXXX (<package-name>)" \
-         --body "$PR_BODY")
-       # Note: --label silently fails if the label doesn't exist in the repo.
-       # The fallback without --label ensures PR is always created.
+       # `gh pr create --label <name>` fails (non-zero, no PR created) when the
+       # label does not exist in the repo. Retry once without --label so the PR
+       # is still created. Keep stderr visible so unrelated failures surface.
+       PR_URL=$(gh pr create \
+         --base <target-branch> \
+         --title "Security: Fix CVE-YYYY-XXXXX (<package-name>)" \
+         --body "$PR_BODY" \
+         --label "cve-fixer-automated") \
+       || PR_URL=$(gh pr create \
+         --base <target-branch> \
+         --title "Security: Fix CVE-YYYY-XXXXX (<package-name>)" \
+         --body "$PR_BODY")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
--base <target-branch> \
--title "Security: Fix CVE-YYYY-XXXXX (<package-name>)" \
--body "$PR_BODY" \
--label "cve-fixer-automated" 2>/dev/null || \
gh pr create \
--base <target-branch> \
--title "Security: Fix CVE-YYYY-XXXXX (<package-name>)" \
--body "$PR_BODY")
# Note: --label silently fails if the label doesn't exist in the repo.
# The fallback without --label ensures PR is always created.
# `gh pr create --label <name>` fails (non-zero, no PR created) when the
# label does not exist in the repo. Retry once without --label so the PR
# is still created. Keep stderr visible so unrelated failures surface.
PR_URL=$(gh pr create \
--base <target-branch> \
--title "Security: Fix CVE-YYYY-XXXXX (<package-name>)" \
--body "$PR_BODY" \
--label "cve-fixer-automated") \
|| PR_URL=$(gh pr create \
--base <target-branch> \
--title "Security: Fix CVE-YYYY-XXXXX (<package-name>)" \
--body "$PR_BODY")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@workflows/cve-fixer/.claude/commands/cve.fix.md` around lines 1246 - 1256,
The comment is wrong and stderr is being swallowed; change the flow to test for
the label before attempting the first gh pr create and stop redirecting stderr:
use something like gh label view "cve-fixer-automated" (or equivalent) to detect
if the label exists and then call gh pr create --base <target-branch> --title
"Security: Fix CVE-YYYY-XXXXX (<package-name>)" --body "$PR_BODY" --label
"cve-fixer-automated" when present, otherwise call gh pr create without --label;
remove the 2>/dev/null so real errors from gh (auth/network/validation) are not
masked, and update the inline note to state that --label causes gh to fail if
the label is missing rather than silently succeeding.

# Enable automerge if --automerge flag was passed and PR was created successfully
if [ "$AUTOMERGE" = "true" ] && [ -n "$PR_URL" ] && [ "$PR_URL" != "null" ]; then
Expand Down Expand Up @@ -1475,37 +1484,31 @@ After completing this phase:
- Filter the repository list to only those that contain the CVE
- **Multi-Repository Support**: A single component can map to MULTIPLE repositories
- Common pattern: an **upstream** repo (e.g., `opendatahub-io/models-as-a-service`) and one or more **downstream** repos (e.g., `red-hat-data-services/models-as-a-service`)
- Each repository has its own `default_branch`, `cve_fix_workflow`, and `repo_type`
- The `repo_type` field can be `"upstream"` or `"downstream"` to indicate the relationship
- When fixing CVEs, iterate through ALL repositories for the component and apply fixes to each one independently
- Downstream repos often track different branches (e.g., `rhoai-3.0`) than upstream (`main`)
- Each repository gets its own clone directory, feature branch, verification, test run, and PR
- **Mapping File Structure**:
- Each repo entry has its own `default_branch`, `active_branches`, and `type`
- The `type` field is `"upstream"`, `"midstream"`, or `"downstream"`
- When fixing CVEs, iterate through ALL repos for the component and apply fixes to each one independently
- Downstream repos often track different branches (e.g., `rhoai-3.4`) than upstream (`main`)
- Each repo gets its own clone directory, feature branch, verification, test run, and PR
- **Mapping File Structure** (simplified schema):
```json
{
"components": {
"Component Name": {
"container_to_repo_mapping": { ... },
"repositories": {
"org/repo-upstream": {
"repos": [
{
"url": "https://github.com/org/upstream-repo",
"type": "upstream",
"default_branch": "main",
"active_release_branches": [...],
"cve_fix_workflow": {
"primary_target": "main",
"backport_targets": "..."
},
"repo_type": "upstream"
"active_branches": ["release-0.6"],
"containers": ["rhoai/odh-container-rhel9"]
},
"org/repo-downstream": {
"default_branch": "rhoai-3.0",
"active_release_branches": ["rhoai-3.0"],
"cve_fix_workflow": {
"primary_target": "rhoai-3.0",
"backport_targets": "rhoai-3.0"
},
"repo_type": "downstream"
{
"url": "https://github.com/org/downstream-repo",
"type": "downstream",
"default_branch": "main",
"active_branches": ["rhoai-3.4", "rhoai-3.4-ea.2"]
}
}
]
}
}
}
Expand Down
Loading
Loading