Skip to content

feat: Add CVE Fixer workflow for automated vulnerability remediation#75

Merged
jwm4 merged 13 commits intoambient-code:mainfrom
angaduom:add-cve-fixer-workflow
Mar 25, 2026
Merged

feat: Add CVE Fixer workflow for automated vulnerability remediation#75
jwm4 merged 13 commits intoambient-code:mainfrom
angaduom:add-cve-fixer-workflow

Conversation

@angaduom
Copy link
Copy Markdown
Contributor

Summary

This PR adds a new CVE Fixer workflow that automates the discovery and remediation of security vulnerabilities tracked in Jira.

What This Workflow Does

/cve.find - Discover CVEs

  • Queries Jira for open CVE issues by component
  • Filters out already-resolved CVEs via comment analysis
  • Generates structured reports with severity and status
  • Supports both MCP Jira integration and direct API access

/cve.fix - Automated Remediation

  • Maps CVE components to GitHub repositories (upstream + downstream)
  • Verifies CVE presence with version-matched scanning (GOTOOLCHAIN for Go)
  • Checks for existing PRs to avoid duplicates
  • Applies fixes: dependency updates, stdlib upgrades, patches
  • Discovers and runs tests pre-PR
  • Creates separate PRs per CVE with comprehensive descriptions
  • Handles unmapped components by prompting user for repo info

Key Features

Multi-repository support - Handles both upstream and downstream repos independently
Smart duplicate detection - Skips CVEs that are already fixed or have open PRs
Version-matched scanning - Uses GOTOOLCHAIN to detect stdlib CVEs accurately
Comprehensive PR descriptions - Includes test results, breaking changes, risk assessment
Flexible component mapping - Prompts for repo info when component not in mapping file

Files Added

  • workflows/cve-fixer/.ambient/ambient.json - Workflow configuration
  • workflows/cve-fixer/.claude/commands/cve.find.md - CVE discovery command
  • workflows/cve-fixer/.claude/commands/cve.fix.md - CVE fixing command
  • workflows/cve-fixer/.claude/agents/vera-security-engineer.md - Security specialist agent
  • workflows/cve-fixer/component-repository-mappings.json - Component to repo mappings
  • workflows/cve-fixer/README.md - Workflow documentation
  • workflows/cve-fixer/FIELD_REFERENCE.md - Field reference guide

Use Cases

  • Automate security patch PRs for Go stdlib CVEs
  • Fix npm/Python dependency vulnerabilities
  • Track CVE remediation across multiple release branches
  • Reduce manual effort in CVE triage and fixing

Testing

This workflow has been tested with real CVE fixes in the RHOAI ecosystem, successfully creating PRs for Go stdlib vulnerabilities across multiple repositories.

🤖 Generated with Claude Code

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 16, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds a new CVE Fixer workflow: ambient runtime config and startup prompts, Claude agent persona and permissions, command docs for /cve.find and /cve.fix, component→repository mappings, workflow docs and field reference, a PowerPoint generator script, and artifact ignore rules.

Changes

Cohort / File(s) Summary
Ambient Config
workflows/cve-fixer/.ambient/ambient.json
New ambient workflow config: name, description, systemPrompt, startupPrompt, and results artifact mappings.
Claude Config & Agent
workflows/cve-fixer/.claude/settings.json, workflows/cve-fixer/.claude/agents/vera-security-engineer.md
Adds Claude permission policy and a detailed Vera security engineer persona (roles, responsibilities, tools, invocation examples).
Command Documentation
workflows/cve-fixer/.claude/commands/cve.find.md, workflows/cve-fixer/.claude/commands/cve.fix.md
Comprehensive docs for /cve.find (Jira querying, pagination, filters, report generation) and /cve.fix (multi-repo mapping, cloning, scanning, fix loop, PR strategy).
Repository Mappings
workflows/cve-fixer/component-repository-mappings.json
New component→repository mapping with per-repo metadata, branch strategies, and CVE-fix workflow scaffolds.
Docs & References
workflows/cve-fixer/README.md, workflows/cve-fixer/FIELD_REFERENCE.md, workflows/cve-fixer/cve-workflow-combined.md
Adds README, field reference schema, and combined workflow doc with diagrams, phases, outputs, examples, and rendering tips.
Presentation Generator
workflows/cve-fixer/create_presentation.py
New python-pptx script to generate a 3-slide PowerPoint summarizing the CVE remediation workflow and architecture.
Artifacts & Ignore
workflows/cve-fixer/.gitignore
Adds ignore entry artifacts/cve-fixer/ and comment for generated artifacts.

Sequence Diagram(s)

sequenceDiagram
    participant User as User/CLI
    participant Find as /cve.find
    participant Jira as Jira API
    participant Report as Report Generator

    User->>Find: Run /cve.find (component, options)
    Find->>Jira: Query via MCP or REST (JQL + pagination)
    Jira-->>Find: Return issues (paged)
    Find->>Find: Filter ignore patterns, extract metadata
    Find->>Report: Write artifacts/cve-fixer/find/cve-issues-<ts>.md
    Report-->>User: Provide report path & preview
Loading
sequenceDiagram
    participant User as User/CLI
    participant Fix as /cve.fix
    participant Map as Mappings (JSON)
    participant Git as Git Repos
    participant Scan as govulncheck/Test Suite
    participant PR as GitHub/Pull Requests

    User->>Fix: Invoke /cve.fix (issues or report)
    Fix->>Map: Resolve repo mapping per component
    Fix->>Git: Clone repo(s) & checkout target branches
    Fix->>Scan: Run vulnerability scans (govulncheck)
    Scan-->>Fix: Return findings
    Fix->>Fix: Analyze, modify code, run tests
    Fix->>Git: Commit changes
    Fix->>PR: Open PR(s) with change details
    PR-->>User: Return PR URLs and artifacts
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding a CVE Fixer workflow for automated vulnerability remediation, which is the primary focus of all file additions.
Description check ✅ Passed The description comprehensively explains the workflow's purpose, features, commands (/cve.find and /cve.fix), key capabilities, files added, and testing, directly relating to the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 10

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@workflows/cve-fixer/.ambient/ambient.json`:
- Around line 4-5: The systemPrompt and startupPrompt list seven slash commands
but only cve.find.md and cve.fix.md exist; update either the prompts or the repo
to stay consistent: either add the missing command files for /cve.scan,
/cve.analyze, /cve.prioritize, /cve.verify, and /cve.report (create
documentation files matching the existing cve.find.md and cve.fix.md naming
convention), or edit the systemPrompt and startupPrompt strings to only mention
/cve.find and /cve.fix (remove or reword references to the unimplemented
commands) so users aren’t directed to non-existent commands; locate and change
the strings named systemPrompt and startupPrompt in ambient.json and ensure the
AVAILABLE COMMANDS list matches the actual files cve.find.md and cve.fix.md.

In `@workflows/cve-fixer/.claude/commands/cve.find.md`:
- Line 203: The script currently writes to a hardcoded /tmp path (e.g., the line
writing BODY to /tmp/jira-cve-response.json) which can collide across concurrent
runs; create a per-execution temp directory early (e.g., TMP_DIR="$(mktemp -d -t
cve-find-XXXXXX)") and register a cleanup trap (trap 'rm -rf "$TMP_DIR"' EXIT)
then replace all occurrences of /tmp/jira-cve-* (including
jira-cve-response.json and every other jira-cve-* filename shown) with
"$TMP_DIR/jira-cve-*" and update any commands that read/write those files to
reference $TMP_DIR so each run uses isolated temp files.
- Around line 173-177: The three curl calls that populate RESPONSE (the POST to
"${JIRA_BASE_URL}/rest/api/2/search" using PAYLOAD and JIRA_API_TOKEN, and the
two other Jira API curl invocations) lack timeout and retry controls; update
each curl invocation to include sensible flags such as --connect-timeout
<seconds>, --max-time <seconds>, --retry <count>, --retry-delay <seconds>, and
--retry-all-errors so a stalled call won't hang the workflow, and preserve
existing headers (-H "Content-Type: application/json", -H "Authorization: Bearer
${JIRA_API_TOKEN}") and the -d "${PAYLOAD}" payload semantics when adding these
flags to the commands that set RESPONSE.
- Around line 257-332: The loop in Step 5 unconditionally uses curl +
JIRA_API_TOKEN to fetch comments (see curl call that sets COMMENTS_RESPONSE,
COMMENTS_HTTP_CODE, COMMENTS_BODY) which breaks the MCP-only flow; modify the
code to branch on the transport selected in Step 1 (e.g., check the variable
that records MCP vs curl/http selection) and, inside the for KEY in
"${ISSUE_KEYS[@]}" loop, use the MCP Jira comments API/path when MCP was chosen
and the existing curl + Bearer token path when HTTP was chosen, preserving the
same downstream parsing (IGNORE_MATCH, IGNORED_ISSUES, IGNORED_REASONS) and
error handling, and ensure no token access is attempted when MCP is selected.
- Line 390: The JQL in the report uses a presence test (${IGNORE_RESOLVED:+...})
which expands for any non-empty value (e.g. "false") and so disagrees with the
actual filtering logic that only treats IGNORE_RESOLVED="true"; update the
report generation to use the same conditional as the filter: replace the
${IGNORE_RESOLVED:+ AND status not in ("Resolved")} expansion with a test that
only emits ' AND status not in ("Resolved")' when IGNORE_RESOLVED equals "true"
(for example using $( [ "$IGNORE_RESOLVED" = "true" ] && echo ' AND status not
in ("Resolved")' ) ), ensuring the JQL Query string in the report matches the
behavior of the filtering logic that references IGNORE_RESOLVED.
- Around line 106-108: The docs currently tell users "For **Jira Server/Data
Center with Basic Auth**" to use their password but all curl examples use an
Authorization: Bearer header, causing a mismatch; either change the Basic Auth
text to instruct using a PAT/Bearer token (so it matches the existing curl
examples and Authorization: Bearer usage) or implement conditional header
generation so examples use Authorization: Basic <base64(username:password)> when
Basic Auth is selected; update the text around "For **Jira Server/Data Center
with Basic Auth**" and the curl examples (the Authorization header lines) so the
chosen auth method (Basic vs Bearer) and the corresponding header format are
consistent.

In `@workflows/cve-fixer/component-repository-mappings.json`:
- Line 359: The "last_updated" metadata entry currently has an
incorrect/future-looking date string ("2026-02-04"); verify the actual last
modification date for the component and update the "last_updated" JSON value to
the correct ISO date (e.g., "YYYY-MM-DD") or remove it if not applicable so it
accurately reflects the file's last change; locate the "last_updated" key in the
JSON (the entry with value "2026-02-04") and replace it with the verified date.
- Around line 96-107: Several repository entries in the
component-repository-mappings JSON (e.g., the
"opendatahub-io/modelmesh-runtime-adapter" object) are missing the github_url
field required by the cve.fix workflow; add a "github_url" property to each
missing repository object (for example "github_url":
"https://github.com/opendatahub-io/modelmesh-runtime-adapter") so the workflow
can derive org/name and run git clone, ensuring the value is the full HTTPS
GitHub URL and that the key names (e.g.,
"opendatahub-io/modelmesh-runtime-adapter",
"opendatahub-io/odh-model-controller", "opendatahub-io/kserve", etc.) match
their repository names exactly.

In `@workflows/cve-fixer/FIELD_REFERENCE.md`:
- Around line 182-187: The "Current Commands" list in FIELD_REFERENCE.md
references non-existent files; update the list to reflect only the command files
actually added in this PR (replace the entries `cve.scan.md`, `cve.analyze.md`,
`cve.prioritize.md`, `cve.verify.md`, and `cve.report.md` with the real files
`cve.find.md` and `cve.fix.md`), or alternatively add the missing command files
if that was intended; edit the "Current Commands" section so it only names
`cve.find.md` and `cve.fix.md` (or the correct set of filenames) to keep
documentation accurate.

In `@workflows/cve-fixer/README.md`:
- Line 214: The markdown link "[ACP
documentation](https://ambient-code.github.io/vteam)" in
workflows/cve-fixer/README.md is broken; update that URL to the correct ACP
documentation link (replace the href for the ACP documentation link text with
the accessible/updated documentation URL) so the reference no longer 404s.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 50e9aa1e-280b-4f0a-a3df-35a77549c78c

📥 Commits

Reviewing files that changed from the base of the PR and between 73ef4fe and 0a8d387.

📒 Files selected for processing (9)
  • workflows/cve-fixer/.ambient/ambient.json
  • workflows/cve-fixer/.claude/agents/vera-security-engineer.md
  • workflows/cve-fixer/.claude/commands/cve.find.md
  • workflows/cve-fixer/.claude/commands/cve.fix.md
  • workflows/cve-fixer/.claude/settings.json
  • workflows/cve-fixer/.gitignore
  • workflows/cve-fixer/FIELD_REFERENCE.md
  • workflows/cve-fixer/README.md
  • workflows/cve-fixer/component-repository-mappings.json

fi

# Save initial results
echo "$BODY" > /tmp/jira-cve-response.json
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 | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n workflows/cve-fixer/.claude/commands/cve.find.md | head -300 | tail -100

Repository: ambient-code/workflows

Length of output: 4224


🏁 Script executed:

wc -l workflows/cve-fixer/.claude/commands/cve.find.md

Repository: ambient-code/workflows

Length of output: 118


🏁 Script executed:

sed -n '200,210p' workflows/cve-fixer/.claude/commands/cve.find.md

Repository: ambient-code/workflows

Length of output: 324


🏁 Script executed:

sed -n '240,255p' workflows/cve-fixer/.claude/commands/cve.find.md

Repository: ambient-code/workflows

Length of output: 638


🏁 Script executed:

sed -n '270,280p' workflows/cve-fixer/.claude/commands/cve.find.md

Repository: ambient-code/workflows

Length of output: 416


🏁 Script executed:

grep -n "/tmp" workflows/cve-fixer/.claude/commands/cve.find.md

Repository: ambient-code/workflows

Length of output: 2284


🏁 Script executed:

head -30 workflows/cve-fixer/.claude/commands/cve.find.md

Repository: ambient-code/workflows

Length of output: 1066


🏁 Script executed:

file workflows/cve-fixer/.claude/commands/cve.find.md

Repository: ambient-code/workflows

Length of output: 110


🏁 Script executed:

find workflows/cve-fixer -type f -name "*.md" | head -10

Repository: ambient-code/workflows

Length of output: 292


🏁 Script executed:

find workflows/cve-fixer -type f \( -name "cve*" -o -name "*find*" \) | grep -v ".md"

Repository: ambient-code/workflows

Length of output: 48


Fix pervasive hardcoded /tmp paths throughout the documented implementation to prevent collision and data leakage in concurrent execution.

The bash code snippets in this documentation use hardcoded /tmp filenames at lines 203, 242, 250–254, 273, and many additional locations (276, 320, 323–324, 330, 341, 343, 361, 376–377, 403, 429, 435, 442, 445, 457–458, 465, 478, 496). Shared filenames can overwrite concurrent invocations and leak cross-run data.

Establish a per-execution temporary directory early in the script using TMP_DIR="$(mktemp -d -t cve-find-XXXXXX)" with a cleanup trap, then replace all /tmp/jira-cve-* paths with $TMP_DIR/jira-cve-*. This issue affects the entire documented workflow and requires systematic replacement across all code blocks.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@workflows/cve-fixer/.claude/commands/cve.find.md` at line 203, The script
currently writes to a hardcoded /tmp path (e.g., the line writing BODY to
/tmp/jira-cve-response.json) which can collide across concurrent runs; create a
per-execution temp directory early (e.g., TMP_DIR="$(mktemp -d -t
cve-find-XXXXXX)") and register a cleanup trap (trap 'rm -rf "$TMP_DIR"' EXIT)
then replace all occurrences of /tmp/jira-cve-* (including
jira-cve-response.json and every other jira-cve-* filename shown) with
"$TMP_DIR/jira-cve-*" and update any commands that read/write those files to
reference $TMP_DIR so each run uses isolated temp files.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We mitigate this by running fresh sessions for each run, no concurrent runs will happen for a run of find and fix

Comment on lines +257 to +332
5. **Filter Issues with Ignore Comments**
- Check each issue for comments containing ignore patterns
- Filter out issues marked to be ignored by automation

**Ignore Patterns (case-insensitive):**
- "cve-automation-ignore"
- "cve automation ignore"
- "skip-cve-automation"
- "ignore-cve-automation"
- "automation-ignore-cve"

```bash
# Initialize arrays for tracking
echo "Checking for ignored issues..."

# Create temporary files for filtered results
cp /tmp/jira-cve-response.json /tmp/jira-cve-response-original.json

# Get all issue keys
ISSUE_KEYS=($(jq -r '.issues[].key' /tmp/jira-cve-response-original.json))
IGNORED_ISSUES=()
IGNORED_REASONS=()

# Check each issue for ignore comments
for KEY in "${ISSUE_KEYS[@]}"; do
echo " Checking $KEY for ignore comments..."

# Fetch comments for this issue
COMMENTS_RESPONSE=$(curl -s -w "\n%{http_code}" -X GET \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${JIRA_API_TOKEN}" \
"${JIRA_BASE_URL}/rest/api/2/issue/${KEY}/comment")

COMMENTS_HTTP_CODE=$(echo "$COMMENTS_RESPONSE" | tail -n1)
COMMENTS_BODY=$(echo "$COMMENTS_RESPONSE" | sed '$d')

if [ "$COMMENTS_HTTP_CODE" = "200" ]; then
# Check if any comment contains ignore patterns (case-insensitive)
IGNORE_MATCH=$(echo "$COMMENTS_BODY" | jq -r '.comments[]?.body' | \
grep -iE 'cve.{0,1}automation.{0,1}ignore|skip.{0,1}cve.{0,1}automation|ignore.{0,1}cve.{0,1}automation|automation.{0,1}ignore.{0,1}cve' || true)

if [ -n "$IGNORE_MATCH" ]; then
echo " ⚠️ Issue $KEY has ignore comment - will be filtered out"
IGNORED_ISSUES+=("$KEY")
# Get first matching comment for reason
REASON=$(echo "$IGNORE_MATCH" | head -n1 | cut -c1-100)
IGNORED_REASONS+=("$REASON")
fi
else
echo " Warning: Could not fetch comments for $KEY (HTTP $COMMENTS_HTTP_CODE)"
fi
done

# Filter out ignored issues from the response
if [ ${#IGNORED_ISSUES[@]} -gt 0 ]; then
echo ""
echo "Filtered out ${#IGNORED_ISSUES[@]} ignored issue(s)"

# Build JSON array of ignored keys and filter safely using jq --argjson (no string interpolation)
IGNORED_JSON=$(printf '%s\n' "${IGNORED_ISSUES[@]}" | jq -R . | jq -s .)
jq --argjson ignored "$IGNORED_JSON" \
'{issues: [.issues[] | select(.key as $k | ($ignored | index($k)) == null)], total: 0} |
.total = (.issues | length)' \
/tmp/jira-cve-response-original.json > /tmp/jira-cve-response.json

# Save ignored issues for reporting
echo "${IGNORED_ISSUES[@]}" > /tmp/jira-cve-ignored-keys.txt
printf "%s\n" "${IGNORED_REASONS[@]}" > /tmp/jira-cve-ignored-reasons.txt
else
echo "No issues with ignore comments found"
fi

# Update TOTAL count
TOTAL=$(jq -r '.total' /tmp/jira-cve-response.json)
echo "Remaining issues after filtering: $TOTAL"
```
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 | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

git ls-files | grep -E "cve.find.md"

Repository: ambient-code/workflows

Length of output: 114


🏁 Script executed:

head -n 100 workflows/cve-fixer/.claude/commands/cve.find.md

Repository: ambient-code/workflows

Length of output: 4227


🏁 Script executed:

wc -l workflows/cve-fixer/.claude/commands/cve.find.md

Repository: ambient-code/workflows

Length of output: 118


🏁 Script executed:

sed -n '200,400p' workflows/cve-fixer/.claude/commands/cve.find.md

Repository: ambient-code/workflows

Length of output: 6753


🏁 Script executed:

sed -n '1,60p' workflows/cve-fixer/.claude/commands/cve.find.md | cat -n

Repository: ambient-code/workflows

Length of output: 2832


🏁 Script executed:

sed -n '150,200p' workflows/cve-fixer/.claude/commands/cve.find.md | cat -n

Repository: ambient-code/workflows

Length of output: 2112


🏁 Script executed:

sed -n '240,350p' workflows/cve-fixer/.claude/commands/cve.find.md | cat -n

Repository: ambient-code/workflows

Length of output: 5006


🏁 Script executed:

sed -n '57,80p' workflows/cve-fixer/.claude/commands/cve.find.md | cat -n

Repository: ambient-code/workflows

Length of output: 1523


🏁 Script executed:

sed -n '100,160p' workflows/cve-fixer/.claude/commands/cve.find.md | cat -n

Repository: ambient-code/workflows

Length of output: 3112


🏁 Script executed:

sed -n '160,200p' workflows/cve-fixer/.claude/commands/cve.find.md | cat -n

Repository: ambient-code/workflows

Length of output: 1703


Step 5 requires JIRA_API_TOKEN and curl but MCP path skips token setup.

If user selects MCP in Step 1, they skip Step 3 (token check) and proceed through Step 4 using MCP. However, Step 5 (Filter Issues with Ignore Comments) unconditionally uses curl + Bearer token for fetching issue comments, with no MCP fallback. This breaks the documented MCP-only workflow. Step 5 must conditionally branch to either use MCP Jira comment APIs or curl based on which transport was chosen in Step 1.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@workflows/cve-fixer/.claude/commands/cve.find.md` around lines 257 - 332, The
loop in Step 5 unconditionally uses curl + JIRA_API_TOKEN to fetch comments (see
curl call that sets COMMENTS_RESPONSE, COMMENTS_HTTP_CODE, COMMENTS_BODY) which
breaks the MCP-only flow; modify the code to branch on the transport selected in
Step 1 (e.g., check the variable that records MCP vs curl/http selection) and,
inside the for KEY in "${ISSUE_KEYS[@]}" loop, use the MCP Jira comments
API/path when MCP was chosen and the existing curl + Bearer token path when HTTP
was chosen, preserving the same downstream parsing (IGNORE_MATCH,
IGNORED_ISSUES, IGNORED_REASONS) and error handling, and ensure no token access
is attempted when MCP is selected.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think I would like to address this during a hardening phase, for now most of this has been tested with API calls and we will be using a Jira token to get the results, the change we need is to move to the new Jira redhat.atlassian.net url

Add a new workflow that automates the discovery and fixing of CVEs in Jira-tracked components.

## Key Features

**`/cve.find` command:**
- Queries Jira for open CVE issues by component
- Filters out already-resolved CVEs via comment analysis
- Generates structured reports with CVE details and severity
- Supports both MCP Jira integration and direct API access

**`/cve.fix` command:**
- Maps CVE components to GitHub repositories (upstream/downstream)
- Verifies CVE presence with version-matched scanning (GOTOOLCHAIN for Go)
- Checks for existing PRs to avoid duplicates
- Applies fixes: dependency updates, stdlib upgrades, patches
- Discovers and runs tests pre-PR
- Creates separate PRs per CVE with comprehensive descriptions
- Handles unmapped components by prompting user for repo info

## Workflow Components

- **Vera Security Engineer agent**: Specialized persona for CVE remediation
- **Component-repository mappings**: Maps Jira components to GitHub repos
- **Multi-repo support**: Handles upstream and downstream repos independently
- **Comprehensive documentation**: Field reference and usage guides

## Use Cases

- Automate security patch PRs for Go stdlib CVEs
- Fix npm/Python dependency vulnerabilities
- Track CVE remediation across multiple release branches
- Reduce manual effort in CVE triage and fixing

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@angaduom angaduom force-pushed the add-cve-fixer-workflow branch from 0a8d387 to 16bed7d Compare March 16, 2026 20:52
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 9

♻️ Duplicate comments (5)
workflows/cve-fixer/.claude/commands/cve.find.md (5)

106-108: ⚠️ Potential issue | 🟠 Major

Basic-auth instructions conflict with Bearer-only request examples.

The setup section tells users to use Jira password for Basic auth, but all request snippets send Authorization: Bearer .... Those flows are incompatible and will fail for Basic-auth users.

For Jira Server/Data Center REST API, what are the required Authorization header formats for Basic auth vs Personal Access Token (Bearer), and are they interchangeable?

Also applies to: 173-177, 227-231, 285-288

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@workflows/cve-fixer/.claude/commands/cve.find.md` around lines 106 - 108, The
documentation mixes Basic auth guidance with request examples that use
"Authorization: Bearer ..." which will fail for Basic-auth users; update the
examples and text so each auth method is consistent: when advising "Jira
Server/Data Center with Basic Auth" show Authorization: Basic
<base64(user:password)> (or explicit curl --user) and keep Personal Access Token
examples using "Authorization: Bearer <token>"; search for occurrences of the
phrase "Authorization: Bearer" and the "Basic Auth" sections (including the
blocks around the setup section and the other instances flagged) and change the
header or explanatory text so Basic-auth examples use Basic headers and PAT
examples use Bearer headers, and add a short note that Basic and Bearer formats
are not interchangeable.

203-204: ⚠️ Potential issue | 🔴 Critical

Hardcoded /tmp filenames are unsafe for concurrent runs.

Shared temp paths can overwrite another execution’s data and leak cross-run issue metadata.

Use per-execution temp workspace
+TMP_DIR="$(mktemp -d -t cve-find-XXXXXX)"
+trap 'rm -rf "$TMP_DIR"' EXIT
...
-echo "$BODY" > /tmp/jira-cve-response.json
+echo "$BODY" > "$TMP_DIR/jira-cve-response.json"
...
-echo "$BODY" >> /tmp/jira-cve-response-page-${START_AT}.json
+echo "$BODY" >> "$TMP_DIR/jira-cve-response-page-${START_AT}.json"

Also applies to: 242-254, 273-324

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@workflows/cve-fixer/.claude/commands/cve.find.md` around lines 203 - 204, The
script writes to hardcoded temp files like "/tmp/jira-cve-response.json" (see
the echo "$BODY" > /tmp/jira-cve-response.json occurrence and similar uses at
the other ranges), which is unsafe for concurrent runs; change these to create a
per-execution temp workspace (e.g., create a unique temp dir via mktemp -d or
similar and use that directory for all temp files), update all occurrences that
reference /tmp/jira-cve-response.json and other /tmp filenames (lines referenced
203, 242-254, 273-324) to write into that unique temp dir (e.g.,
"$TMPDIR/jira-cve-response.json"), and ensure the script securely sets strict
permissions and removes the temp dir/files on exit (trap cleanup).

173-177: ⚠️ Potential issue | 🟠 Major

Jira API calls need timeout/retry guards for automation reliability.

All current curl calls can hang indefinitely on network stalls.

Suggested hardening pattern
-RESPONSE=$(curl -s -w "\n%{http_code}" -X POST \
+RESPONSE=$(curl -sS --connect-timeout 10 --max-time 60 \
+  --retry 3 --retry-delay 2 --retry-all-errors \
+  -w "\n%{http_code}" -X POST \
   -H "Content-Type: application/json" \
   -H "Authorization: Bearer ${JIRA_API_TOKEN}" \

Also applies to: 227-231, 285-288

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@workflows/cve-fixer/.claude/commands/cve.find.md` around lines 173 - 177, The
curl invocation that sets RESPONSE (the POST to
"${JIRA_BASE_URL}/rest/api/2/search" using PAYLOAD and JIRA_API_TOKEN) can hang
and needs timeout and retry guards; update each curl call (including the other
similar calls that set RESPONSE) to include a connection/total timeout (e.g.
--connect-timeout and --max-time), retry options (--retry, --retry-delay,
--retry-connrefused) and fail-fast behavior (--fail) so the script won't block
indefinitely, and ensure non-2xx responses are handled by checking the HTTP
status appended to RESPONSE and retrying or exiting as appropriate.

57-63: ⚠️ Potential issue | 🔴 Critical

MCP-only execution path breaks during comment filtering.

Step 5 always uses curl + JIRA_API_TOKEN, even when Step 1 selected MCP and skipped token setup. Add transport-conditional logic for comment retrieval as well.

Also applies to: 281-288

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@workflows/cve-fixer/.claude/commands/cve.find.md` around lines 57 - 63, The
MCP-only execution path currently still uses curl with JIRA_API_TOKEN during
Step 5 comment retrieval/filtering, which breaks when Step 1 selected MCP and
token setup was skipped; update the comment retrieval logic to be
transport-conditional: detect whether MCP mode/tools were selected (the MCP
server selection flag/state) and, if so, call the MCP server query path for
fetching comments instead of invoking curl and reading JIRA_API_TOKEN, otherwise
fall back to the existing curl/token path; ensure the comment-filtering code
that expects comments (used in Step 5) accepts the MCP query response shape or
normalizes it to the same format.

390-390: ⚠️ Potential issue | 🟠 Major

Reported JQL condition does not match the actual --ignore-resolved logic.

${IGNORE_RESOLVED:+...} expands for any non-empty value, including "false", so report output can be incorrect.

Make report condition match execution condition
-- **JQL Query:** project = RHOAIENG AND component = "${COMPONENT_NAME}" AND text ~ "CVE-*"${IGNORE_RESOLVED:+ AND status not in ("Resolved")}
+- **JQL Query:** project = RHOAIENG AND component = "${COMPONENT_NAME}" AND text ~ "CVE-*"$( [ "${IGNORE_RESOLVED}" = "true" ] && printf ' AND status not in ("Resolved")' )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@workflows/cve-fixer/.claude/commands/cve.find.md` at line 390, The JQL shown
in the report uses ${IGNORE_RESOLVED:+...} which expands for any non-empty value
(including "false") and so can misreport; update the template in
workflows/cve-fixer/.claude/commands/cve.find.md so the 'AND status not in
("Resolved")' clause is only emitted when IGNORE_RESOLVED is exactly "true" (not
merely set). Locate the JQL line that references COMPONENT_NAME and
IGNORE_RESOLVED and change the conditional expansion to check IGNORE_RESOLVED ==
"true" (or mirror the same runtime conditional used when executing the query) so
the report matches execution.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@workflows/cve-fixer/.claude/commands/cve.fix.md`:
- Around line 113-117: The workflow currently clones repos into /tmp using
REPO_DIR="/tmp/${REPO_ORG}/${REPO_NAME}" (in the "Clone or Use Existing
Repository" logic) but never removes them; add a deterministic cleanup step that
removes only the cloned path(s) after processing each repo or on workflow exit:
either delete /tmp/${REPO_ORG}/${REPO_NAME} at the end of the per-repo
processing loop or install a trap/exit handler to remove those REPO_DIR paths
(and preserve safety by only removing the exact REPO_DIR values, not parent
dirs), and apply the same cleanup for the other clone sections referenced around
lines 752-808.
- Around line 36-44: When /cve.fix is invoked with explicit Jira issue IDs (the
"Option A: User specifies one or more Jira issues" flow), preserve and use those
provided Jira IDs from Step 1 and do not run the logic that re-parses the latest
/cve.find artifact (the code currently executed around the later lookup at the
/cve.find artifact handling). Modify the /cve.fix command handler to branch on
presence of explicit Jira IDs (i.e., the explicit input mode) so it fetches
issue details from Jira using the supplied IDs, extracts CVE and component from
those issues, and skips any code that re-derives or overwrites jira IDs from the
latest /cve.find output.
- Around line 153-160: The GO_VERSION/TOOLCHAIN_VERSION/TARGET_GO_VERSION
extraction must read go.mod from the build location; change the commands that
compute GO_VERSION and TOOLCHAIN_VERSION to operate on the resolved
BUILD_LOCATION (either by cd "$BUILD_LOCATION" before running the grep/sed or by
grepping "$BUILD_LOCATION/go.mod" explicitly) so TARGET_GO_VERSION is derived
from the correct go.mod file in subdirectories or monorepos.
- Around line 252-289: The existing PR detection uses the gh pr list call
assigned to EXISTING_PR and currently searches the whole repo by CVE_ID; update
that gh pr list invocation (the command that builds EXISTING_PR) to include
--base "$TARGET_BRANCH" so it only considers open PRs targeting the branch
you're fixing (TARGET_BRANCH/branch variable), ensuring the skip logic, printed
message and artifacts creation still run when a matching open PR exists for that
specific target branch.

In `@workflows/cve-fixer/component-repository-mappings.json`:
- Around line 46-67: The mapping currently points to test forks
"angaduom/models-as-a-service" and "angaduom/models-as-a-service-downstream"
causing /cve.fix to target forks; update the mapping so the top-level key that
maps components (e.g., "rhoai/odh-maas-api-rhel9") points to the canonical
upstream repo (replace "angaduom/models-as-a-service" with the official repo
like "opendatahub-io/models-as-a-service"), and replace the "repositories"
entries for "angaduom/models-as-a-service" and
"angaduom/models-as-a-service-downstream" with the official repository keys,
correct "github_url", "default_branch", "repo_type" (upstream/downstream) and
remove fork/testing notes so remediation PRs target the canonical
upstream/downstream repos instead of forks.
- Around line 271-281: The entry for "argoproj/argo-workflows" sets
cve_fix_workflow.primary_target to the non-executable sentinel "N/A - upstream
project"; either replace that value with a real branch name (e.g., "main") if
you want the /cve.fix flow to operate on this repo, or explicitly mark the repo
as not eligible for automation by adding a boolean flag such as
"excluded_from_automation": true (or similar agreed-upon field) inside
cve_fix_workflow so the automation will skip it; update the "primary_target" or
add the exclusion flag in the "argoproj/argo-workflows" cve_fix_workflow object
accordingly.

In `@workflows/cve-fixer/create_presentation.py`:
- Around line 354-356: The code uses a machine-specific hard-coded output_path
for saving the presentation which will fail on other machines/CI; change
creation of output_path (used just before prs.save) to be configurable and
repo-relative instead: read an env var or CLI/config value (e.g.,
PRESENTATION_OUTPUT_DIR) with a sensible default, build the path using pathlib
relative to the repository root (derive root from
Path(__file__).resolve().parents[...] or a project root helper), ensure the
target directory exists (mkdir(parents=True, exist_ok=True)), and then call
prs.save(output_path) with that constructed path so the file is portable across
environments.

In `@workflows/cve-fixer/FIELD_REFERENCE.md`:
- Around line 52-61: FIELD_REFERENCE.md lists six artifact mappings that don't
match the actual workflow exposure in workflows/cve-fixer/.ambient/ambient.json;
update the JSON under "Current Mappings" so the keys and patterns reflect the
two real artifact categories ("Jira CVE Issues" and "Fix Implementations") and
their paths (e.g., "Jira CVE Issues": "artifacts/cve-fixer/find/**/*.md" and
"Fix Implementations": "artifacts/cve-fixer/fixes/**/*") so the documented
mapping keys in FIELD_REFERENCE.md match the exposed keys in ambient.json.

In `@workflows/cve-fixer/README.md`:
- Around line 58-111: The README advertises CLI commands that don't exist in
this package (mentions /cve.scan, /cve.analyze, /cve.prioritize, /cve.verify,
/cve.report) while the diff only implements /cve.find and /cve.fix; fix by
either (A) updating the README sections (Phase 2..7) to only document the actual
implemented commands (/cve.find, /cve.fix) and their outputs, or (B) add minimal
command handlers/stubs for the missing commands (/cve.scan, /cve.analyze,
/cve.prioritize, /cve.verify, /cve.report) so the docs match the code; update
references for the command names in the README and any example usage so they are
consistent with the implemented handlers (/cve.find, /cve.fix).

---

Duplicate comments:
In `@workflows/cve-fixer/.claude/commands/cve.find.md`:
- Around line 106-108: The documentation mixes Basic auth guidance with request
examples that use "Authorization: Bearer ..." which will fail for Basic-auth
users; update the examples and text so each auth method is consistent: when
advising "Jira Server/Data Center with Basic Auth" show Authorization: Basic
<base64(user:password)> (or explicit curl --user) and keep Personal Access Token
examples using "Authorization: Bearer <token>"; search for occurrences of the
phrase "Authorization: Bearer" and the "Basic Auth" sections (including the
blocks around the setup section and the other instances flagged) and change the
header or explanatory text so Basic-auth examples use Basic headers and PAT
examples use Bearer headers, and add a short note that Basic and Bearer formats
are not interchangeable.
- Around line 203-204: The script writes to hardcoded temp files like
"/tmp/jira-cve-response.json" (see the echo "$BODY" >
/tmp/jira-cve-response.json occurrence and similar uses at the other ranges),
which is unsafe for concurrent runs; change these to create a per-execution temp
workspace (e.g., create a unique temp dir via mktemp -d or similar and use that
directory for all temp files), update all occurrences that reference
/tmp/jira-cve-response.json and other /tmp filenames (lines referenced 203,
242-254, 273-324) to write into that unique temp dir (e.g.,
"$TMPDIR/jira-cve-response.json"), and ensure the script securely sets strict
permissions and removes the temp dir/files on exit (trap cleanup).
- Around line 173-177: The curl invocation that sets RESPONSE (the POST to
"${JIRA_BASE_URL}/rest/api/2/search" using PAYLOAD and JIRA_API_TOKEN) can hang
and needs timeout and retry guards; update each curl call (including the other
similar calls that set RESPONSE) to include a connection/total timeout (e.g.
--connect-timeout and --max-time), retry options (--retry, --retry-delay,
--retry-connrefused) and fail-fast behavior (--fail) so the script won't block
indefinitely, and ensure non-2xx responses are handled by checking the HTTP
status appended to RESPONSE and retrying or exiting as appropriate.
- Around line 57-63: The MCP-only execution path currently still uses curl with
JIRA_API_TOKEN during Step 5 comment retrieval/filtering, which breaks when Step
1 selected MCP and token setup was skipped; update the comment retrieval logic
to be transport-conditional: detect whether MCP mode/tools were selected (the
MCP server selection flag/state) and, if so, call the MCP server query path for
fetching comments instead of invoking curl and reading JIRA_API_TOKEN, otherwise
fall back to the existing curl/token path; ensure the comment-filtering code
that expects comments (used in Step 5) accepts the MCP query response shape or
normalizes it to the same format.
- Line 390: The JQL shown in the report uses ${IGNORE_RESOLVED:+...} which
expands for any non-empty value (including "false") and so can misreport; update
the template in workflows/cve-fixer/.claude/commands/cve.find.md so the 'AND
status not in ("Resolved")' clause is only emitted when IGNORE_RESOLVED is
exactly "true" (not merely set). Locate the JQL line that references
COMPONENT_NAME and IGNORE_RESOLVED and change the conditional expansion to check
IGNORE_RESOLVED == "true" (or mirror the same runtime conditional used when
executing the query) so the report matches execution.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: e15016ea-f40a-4648-bd27-b92451b47986

📥 Commits

Reviewing files that changed from the base of the PR and between 0a8d387 and 16bed7d.

⛔ Files ignored due to path filters (1)
  • workflows/cve-fixer/cve-workflow-presentation.pptx is excluded by !**/*.pptx
📒 Files selected for processing (11)
  • workflows/cve-fixer/.ambient/ambient.json
  • workflows/cve-fixer/.claude/agents/vera-security-engineer.md
  • workflows/cve-fixer/.claude/commands/cve.find.md
  • workflows/cve-fixer/.claude/commands/cve.fix.md
  • workflows/cve-fixer/.claude/settings.json
  • workflows/cve-fixer/.gitignore
  • workflows/cve-fixer/FIELD_REFERENCE.md
  • workflows/cve-fixer/README.md
  • workflows/cve-fixer/component-repository-mappings.json
  • workflows/cve-fixer/create_presentation.py
  • workflows/cve-fixer/cve-workflow-combined.md

Comment on lines +354 to +356
output_path = "/Users/angadsingh/Desktop/Redhat/angaduom/workflows/workflows/cve-fixer/cve-workflow-presentation.pptx"
prs.save(output_path)
print(f"Presentation created successfully: {output_path}")
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 | 🟠 Major

Replace the hard-coded local output path with a configurable, repo-relative path.

Line 354 is machine-specific (/Users/...) and will fail on other environments or CI agents where that directory does not exist.

Portable output handling
+from pathlib import Path
+import argparse
+
 # Save presentation
-output_path = "/Users/angadsingh/Desktop/Redhat/angaduom/workflows/workflows/cve-fixer/cve-workflow-presentation.pptx"
-prs.save(output_path)
-print(f"Presentation created successfully: {output_path}")
+parser = argparse.ArgumentParser()
+parser.add_argument(
+    "--output",
+    default="workflows/cve-fixer/cve-workflow-presentation.pptx",
+    help="Output .pptx path"
+)
+args = parser.parse_args()
+output_path = Path(args.output)
+output_path.parent.mkdir(parents=True, exist_ok=True)
+prs.save(str(output_path))
+print(f"Presentation created successfully: {output_path.resolve()}")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@workflows/cve-fixer/create_presentation.py` around lines 354 - 356, The code
uses a machine-specific hard-coded output_path for saving the presentation which
will fail on other machines/CI; change creation of output_path (used just before
prs.save) to be configurable and repo-relative instead: read an env var or
CLI/config value (e.g., PRESENTATION_OUTPUT_DIR) with a sensible default, build
the path using pathlib relative to the repository root (derive root from
Path(__file__).resolve().parents[...] or a project root helper), ensure the
target directory exists (mkdir(parents=True, exist_ok=True)), and then call
prs.save(output_path) with that constructed path so the file is portable across
environments.

- Add curl timeouts (10s connect, 30s max, 3 retries) to all API calls
- Fix auth documentation to only mention Bearer tokens (removed confusing Basic Auth reference)
- Fix JQL filter logic bug: only add resolved filter when IGNORE_RESOLVED='true' (not any non-empty value)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
workflows/cve-fixer/.claude/commands/cve.find.md (2)

264-343: ⚠️ Potential issue | 🔴 Critical

Step 5 still breaks MCP-only execution path.

If MCP is selected in step 1, this block still unconditionally uses curl + JIRA_API_TOKEN (Line 292, Line 298), which contradicts the earlier MCP flow and can fail without token setup. Add a transport branch in Step 5 (MCP comments retrieval vs curl comments retrieval) and keep shared filtering logic after fetch.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@workflows/cve-fixer/.claude/commands/cve.find.md` around lines 264 - 343, The
Step 5 loop currently always uses curl and JIRA_API_TOKEN to fetch issue
comments (see COMMENTS_RESPONSE/COMMENTS_HTTP_CODE handling and JIRA_API_TOKEN
use) which breaks the MCP-only path; add a transport branch inside the for KEY
in "${ISSUE_KEYS[@]}" loop that checks the selected transport (the same flag set
in earlier steps, e.g., MCP/TRANSPORT variable) and, when MCP is selected, fetch
comments via the MCP comment retrieval call instead of curl (populate
COMMENTS_BODY and COMMENTS_HTTP_CODE the same way), keep the existing
ignore-pattern detection (IGNORE_MATCH, IGNORED_ISSUES, IGNORED_REASONS) shared
and unchanged after the fetch, and ensure no curl/JIRA_API_TOKEN access is
attempted when MCP is selected.

206-207: ⚠️ Potential issue | 🟠 Major

Replace hardcoded /tmp files with per-run temp directory.

The workflow still relies on shared /tmp/jira-cve-* filenames across multiple steps. This can cause cross-run collisions and data leakage in concurrent executions. Use TMP_DIR="$(mktemp -d -t cve-find-XXXXXX)" plus trap 'rm -rf "$TMP_DIR"' EXIT, then rewrite temp paths to "${TMP_DIR}/...".

Also applies to: 249-261, 280-284, 331-335, 341-342, 352-355, 372-373, 387-389, 414-415, 440-441, 446-447, 453-457, 468-470, 476-477, 489-490, 507-507

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@workflows/cve-fixer/.claude/commands/cve.find.md` around lines 206 - 207,
Replace the shared hardcoded /tmp files with a per-run temp dir: at the top of
the script create TMP_DIR="$(mktemp -d -t cve-find-XXXXXX)" and add trap 'rm -rf
"$TMP_DIR"' EXIT; then change every use of /tmp/jira-cve-*.json (for example the
echo "$BODY" > /tmp/jira-cve-response.json occurrence and all other instances
called out in the review) to "${TMP_DIR}/jira-cve-response.json" (and similarly
rename other files to "${TMP_DIR}/jira-cve-*.json") so each run uses an isolated
temp directory that is removed on exit.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@workflows/cve-fixer/.claude/commands/cve.find.md`:
- Around line 143-146: Update the documentation to reference the correct step
for obtaining COMPONENT_NAME: change the mention of "[from step 3]" to "[from
step 2]" in the variables setup so it accurately reflects that COMPONENT_NAME is
provided by the argument parsing in step 2; locate the string
"COMPONENT_NAME=\"[from step 3]\"" and replace the step number accordingly to
avoid confusion.

---

Duplicate comments:
In `@workflows/cve-fixer/.claude/commands/cve.find.md`:
- Around line 264-343: The Step 5 loop currently always uses curl and
JIRA_API_TOKEN to fetch issue comments (see COMMENTS_RESPONSE/COMMENTS_HTTP_CODE
handling and JIRA_API_TOKEN use) which breaks the MCP-only path; add a transport
branch inside the for KEY in "${ISSUE_KEYS[@]}" loop that checks the selected
transport (the same flag set in earlier steps, e.g., MCP/TRANSPORT variable)
and, when MCP is selected, fetch comments via the MCP comment retrieval call
instead of curl (populate COMMENTS_BODY and COMMENTS_HTTP_CODE the same way),
keep the existing ignore-pattern detection (IGNORE_MATCH, IGNORED_ISSUES,
IGNORED_REASONS) shared and unchanged after the fetch, and ensure no
curl/JIRA_API_TOKEN access is attempted when MCP is selected.
- Around line 206-207: Replace the shared hardcoded /tmp files with a per-run
temp dir: at the top of the script create TMP_DIR="$(mktemp -d -t
cve-find-XXXXXX)" and add trap 'rm -rf "$TMP_DIR"' EXIT; then change every use
of /tmp/jira-cve-*.json (for example the echo "$BODY" >
/tmp/jira-cve-response.json occurrence and all other instances called out in the
review) to "${TMP_DIR}/jira-cve-response.json" (and similarly rename other files
to "${TMP_DIR}/jira-cve-*.json") so each run uses an isolated temp directory
that is removed on exit.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: f693ee3c-adb8-4acb-8559-d31b2617e0f6

📥 Commits

Reviewing files that changed from the base of the PR and between 16bed7d and daee6be.

📒 Files selected for processing (1)
  • workflows/cve-fixer/.claude/commands/cve.find.md

COMPONENT_NAME comes from step 2 (argument parsing), not step 3.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (2)
workflows/cve-fixer/.claude/commands/cve.find.md (2)

264-343: ⚠️ Potential issue | 🔴 Critical

Step 5 breaks MCP-only execution path.

Line 264 onward unconditionally uses curl + JIRA_API_TOKEN for comment retrieval, even though earlier steps allow MCP mode and skipping token setup. This makes the documented MCP path fail during ignore-comment filtering.

Please branch this loop by selected transport: MCP comment API when MCP is active, curl/Bearer only for HTTP mode.

Suggested structure
 # Check each issue for ignore comments
 for KEY in "${ISSUE_KEYS[@]}"; do
   echo "  Checking $KEY for ignore comments..."
 
-  COMMENTS_RESPONSE=$(curl -s -w "\n%{http_code}" -X GET \
-    ... \
-    -H "Authorization: Bearer ${JIRA_API_TOKEN}" \
-    "${JIRA_BASE_URL}/rest/api/2/issue/${KEY}/comment")
-  COMMENTS_HTTP_CODE=$(echo "$COMMENTS_RESPONSE" | tail -n1)
-  COMMENTS_BODY=$(echo "$COMMENTS_RESPONSE" | sed '$d')
+  if [ "${USE_MCP}" = "true" ]; then
+    # Use MCP Jira comments tool here and normalize to COMMENTS_BODY/COMMENTS_HTTP_CODE
+    COMMENTS_HTTP_CODE="200"
+    COMMENTS_BODY="$(...)"
+  else
+    COMMENTS_RESPONSE=$(curl -s -w "\n%{http_code}" -X GET \
+      ... \
+      -H "Authorization: Bearer ${JIRA_API_TOKEN}" \
+      "${JIRA_BASE_URL}/rest/api/2/issue/${KEY}/comment")
+    COMMENTS_HTTP_CODE=$(echo "$COMMENTS_RESPONSE" | tail -n1)
+    COMMENTS_BODY=$(echo "$COMMENTS_RESPONSE" | sed '$d')
+  fi
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@workflows/cve-fixer/.claude/commands/cve.find.md` around lines 264 - 343, The
ignore-comment filtering loop always calls curl with JIRA_API_TOKEN
(COMMENTS_RESPONSE) which breaks the MCP-only execution path; update the for KEY
in "${ISSUE_KEYS[@]}" loop to branch on the transport/mode flag (the MCP
activation variable used earlier in the script) and, when MCP is active, call
the MCP comment API instead of curl (populate COMMENTS_BODY/COMMENTS_HTTP_CODE
from the MCP response), otherwise keep the existing curl path using
JIRA_API_TOKEN; ensure variables used later (IGNORE_MATCH, IGNORED_ISSUES,
IGNORED_REASONS, /tmp/jira-cve-response.json updates and TOTAL extraction)
remain unchanged so filtering logic works identically in both branches.

206-207: ⚠️ Potential issue | 🟠 Major

Hardcoded /tmp files are unsafe for concurrent runs.

The workflow writes many shared filenames under /tmp/jira-cve-*. Parallel invocations can overwrite each other’s data and leak cross-run results.

Use a per-run temp directory (mktemp -d) with cleanup trap, and replace all /tmp/jira-cve-* paths with "${TMP_DIR}/jira-cve-*" consistently.

Suggested base patch
+TMP_DIR="$(mktemp -d -t cve-find-XXXXXX)"
+trap 'rm -rf "$TMP_DIR"' EXIT
...
-echo "$BODY" > /tmp/jira-cve-response.json
+echo "$BODY" > "${TMP_DIR}/jira-cve-response.json"

Also applies to: 249-261, 280-284, 331-335, 341-342, 352-355, 372-373, 387-389, 414-417, 440-447, 453-457, 468-470, 476-477, 489-490, 507-508

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@workflows/cve-fixer/.claude/commands/cve.find.md` around lines 206 - 207, The
script currently writes shared files like "/tmp/jira-cve-response.json" (and
other "/tmp/jira-cve-*" paths referenced throughout) which can collide in
concurrent runs; change the script to create a per-run temp directory at start
with TMP_DIR="$(mktemp -d)" and install a cleanup trap (trap 'rm -rf "$TMP_DIR"'
EXIT), then replace every hardcoded "/tmp/jira-cve-*" filename (e.g., the file
written in the echo "$BODY" > /tmp/jira-cve-response.json line and all other
occurrences listed in the comment) with "${TMP_DIR}/jira-cve-*" so all
reads/writes use the per-run directory and are removed on exit. Ensure TMP_DIR
is exported/available to all functions or subshells that reference these paths.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@workflows/cve-fixer/.claude/commands/cve.find.md`:
- Around line 10-19: The markdown has lint warnings: ensure every fenced code
block has a blank line before and after the fence and declare a language (e.g.,
use ```bash or ```text instead of plain ```), and normalize heading spacing by
ensuring a single blank line after each heading (fix MD031, MD040, MD022). Apply
these changes to the shown Example block and the other reported ranges (86-92,
109-116, 144-149, 151-207, 210-263, 350-367, 370-373, 522-524, 527-529, 532-534,
537-539) so all fenced blocks include proper surrounding blank lines and
language markers and all headings have consistent blank-line spacing.
- Around line 118-120: The README currently tells users to export
JIRA_AUTH_TYPE="bearer" but no code reads it; either remove that step from the
docs or wire JIRA_AUTH_TYPE into the code path that builds the Authorization
header: read process.env.JIRA_AUTH_TYPE (JIRA_AUTH_TYPE) where the auth header
is constructed (the module/function that sets the HTTP Authorization header /
auth header creation logic) and if it equals "bearer" send "Authorization:
Bearer <token>" instead of the current behavior, ensuring tests/usage examples
reflect the change.

---

Duplicate comments:
In `@workflows/cve-fixer/.claude/commands/cve.find.md`:
- Around line 264-343: The ignore-comment filtering loop always calls curl with
JIRA_API_TOKEN (COMMENTS_RESPONSE) which breaks the MCP-only execution path;
update the for KEY in "${ISSUE_KEYS[@]}" loop to branch on the transport/mode
flag (the MCP activation variable used earlier in the script) and, when MCP is
active, call the MCP comment API instead of curl (populate
COMMENTS_BODY/COMMENTS_HTTP_CODE from the MCP response), otherwise keep the
existing curl path using JIRA_API_TOKEN; ensure variables used later
(IGNORE_MATCH, IGNORED_ISSUES, IGNORED_REASONS, /tmp/jira-cve-response.json
updates and TOTAL extraction) remain unchanged so filtering logic works
identically in both branches.
- Around line 206-207: The script currently writes shared files like
"/tmp/jira-cve-response.json" (and other "/tmp/jira-cve-*" paths referenced
throughout) which can collide in concurrent runs; change the script to create a
per-run temp directory at start with TMP_DIR="$(mktemp -d)" and install a
cleanup trap (trap 'rm -rf "$TMP_DIR"' EXIT), then replace every hardcoded
"/tmp/jira-cve-*" filename (e.g., the file written in the echo "$BODY" >
/tmp/jira-cve-response.json line and all other occurrences listed in the
comment) with "${TMP_DIR}/jira-cve-*" so all reads/writes use the per-run
directory and are removed on exit. Ensure TMP_DIR is exported/available to all
functions or subshells that reference these paths.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: ad3d0b43-f4e6-4601-922a-1e0157070f59

📥 Commits

Reviewing files that changed from the base of the PR and between daee6be and 918650b.

📒 Files selected for processing (1)
  • workflows/cve-fixer/.claude/commands/cve.find.md

- Add blank lines around code blocks per MD031
- Add language specs to all code blocks per MD040
- Remove unused JIRA_AUTH_TYPE variable setup (Step 3)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (3)
workflows/cve-fixer/.claude/commands/cve.find.md (3)

262-341: ⚠️ Potential issue | 🔴 Critical

Step 5 breaks MCP-only flow by unconditionally using tokenized curl calls.

Line 290 onward always fetches comments via curl + Authorization: Bearer ${JIRA_API_TOKEN} even when Step 1 selected MCP and Step 3 was skipped. That makes MCP mode non-functional in this phase. Add an explicit transport branch in this loop so MCP mode never touches JIRA_API_TOKEN and uses MCP comment APIs instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@workflows/cve-fixer/.claude/commands/cve.find.md` around lines 262 - 341, The
loop that checks comments (for KEY in "${ISSUE_KEYS[@]}") always calls curl with
Authorization: Bearer ${JIRA_API_TOKEN} (building COMMENTS_RESPONSE /
COMMENTS_HTTP_CODE / COMMENTS_BODY), breaking MCP-only flow; change the loop to
branch on transport mode (e.g., if using MCP mode/variable set) and call the MCP
comment API path instead of the tokenized curl, otherwise keep the existing curl
path that uses JIRA_API_TOKEN; ensure the branch updates the same variables used
later (COMMENTS_RESPONSE/COMMENTS_HTTP_CODE/COMMENTS_BODY or normalized
equivalents) so the subsequent ignore-pattern check (IGNORE_MATCH, REASON,
IGNORED_ISSUES) remains unchanged.

3-4: ⚠️ Potential issue | 🟡 Minor

Address remaining markdownlint violations (MD022/MD031) in this file.

Static analysis still reports heading-spacing and fenced-block spacing failures at multiple locations (e.g., Line 3 and Line 509 for MD022; several fenced blocks including Line 87 for MD031). These should be normalized to keep lint clean in CI.

Also applies to: 87-93, 110-117, 142-146, 149-205, 208-260, 273-341, 348-365, 368-371, 378-507, 509-510

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@workflows/cve-fixer/.claude/commands/cve.find.md` around lines 3 - 4, The
file workflows/cve-fixer/.claude/commands/cve.find.md has markdownlint MD022
(heading spacing) and MD031 (fenced code block spacing) violations; fix by
ensuring every ATX heading (e.g., "## Purpose" and other "##", "###" headings)
is preceded by exactly one blank line and followed by a single blank line, and
normalize all fenced code blocks (the blocks around lines referenced such as the
blocks near lines 87, 110, 142, 149-205, etc.) so there is a blank line after
the opening ``` fence, no trailing spaces inside the block, and a blank line
before the closing ``` fence; apply this pattern consistently across the file to
resolve MD022/MD031 failures.

203-259: ⚠️ Potential issue | 🟠 Major

Replace hardcoded /tmp/jira-cve-* files with a per-run temp directory.

The script still uses shared /tmp/jira-cve-* filenames across phases (query, pagination, filtering, reporting). Concurrent executions can overwrite each other and leak data between runs. Initialize TMP_DIR="$(mktemp -d -t cve-find-XXXXXX)" once, trap 'rm -rf "$TMP_DIR"' EXIT, and use $TMP_DIR/... everywhere.

Also applies to: 277-340, 350-353, 385-387, 412-413, 438-439, 444-445, 451-452, 466-467, 474-475, 487-488, 505-506

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@workflows/cve-fixer/.claude/commands/cve.find.md` around lines 203 - 259, The
script currently writes shared files like /tmp/jira-cve-response.json and
/tmp/jira-cve-response-page-*.json causing race/leak issues; fix by creating a
per-run temp directory once (e.g., TMP_DIR="$(mktemp -d -t cve-find-XXXXXX)";
trap 'rm -rf "$TMP_DIR"' EXIT) at script start and replace all occurrences of
/tmp/jira-cve-* with "$TMP_DIR"/jira-cve-* (including the initial save,
pagination append, jq merge, ls checks and mv) so the pagination loop that
writes /tmp/jira-cve-response-page-${START_AT}.json and the merge command that
reads /tmp/jira-cve-response.json and /tmp/jira-cve-response-page-*.json use the
per-run TMP_DIR.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@workflows/cve-fixer/.claude/commands/cve.find.md`:
- Around line 29-33: The docs currently contradict about whether jq is required:
decide which behavior you want and make both sections consistent—either (A) make
jq mandatory by removing the plain curl fallback and updating the "Alternative
Option - Manual Setup" bullet to state jq is required, or (B) mark jq as
optional by changing the prerequisite wording to "jq recommended/optional for
JSON parsing" and add a note in the curl fallback (the curl-only section)
describing reduced functionality and manual JSON handling steps; update both the
"Alternative Option - Manual Setup" header text and the curl fallback paragraph
to reflect the chosen option so the two places no longer conflict.

---

Duplicate comments:
In `@workflows/cve-fixer/.claude/commands/cve.find.md`:
- Around line 262-341: The loop that checks comments (for KEY in
"${ISSUE_KEYS[@]}") always calls curl with Authorization: Bearer
${JIRA_API_TOKEN} (building COMMENTS_RESPONSE / COMMENTS_HTTP_CODE /
COMMENTS_BODY), breaking MCP-only flow; change the loop to branch on transport
mode (e.g., if using MCP mode/variable set) and call the MCP comment API path
instead of the tokenized curl, otherwise keep the existing curl path that uses
JIRA_API_TOKEN; ensure the branch updates the same variables used later
(COMMENTS_RESPONSE/COMMENTS_HTTP_CODE/COMMENTS_BODY or normalized equivalents)
so the subsequent ignore-pattern check (IGNORE_MATCH, REASON, IGNORED_ISSUES)
remains unchanged.
- Around line 3-4: The file workflows/cve-fixer/.claude/commands/cve.find.md has
markdownlint MD022 (heading spacing) and MD031 (fenced code block spacing)
violations; fix by ensuring every ATX heading (e.g., "## Purpose" and other
"##", "###" headings) is preceded by exactly one blank line and followed by a
single blank line, and normalize all fenced code blocks (the blocks around lines
referenced such as the blocks near lines 87, 110, 142, 149-205, etc.) so there
is a blank line after the opening ``` fence, no trailing spaces inside the
block, and a blank line before the closing ``` fence; apply this pattern
consistently across the file to resolve MD022/MD031 failures.
- Around line 203-259: The script currently writes shared files like
/tmp/jira-cve-response.json and /tmp/jira-cve-response-page-*.json causing
race/leak issues; fix by creating a per-run temp directory once (e.g.,
TMP_DIR="$(mktemp -d -t cve-find-XXXXXX)"; trap 'rm -rf "$TMP_DIR"' EXIT) at
script start and replace all occurrences of /tmp/jira-cve-* with
"$TMP_DIR"/jira-cve-* (including the initial save, pagination append, jq merge,
ls checks and mv) so the pagination loop that writes
/tmp/jira-cve-response-page-${START_AT}.json and the merge command that reads
/tmp/jira-cve-response.json and /tmp/jira-cve-response-page-*.json use the
per-run TMP_DIR.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7315a348-6861-4d1d-9523-57d5cc8db326

📥 Commits

Reviewing files that changed from the base of the PR and between 918650b and 40c5fae.

📒 Files selected for processing (1)
  • workflows/cve-fixer/.claude/commands/cve.find.md

angaduom and others added 6 commits March 19, 2026 10:52
BREAKING CHANGES:
- Remove MCP server integration entirely (simplify to curl-only)
- Update Jira URL from issues.redhat.com to redhat.atlassian.net
- Make jq truly required (remove grep/cut fallback)
- Remove presentation files (create_presentation.py, .pptx, combined.md)

Changes:
- cve.find.md: Remove step 1 (MCP check), renumber all steps
- cve.find.md: Remove all MCP server code paths and documentation
- cve.find.md: Update JIRA_BASE_URL to redhat.atlassian.net
- cve.find.md: Remove jq fallback section (lines 313-317)
- cve.fix.md: Update example Jira URL to redhat.atlassian.net
- README.md: Remove MCP from prerequisites, update Jira URL

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The `text` field is not supported by the Jira v3 `/search/jql` endpoint,
causing 400 errors. Changed to `summary ~ "CVE"` which works correctly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Update FIELD_REFERENCE.md artifact mappings to match ambient.json (only 2 implemented)
- Fix argoproj/argo-workflows primary_target and add excluded_from_automation flag
- Update README.md to only document implemented commands (/cve.find and /cve.fix)
- Fix broken ACP documentation link (https://docs.ambient-code.com)
- Add go.mod resolution from build_location for monorepos in cve.fix.md
- Preserve Jira IDs from explicit input instead of re-parsing /cve.find output
- Add cleanup phase (Step 12) for temporary repository clones in /tmp
- Add --base parameter to PR duplicate detection to scope by target branch
- Merge upstream documentation improvements for existing PR detection

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- Replace angaduom/models-as-a-service with opendatahub-io/models-as-a-service
- Remove downstream fork angaduom/models-as-a-service-downstream
- Update branch_strategy to TBD (from fork description)
- Remove repo_type field
- Update notes to reflect original repository

This addresses PR comment about fork mappings pointing to test repos
instead of canonical upstream repositories.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Added back the downstream repository mapping that was incorrectly removed.

Changes:
- Upstream: opendatahub-io/models-as-a-service (main branch)
- Downstream: red-hat-data-services/models-as-a-service (rhoai-3.0 branch)

Both now point to original repositories instead of forks:
- angaduom/models-as-a-service → opendatahub-io/models-as-a-service
- angaduom/models-as-a-service-downstream → red-hat-data-services/models-as-a-service

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Changed Jira JQL query from:
  summary ~ "CVE"
to:
  summary ~ "CVE*"

The wildcard pattern correctly matches CVE IDs like "CVE-2024-12345"
whereas the previous pattern without the asterisk was not working.

This affects:
- The main JQL query construction in Step 3
- The query documentation shown in generated reports

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
@kami619
Copy link
Copy Markdown

kami619 commented Mar 20, 2026

@angaduom - this is a great first step to remediate CVE workflows using a tight agentic loop based workflow.

A good example workflow to refer is (which is an awesome workflow btw, you should try this) - https://github.com/ambient-code/workflows/tree/main/workflows/bugfix

Without changing too much direction few things that can help your cve fix workflow are below.,

Add a dedicated CLAUDE.md:

With hard limits (no force-push, no commits to main, no skipping CI), safety rules (show plan first, indicate confidence), and quality standards (zero test failures, conventional commits). The settings.json allows Bash broadly and only denies Bash(rm). For a workflow that clones repos, runs go get, and creates PRs, there should be explicit guardrails around:

  • Never force-pushing
  • Never modifying protected branches directly
  • Requiring human approval before PR creation (or at least a confirmation step)
  • How to handle failed tests (the current instructions say "run tests" but don't define what to do if they fail)

Tight coupling to RHOAI and RH specific assets:

  • redhat.atlassian.net as the Jira instance
  • Specific RHOAI component names and repos

We might want to make this generic down the line. This is an opportunity to improve in a future PR.

Inline bash and curl commands:

The cve.find.md file contains many inline bash — curl commands with specific JQL construction, jq pipelines for JSON parsing, pagination logic. Consider moving these into thin scripts outside of the markdown file for easier maintenance.

If you can add the CLAUDE.md and add a gating for PR creation needing approval, this workflow looks good to go. I can test them with some CVE's that might be in the system to have the full experience to find any other interesting issues with this flow.

Cheers !

Added comprehensive agent guidelines covering:

Hard Limits:
- Never force-push or modify protected branches
- Never skip git hooks or CI checks
- Always create feature branches
- Always verify CVE exists before fixing
- Always check for duplicate PRs

Safety Rules:
- Version-matched vulnerability scanning
- Test execution before PR creation
- Comprehensive documentation for all actions
- Graceful error handling

Quality Standards:
- Test failure handling (create PR anyway with details)
- Conventional commit format
- Comprehensive PR descriptions with test results
- Risk assessments and verification checklists

Automation Support:
- Designed for scheduled/unattended runs
- No manual approval gates (PRs are the review mechanism)
- All output saved to artifacts
- Automatic cleanup of /tmp clones

Addresses reviewer feedback from @kami619 while maintaining
support for automated scheduled runs.

Reference: bugfix workflow best practices
https://github.com/ambient-code/workflows/tree/main/workflows/bugfix

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
@vmrh21
Copy link
Copy Markdown
Contributor

vmrh21 commented Mar 23, 2026

Hi @kami619,
The claude md file is now part of the workflow. Let me know if you'd like any adjustments!

Copy link
Copy Markdown
Contributor

@jwm4 jwm4 left a comment

Choose a reason for hiding this comment

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

Honestly, I'm not super happy about putting a workflow designed for fixings CVEs reported by product security in Jira into Ambient (as we have discussed in Slack). Red Hat is an open source company, and a workflow narrowly targeted to our own internal product concerns feels like a bad fit. However, I think this is useful in its current form for some teams, and I think it's a solid starting point for something that could become a generally useful capability that would be broadly applicable for working on open source. So I think it's probably OK to go ahead with this anyway despite my reservations.

I also have a couple of technical concerns that I raised below. None of them seem like they are critical defects that must be fixed. I would recommend consider addressing one or both of these before merging if you have time. If not, let's open new tickets on the tracker for somebody to address them in the future.

There's nothing in my review that's an absolute blocker to merging the PR as-is, I'm marking it as approved.

- **This is the ONLY thing the user must configure manually before proceeding**

- Check if JIRA_API_TOKEN and JIRA_EMAIL are set:
```bash
Copy link
Copy Markdown
Contributor

@jwm4 jwm4 Mar 24, 2026

Choose a reason for hiding this comment

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

Putting a bash script in the markdown like this (and other places below) has pros and cons. I think in most cases, the best practice is to put scripts into a separate scripts directory and have the commands or skills reference them. That way, the agents can just run the scripts. The way it's done here by putting it into the markdown file means that the agent has to first read the script, and then either write it to disk to run it or turn it into one or more shell commands to run it. That consumes a lot of extra input, tokens and output tokens, making it slower and more expensive to operate. It also, causes the context window to fill up faster. When the context window fills up, the agentic runner will "compact it", but compacting the context window is lossy, and sometimes it can cause the agent to stop following instructions that were given earlier in the process.

On the other hand, there is a benefit to doing it this way: by forcing the model to actually process all of the tokens in the script, it has some "understanding" of what it's being instructed to do which puts it in a better position to be flexible. If it encounters a situation where the desired behavior isn't quite what's in your bash code, but it is kind of like what's in your bash code it would be potentially more likely to get that right the way you have this here in the markdown file then if you put it in the scripts directory. In the the scripts directory it still can read the file if it wants to, but it isn't really being forced to and so there's a decent chance that it won't have looked at the script when the time comes to do something that's kind of like the script but not exactly the same.

For now, I would say this is acceptable as is, but I would also call it tech debt that we should consider addressing in a future version of this workflow. My guess is that moving all the scripts to a scripts directory and just referencing them will produce faster and more reliable results. However, there are reasons why I might be wrong, which is why this is something I wouldn't want to do without substantial testing.

@@ -0,0 +1,494 @@
# /cve.find - Find CVEs in Jira
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.

Commands in Claude Code are deprecated and also not as portable as skills. We still have a bunch of them in the workflow repo, but we'd like to reduce that number over time. So my preference would be for these to be converted into skills before we merge this. However, I wouldn't say this is an absolutely must address requirement for right now. We could also merge these as commands and then convert them to skills in the future.

Copy link
Copy Markdown

@kami619 kami619 left a comment

Choose a reason for hiding this comment

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

I agree with the sentiment from @jwm4's review but I think we can iterate to make this workflow generic in future. with that in mind, approving this to not block the work done by @angaduom and @vmrh21

@jwm4 jwm4 merged commit 4a058d0 into ambient-code:main Mar 25, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants