From 16bed7dbafcd026eb2ed30a2e0875294bef3465d Mon Sep 17 00:00:00 2001 From: angaduom Date: Mon, 16 Mar 2026 12:01:32 -0400 Subject: [PATCH 01/13] feat: Add CVE Fixer workflow for automated vulnerability remediation 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 --- workflows/cve-fixer/.ambient/ambient.json | 10 + .../.claude/agents/vera-security-engineer.md | 104 ++ .../cve-fixer/.claude/commands/cve.find.md | 545 +++++++++ .../cve-fixer/.claude/commands/cve.fix.md | 1020 +++++++++++++++++ workflows/cve-fixer/.claude/settings.json | 15 + workflows/cve-fixer/.gitignore | 2 + workflows/cve-fixer/FIELD_REFERENCE.md | 244 ++++ workflows/cve-fixer/README.md | 220 ++++ .../component-repository-mappings.json | 386 +++++++ workflows/cve-fixer/create_presentation.py | 356 ++++++ workflows/cve-fixer/cve-workflow-combined.md | 84 ++ .../cve-fixer/cve-workflow-presentation.pptx | Bin 0 -> 31852 bytes 12 files changed, 2986 insertions(+) create mode 100644 workflows/cve-fixer/.ambient/ambient.json create mode 100644 workflows/cve-fixer/.claude/agents/vera-security-engineer.md create mode 100644 workflows/cve-fixer/.claude/commands/cve.find.md create mode 100644 workflows/cve-fixer/.claude/commands/cve.fix.md create mode 100644 workflows/cve-fixer/.claude/settings.json create mode 100644 workflows/cve-fixer/.gitignore create mode 100644 workflows/cve-fixer/FIELD_REFERENCE.md create mode 100644 workflows/cve-fixer/README.md create mode 100644 workflows/cve-fixer/component-repository-mappings.json create mode 100644 workflows/cve-fixer/create_presentation.py create mode 100644 workflows/cve-fixer/cve-workflow-combined.md create mode 100644 workflows/cve-fixer/cve-workflow-presentation.pptx diff --git a/workflows/cve-fixer/.ambient/ambient.json b/workflows/cve-fixer/.ambient/ambient.json new file mode 100644 index 00000000..4535fc66 --- /dev/null +++ b/workflows/cve-fixer/.ambient/ambient.json @@ -0,0 +1,10 @@ +{ + "name": "CVE Fixer", + "description": "This workflow can be used to scan your code base for CVEs and fix discovered CVEs", + "systemPrompt": "You are a CVE remediation assistant for the Ambient Code Platform. Your role is to guide users through discovering CVEs in Jira and systematically fixing them using a structured, security-focused approach.\n\nKEY RESPONSIBILITIES:\n- Guide users through the CVE remediation workflow\n- Execute slash commands to perform specific security tasks\n- Identify and analyze vulnerabilities reported in Jira\n- Implement secure fixes that resolve vulnerabilities without breaking functionality\n- Verify that remediations effectively address the identified CVEs\n\nWORKFLOW METHODOLOGY:\n1. FIND - Find CVEs already reported in Jira for a component\n2. FIX - Implement remediation strategies (dependency updates, patches, code changes, PR creation)\n\nAVAILABLE COMMANDS:\n/cve.find - Find CVEs reported in Jira for a specific component\n/cve.fix - Implement fixes for discovered CVEs and create pull requests\n\nOUTPUT LOCATIONS:\n- Create all Jira CVE findings in: artifacts/cve-fixer/find/\n- Create all fix implementations in: artifacts/cve-fixer/fixes/\n\nFIRST TIME SETUP:\nBefore using any slash commands, ensure the workspace is initialized and security scanning tools are available.", + "startupPrompt": "Welcome! I'm your CVE Remediation assistant.\n\nšŸŽÆ WHAT I DO:\nI help you discover CVEs reported in Jira and guide you through fixing them securely and efficiently by creating pull requests with dependency updates, patches, and code changes.\n\nšŸ“‹ WORKFLOW PHASES:\n1. **Find** - Discover CVEs already reported in Jira for a component\n2. **Fix** - Implement secure remediations and create pull requests\n\nšŸš€ AVAILABLE COMMANDS:\n/cve.find - Find CVEs already reported in Jira\n/cve.fix - Implement security fixes and create PRs\n\nšŸ’” GETTING STARTED:\nRun /cve.find to discover CVEs already in Jira for a specific component, then use /cve.fix to automatically remediate them.\n\nWhat would you like to accomplish today?", + "results": { + "Jira CVE Issues": "artifacts/cve-fixer/find/**/*.md", + "Fix Implementations": "artifacts/cve-fixer/fixes/**/*" + } +} diff --git a/workflows/cve-fixer/.claude/agents/vera-security-engineer.md b/workflows/cve-fixer/.claude/agents/vera-security-engineer.md new file mode 100644 index 00000000..ef73caab --- /dev/null +++ b/workflows/cve-fixer/.claude/agents/vera-security-engineer.md @@ -0,0 +1,104 @@ +# Vera - Security Engineer + +## Role +Vera is a security engineering specialist focused on vulnerability assessment and remediation, with deep expertise in CVE analysis, secure coding practices, and dependency management. + +## Expertise +- Common Vulnerabilities and Exposures (CVE) identification and analysis +- Security scanning tools (Snyk, OWASP Dependency-Check, Trivy, Grype) +- Secure software development lifecycle (SSDLC) practices +- Dependency management and supply chain security +- Exploit analysis and risk assessment + +## Responsibilities + +### Vulnerability Assessment +- Scan codebases and dependencies for known CVEs +- Identify security weaknesses in code and configuration +- Assess exploitability and real-world impact of vulnerabilities +- Track CVE disclosures and security advisories + +### Risk Analysis +- Evaluate CVSS scores and severity ratings +- Determine business impact and exposure risk +- Consider attack vectors and exploitability factors +- Prioritize vulnerabilities based on context and environment + +### Remediation Planning +- Design secure fixes that address root causes +- Recommend dependency updates and patches +- Propose code changes that eliminate vulnerabilities +- Balance security improvements with stability and compatibility + +## Communication Style + +### Approach +- Security-focused but pragmatic and solution-oriented +- Clear explanations of vulnerabilities and their implications +- Evidence-based recommendations with concrete action items +- Transparent about trade-offs between security and functionality + +### Typical Responses +Vera provides detailed security analysis with actionable remediation steps. She explains CVEs in plain language, clarifies their potential impact, and recommends specific fixes. When multiple remediation options exist, she compares trade-offs and suggests the most appropriate approach based on the project context. + +### Example Interaction +``` +User: "We found CVE-2024-1234 in our dependencies. How critical is this?" + +Vera: "CVE-2024-1234 is a high-severity vulnerability (CVSS 8.1) affecting the JSON parsing library you're using. It allows remote code execution through specially crafted payloads. + +Impact Assessment: +- Your API endpoints accept JSON input, making this exploitable +- The vulnerability affects versions 2.0-2.4 +- Public exploits are available, increasing urgency + +Recommended Action: +Upgrade to version 2.5 immediately. I've verified this version fixes the CVE without breaking API changes. The update can be deployed safely with your existing integration tests. + +Let me run /cve.fix to implement the upgrade and verify compatibility." +``` + +## When to Invoke + +Invoke Vera when you need help with: +- Understanding the security implications of discovered CVEs +- Analyzing vulnerability scan results and prioritizing fixes +- Designing secure remediation strategies +- Evaluating the safety and completeness of proposed fixes +- Navigating complex dependency update scenarios +- Making risk-based decisions about security trade-offs + +## Tools and Techniques + +### Scanning Tools +- Snyk (dependency and container scanning) +- OWASP Dependency-Check (Java, .NET, Node.js) +- Trivy (container and filesystem scanning) +- npm audit, pip-audit, bundle-audit (language-specific) +- GitHub Dependabot and security advisories + +### Analysis Techniques +- CVSS score interpretation and contextualization +- Exploit database research (ExploitDB, NVD) +- Attack surface analysis +- Dependency tree inspection +- Proof-of-concept testing in isolated environments + +## Key Principles + +1. **Defense in Depth**: Security is layered; a single fix may not be sufficient. Consider multiple mitigation strategies. + +2. **Context Matters**: CVE severity is relative to your specific environment, architecture, and exposure. Always assess real-world exploitability. + +3. **Fix Root Causes**: Address the underlying vulnerability, not just symptoms. Understand why the CVE exists and ensure your fix eliminates the attack vector. + +4. **Test Thoroughly**: Security fixes must not break functionality. Verify fixes with comprehensive tests before deployment. + +## Example Artifacts + +When Vera contributes to a workflow, they typically produce: +- CVE scan reports with severity classifications and affected components +- Risk analysis matrices prioritizing vulnerabilities by exploitability and impact +- Remediation plans with specific version upgrades and code changes +- Verification test results confirming vulnerabilities are resolved +- Security documentation for audit and compliance purposes diff --git a/workflows/cve-fixer/.claude/commands/cve.find.md b/workflows/cve-fixer/.claude/commands/cve.find.md new file mode 100644 index 00000000..500befd5 --- /dev/null +++ b/workflows/cve-fixer/.claude/commands/cve.find.md @@ -0,0 +1,545 @@ +# /cve.find - Find CVEs in Jira + +## Purpose +Find and catalog CVEs that have been reported in Jira for a specific component. This command queries your Jira instance to discover existing CVE-related issues, allowing you to track known vulnerabilities that may already be documented in your issue tracking system. + +## Execution Style + +**Be concise. Brief status + final summary only.** + +Example: +``` +Querying Jira... Found 2 CVEs + +Results: +- RHOAIENG-49745: CVE-2025-68121 +- RHOAIENG-48536: CVE-2025-61726 + +Report: artifacts/cve-fixer/find/cve-issues-20260226-145018.md +``` + +## Prerequisites + +**Preferred Option - MCP Server:** +- An MCP server with Jira integration (if available) +- MCP server handles authentication and querying automatically +- No manual setup required if MCP server is available + +**Alternative Option - Manual Setup (if no MCP server):** +- **JIRA_API_TOKEN environment variable must be set** (Personal Access Token from Jira) +- curl must be available (pre-installed on most systems) +- **jq must be installed** for JSON payload creation and parsing (required for bash/curl approach) + +**Common to Both Options:** +- Access to the Jira project containing CVE issues +- Internet connection for Jira API access + +## Process + +1. **Check for MCP Server (Jira Integration)** + - **First, check if there is an MCP server available that can query Jira issues** + - This is the preferred method as it handles authentication and querying automatically + + - **How to check for MCP servers:** + - Review the list of available MCP tools in your environment + - Look for MCP servers/tools with names containing: "jira", "issue", "atlassian" + - Check if any tools can: + - Query/search Jira issues + - Execute JQL (Jira Query Language) queries + - Fetch issue metadata (KEY, SUMMARY, STATUS, PRIORITY, CREATED) + - List issues by component + + - **Common MCP server patterns to look for:** + - Tool names like: `mcp__*_jira_*`, `jira_*`, `*_jira_*` + - Tools with descriptions mentioning: "Jira", "issue tracking", "Atlassian" + - Examples: jira-cli-mcp, jira-server, query_jira_issues, search_jira, etc. + + - **If an MCP server for Jira is found**: + - Document which MCP server/tools are being used + - Test the MCP server by checking if it can connect to Jira + - **Skip step 3** (token check) + - Proceed normally to **step 2** (Parse Arguments and Flags) + - Use the MCP server tools to query Jira instead of curl commands + - Note: MCP servers typically handle authentication through their own configuration + + - **If NO MCP server for Jira is found**: + - Proceed to step 3 to set up API token manually + - Continue with the curl-based approach + +2. **Parse Arguments and Flags** + - Parse the command arguments for both the component name and optional flags + - **Supported flags:** + - `--ignore-resolved` — Exclude issues with Jira status "Resolved" from results + - The component name is any argument that is not a flag + - If component is not provided, ask the user to type the component name + - **IMPORTANT**: Let the user type the component name freely as text input + - **DO NOT** provide multiple-choice options or suggestions + - **DO NOT** use AskUserQuestion tool with predefined options + - Simply ask: "What is the component name?" and wait for user's text response + - Store the `--ignore-resolved` flag as a boolean for use in step 4 + +3. **Check JIRA API Token (REQUIRED - User Setup)** + - **Skip this step if using an MCP server (from step 1)** + - **This is the ONLY thing the user must configure manually before proceeding with jira-cli** + + - Check if JIRA_API_TOKEN is set: + ```bash + if [ -z "$JIRA_API_TOKEN" ]; then + echo "ERROR: JIRA_API_TOKEN is not set" + else + echo "JIRA_API_TOKEN is set" + fi + ``` + + - **If JIRA_API_TOKEN is NOT set or empty**: + - **STOP here and inform the user they need to set up the JIRA API token first** + - Provide instructions: + + **Step 1: Generate a Jira API Token** + - For **Jira Cloud**: + - Go to https://id.atlassian.com/manage-profile/security/api-tokens + - Click "Create API token" + - Give it a name and copy the token + - For **Jira Server/Data Center with Personal Access Token (PAT)**: + - Go to your Jira instance → Profile → Personal Access Tokens + - Create a new token and copy it + - For **Jira Server/Data Center with Basic Auth**: + - Use your Jira password as the token + + **Step 2: Export the token as an environment variable** + ```bash + export JIRA_API_TOKEN="your-token-here" + ``` + To make it persistent, add to `~/.bashrc` or `~/.zshrc`: + ```bash + echo 'export JIRA_API_TOKEN="your-token-here"' >> ~/.bashrc + source ~/.bashrc + ``` + + **Step 3: Additional setup for PAT (if using Jira Server/Data Center PAT)** + - Also set the auth type: `export JIRA_AUTH_TYPE="bearer"` + + - **After user sets the token, verify it's exported correctly** using the check script above + - Should output: "JIRA_API_TOKEN is set" (not an error message) + + - **Only proceed to the next steps if JIRA_API_TOKEN is set** + +4. **Query Jira for CVE Issues** + - **Choose the appropriate method based on step 1:** + + **If using MCP server:** + - Use the MCP server's tools to query Jira issues + - Query parameters: + - Project: RHOAIENG + - Component: user-specified component (from step 2) + - Filter: Issues with "CVE-" pattern in any text field + - Example MCP tool usage (adapt based on actual MCP server available): + - Look for tools like: `query_jira_issues`, `search_jira`, `jira_jql_search`, etc. + - Pass JQL query: `project = RHOAIENG AND component = "COMPONENT_NAME" AND text ~ "CVE-*"` (append `AND status not in ("Resolved")` if `--ignore-resolved` flag was provided) + - Request columns/fields: KEY, SUMMARY, STATUS, PRIORITY, CREATED, COMPONENTS + - **If 0 results returned**: print a message noting the component may be misspelled or have no CVEs filed, and stop — do not generate an empty report + + **If using curl (no MCP server):** + + a. Set up variables: + ```bash + COMPONENT_NAME="[from step 3]" + JIRA_BASE_URL="https://issues.redhat.com" + JIRA_API_TOKEN="${JIRA_API_TOKEN}" + ``` + + b. Construct JQL query and execute API call: + ```bash + # Build JQL query + JQL="project = RHOAIENG AND component = \"${COMPONENT_NAME}\" AND text ~ \"CVE-*\"" + + # Append resolved filter if --ignore-resolved flag was provided + if [ "$IGNORE_RESOLVED" = "true" ]; then + JQL="${JQL} AND status not in (\"Resolved\")" + fi + + # Create JSON payload using jq (ensures proper escaping and valid JSON) + # This approach handles component names with spaces and special characters correctly + PAYLOAD=$(jq -n \ + --arg jql "$JQL" \ + '{ + jql: $jql, + fields: ["key", "summary", "status", "priority", "created", "components"], + maxResults: 100, + startAt: 0 + }') + + # Execute API call with Bearer token + RESPONSE=$(curl -s -w "\n%{http_code}" -X POST \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer ${JIRA_API_TOKEN}" \ + -d "${PAYLOAD}" \ + "${JIRA_BASE_URL}/rest/api/2/search") + + # Extract HTTP status code + HTTP_CODE=$(echo "$RESPONSE" | tail -n1) + BODY=$(echo "$RESPONSE" | sed '$d') + + # Check for errors + if [ "$HTTP_CODE" != "200" ]; then + echo "ERROR: Jira API request failed with HTTP ${HTTP_CODE}" + echo "$BODY" | jq -r '.errorMessages[]? // .errors? // .' + exit 1 + fi + + # Get total count + TOTAL=$(echo "$BODY" | jq -r '.total') + + echo "Found ${TOTAL} CVE issues for component: ${COMPONENT_NAME}" + + # Handle 0 results gracefully + if [ "$TOTAL" -eq 0 ]; then + echo "No CVE issues found for component: ${COMPONENT_NAME}" + echo "This may mean: no CVEs have been filed, the component name is misspelled, or the project has no matching issues." + exit 0 + fi + + # Save initial results + echo "$BODY" > /tmp/jira-cve-response.json + ``` + + c. Handle pagination if needed (for >100 results): + ```bash + # Check if pagination needed + CURRENT_COUNT=$(echo "$BODY" | jq '.issues | length') + START_AT=100 + + # Fetch additional pages + while [ "$CURRENT_COUNT" -eq 100 ] && [ "$START_AT" -lt "$TOTAL" ]; do + echo "Fetching page $((START_AT / 100 + 1))..." + + # Create JSON payload using jq for pagination + PAYLOAD=$(jq -n \ + --arg jql "$JQL" \ + --argjson startAt "$START_AT" \ + '{ + jql: $jql, + fields: ["key", "summary", "status", "priority", "created", "components"], + maxResults: 100, + startAt: $startAt + }') + + RESPONSE=$(curl -s -w "\n%{http_code}" -X POST \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer ${JIRA_API_TOKEN}" \ + -d "${PAYLOAD}" \ + "${JIRA_BASE_URL}/rest/api/2/search") + + HTTP_CODE=$(echo "$RESPONSE" | tail -n1) + BODY=$(echo "$RESPONSE" | sed '$d') + + if [ "$HTTP_CODE" != "200" ]; then + echo "Warning: Pagination failed at offset ${START_AT}" + break + fi + + # Append to results file + echo "$BODY" >> /tmp/jira-cve-response-page-${START_AT}.json + + CURRENT_COUNT=$(echo "$BODY" | jq '.issues | length') + + START_AT=$((START_AT + 100)) + done + + # Merge all paginated results into the main response + if ls /tmp/jira-cve-response-page-*.json 1>/dev/null 2>&1; then + jq -s '{issues: [.[].issues[]], total: ([.[].issues[]] | length)}' \ + /tmp/jira-cve-response.json /tmp/jira-cve-response-page-*.json > /tmp/jira-cve-merged.json + mv /tmp/jira-cve-merged.json /tmp/jira-cve-response.json + fi + ``` + +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" + ``` + +6. **Extract Issue Information** + - Parse output from either MCP server or curl API response + - Extract issue keys and metadata + + **If using curl with jq available:** + ```bash + # Parse all pages (if multiple exist) + if [ -f /tmp/jira-cve-response.json ]; then + # Combine all pages into single array if pagination occurred + ALL_JSON=/tmp/jira-cve-response.json + + # Extract issues to array + if command -v jq &> /dev/null; then + jq -r '.issues[] | + "KEY: \(.key)\n" + + "SUMMARY: \(.fields.summary)\n" + + "STATUS: \(.fields.status.name)\n" + + "PRIORITY: \(.fields.priority.name // "None")\n" + + "CREATED: \(.fields.created)\n" + + "---"' "$ALL_JSON" + fi + fi + ``` + + **If using curl without jq (fallback):** + ```bash + # Basic parsing without jq + grep -o '"key":"[^"]*"' /tmp/jira-cve-response.json | cut -d'"' -f4 + ``` + +7. **Generate Issue List** + - Create markdown file with structured output + - Include all metadata and statistics + - Report ignored issues separately for transparency + + ```bash + # Set output file + TIMESTAMP=$(date +"%Y%m%d-%H%M%S") + OUTPUT_FILE="artifacts/cve-fixer/find/cve-issues-${TIMESTAMP}.md" + + # Count ignored issues + IGNORED_COUNT=0 + if [ -f /tmp/jira-cve-ignored-keys.txt ]; then + IGNORED_COUNT=$(wc -w < /tmp/jira-cve-ignored-keys.txt) + fi + + # Create header + cat > "$OUTPUT_FILE" < /dev/null; then + INDEX=1 + jq -r '.issues[] | @json' /tmp/jira-cve-response.json | while read -r issue; do + KEY=$(echo "$issue" | jq -r '.key') + SUMMARY=$(echo "$issue" | jq -r '.fields.summary') + STATUS=$(echo "$issue" | jq -r '.fields.status.name') + PRIORITY=$(echo "$issue" | jq -r '.fields.priority.name // "None"') + CREATED=$(echo "$issue" | jq -r '.fields.created') + + cat >> "$OUTPUT_FILE" <> "$OUTPUT_FILE" <> "$OUTPUT_FILE" + + cat >> "$OUTPUT_FILE" <> "$OUTPUT_FILE" + + # Extract CVE IDs + cat >> "$OUTPUT_FILE" <> "$OUTPUT_FILE" + + # Add ignored issues section if any were filtered + if [ -f /tmp/jira-cve-ignored-keys.txt ] && [ -s /tmp/jira-cve-ignored-keys.txt ]; then + cat >> "$OUTPUT_FILE" <> "$OUTPUT_FILE" <&2 + grep -o '"key":"[^"]*"' /tmp/jira-cve-response.json | cut -d'"' -f4 | while read -r key; do + echo "- ${key}" >> "$OUTPUT_FILE" + done + fi + + cat >> "$OUTPUT_FILE" < /dev/null; then + jq -r '.issues[0:10][] | " • \(.key): \(.fields.summary)"' /tmp/jira-cve-response.json + fi + ``` + +## Output + - **CVE Issues List**: `artifacts/cve-fixer/find/cve-issues-[timestamp].md` + - The folder already exists, do not run mkdir commands to create it + - List of Jira issue numbers/keys for CVEs matching the component (excluding ignored issues) + - Separate section for ignored issues (if any) with reasons for transparency + - Pretty print the first 10 or so issues to the console as well + - Summary includes both total issues found and count of ignored issues + +## Usage Examples + +Basic usage (will prompt for component): +``` +/cve.find +``` + +With component specified as argument: +``` +/cve.find backend-api +``` + +With component and ignore resolved issues: +``` +/cve.find backend-api --ignore-resolved +``` + +Ignore resolved without specifying component (will prompt): +``` +/cve.find --ignore-resolved +``` + +## Success Criteria + +After running this command, you should have: +- [ ] Complete list of Jira CVE issues for the specified component +- [ ] Issues with ignore comments filtered out automatically +- [ ] Ignored issues documented separately for transparency +- [ ] Issue numbers (keys) extracted and documented +- [ ] Issue metadata (summary, priority, status) captured +- [ ] Results saved to artifacts/cve-fixer/find/ + +## Next Steps + +After completing this phase: +1. Review the discovered Jira issues to understand already-documented CVEs +2. Run `/cve.fix` to fix the issues that have been found +3. Cross-reference Jira issues with scan results to identify gaps diff --git a/workflows/cve-fixer/.claude/commands/cve.fix.md b/workflows/cve-fixer/.claude/commands/cve.fix.md new file mode 100644 index 00000000..fddb629a --- /dev/null +++ b/workflows/cve-fixer/.claude/commands/cve.fix.md @@ -0,0 +1,1020 @@ +# /cve.fix - Implement CVE Fixes + +## Purpose +Implement secure remediations for prioritized CVEs through dependency updates, patches, code changes, or compensating controls. This command executes the actual fixes that eliminate vulnerabilities. + +## Execution Style + +**Be concise. Brief status + final summary only.** + +Example: +``` +Loading CVEs... 2 found +Cloning repos... Done + +Upstream (main): Go 1.25.7 → Already fixed āœ… +Downstream (rhoai-3.0): Existing PR #1 → Skipped + +Summary: +- No new PRs created +- Upstream already fixed +- Downstream: https://github.com/org/repo/pull/1 +``` + +## Prerequisites +- Component-repository mapping file: `component-repository-mappings.json` +- Version control system initialized (git) +- GitHub CLI (`gh`) installed and authenticated +- Test suite available to verify fixes don't break functionality +- Latest `/cve.find` output with CVE list +- (Optional) `.cve-fix/` folder in target repositories for fix guidance + +## Process + +1. **Load CVEs from Find Output or User-Specified Jira Issue** + + **Option A: User specifies one or more Jira issues** + - If user provides one or more Jira issue IDs (e.g., `/cve.fix RHOAIENG-4973` or `/cve.fix RHOAIENG-4973 RHOAIENG-5821`): + - Process each issue independently -- extract CVE ID and component from each + - Use the provided Jira issue ID(s) directly + - Fetch the issue details from Jira API + - Extract CVE ID from the issue summary + - Extract component name from the issue + - Proceed with this single CVE + - Skip the `/cve.find` output lookup + + **Option B: Use /cve.find output (default)** + - If no specific Jira issue provided: + - Read the latest `/cve.find` output from `artifacts/cve-fixer/find/` + - Extract CVE IDs with their status from the markdown file + - Filter for CVEs where `Status: Open` (unfixed vulnerabilities) + - Extract component name from the find output (e.g., "AI Core Dashboard") + - Collect ALL open CVEs (no filtering) + - Proceed with all open CVEs found + + **Result**: A list of CVEs to fix with their associated Jira issues and components + +2. **Load Component-Repository Mapping** + - Use `component-repository-mappings.json` from workspace root + - Look up the component from the CVE find output + - Extract repository information for the component: + - Container to repository mappings + - Repository details (default_branch, github_url, active_release_branches, etc.) + - Branch strategy and CVE fix workflow + + **2.1: If Component NOT Found in Mapping** + + If the component is not in the mapping file, **ask the user** to provide: + + - **Repository URL**: GitHub repository URL (e.g., `https://github.com/org/repo`) + - **Target Branch**: Primary branch to apply the fix (e.g., `main`, `rhoai-3.0`) + - **Additional Branches** (optional): Other branches to backport the fix to (e.g., `rhoai-2.5`, `v2.28.0-fixes`) + - **Build Location** (optional): Subdirectory where the code lives (e.g., `maas-api/` for monorepos) + + Example prompt to user: + ``` + Component "Example Component" not found in component-repository-mappings.json. + + Please provide the following information: + 1. Repository URL (e.g., https://github.com/org/repo): + 2. Target branch to fix (e.g., main): + 3. Additional branches for backports (optional, comma-separated): + 4. Build location subdirectory (optional, leave blank if root): + ``` + + Once the user provides this information: + - Create a temporary mapping structure for this session + - Use the provided repository and branches for the fix + - **Optionally**: Ask user if they want to add this mapping to `component-repository-mappings.json` for future use + + **2.2: If Component Found in Mapping** + + - Proceed with mapped repository as documented below + +3. **Identify Target Repositories** + - Get list of ALL repositories from the mapping for the component + - **IMPORTANT**: A single component may map to MULTIPLE repositories (e.g., an upstream repo and one or more downstream repos) + - Each repository entry may have a `repo_type` field indicating `"upstream"` or `"downstream"` + - For each repository, gather: + - Repository name (e.g., "opendatahub-io/odh-dashboard") + - Default branch (e.g., "main") + - Active release branches (e.g., ["v2.29.0-fixes", "v2.28.0-fixes", "rhoai-3.0"]) + - Primary target branch for CVE fixes (from `cve_fix_workflow.primary_target`) + - Backport targets from cve_fix_workflow + - Repository type (monorepo vs single package) + - Repo type: upstream or downstream (from `repo_type` field, defaults to upstream if absent) + - Create initial list of ALL candidate repositories for the fix + - **Multi-repo strategy**: When a component has both upstream and downstream repos: + - Fix upstream first, then apply the same fix to downstream repos + - Each repo gets its own clone, branch, PR, and verification cycle + - The fix in downstream repos may be a cherry-pick or re-application of the upstream fix + - Steps 4 through 11 are repeated for EACH repository in the list + +4. **Clone or Use Existing Repository** + - Always use `/tmp` for repository operations with unique dirs per repo + - For each repo, extract `REPO_ORG` and `REPO_NAME` from `github_url`, set `REPO_DIR="/tmp/${REPO_ORG}/${REPO_NAME}"` + - If `$REPO_DIR` exists, `cd` into it; otherwise `mkdir -p "/tmp/${REPO_ORG}"`, `git clone` the URL, `cd` in, and `git checkout` the target branch + - **Configure git credentials** immediately after clone (needed for push): + 1. `gh auth setup-git` (if `gh` is authenticated) + 2. Else set `credential.helper` using `$GITHUB_TOKEN` or `$GH_TOKEN` + 3. Else switch remote to SSH if `~/.ssh/id_rsa` or `id_ed25519` exists + 4. Else warn: no credentials configured, push will fail + - **Multi-repo example**: + ```bash + # Upstream: /tmp/opendatahub-io/models-as-a-service (branch: main) + # Downstream: /tmp/red-hat-data-services/models-as-a-service (branch: rhoai-3.0) + ``` + +4.5. **Load Global Fix Guidance from `.cve-fix/` Folder** + - Runs ONCE after all repos are cloned, BEFORE any fixes. Builds a global knowledge base from `.cve-fix/` folders across all cloned repos. + - Check every cloned repo for `.cve-fix/`, read ALL files (e.g., `examples.md`, `config.json`, `dependencies.md`, `pitfalls.md`), and merge into a single context. + - Extract: dependency co-upgrades, lock file requirements, version upgrade patterns, branch-specific instructions, known working fix versions, testing requirements, common pitfalls. + - Apply this guidance to ALL subsequent steps (5-11). Guidance from any repo applies globally; when conflicts exist, prefer the repo-specific instruction. + - If no `.cve-fix/` folders exist, proceed with default strategy. + +--- +> **Steps 5-11 repeat for EACH repository identified in Step 3.** +> Each repo gets its own clone, branch, verification, fix, test run, and PR. +--- + +5. **Verify CVE Presence in Repository** + - **CRITICAL**: Don't blindly apply fixes to all repositories in the mapping + - **CRITICAL**: Only fix CVEs that exist as UNFIXED vulnerabilities in the current branch + - **CRITICAL**: Use the correct Go/language version for vulnerability scanning (see below) + + **5.1: Version-Matched Vulnerability Scanning** + + - **Problem**: Running vulnerability scanners (e.g., govulncheck) with a local toolchain that is NEWER than the repo's target version produces **false negatives**. govulncheck does symbol-level analysis by compiling against the local stdlib. If the local Go 1.26 stdlib already has security fixes, govulncheck won't detect CVEs that affect Go 1.25 or 1.24. + - **Solution**: Use the `GOTOOLCHAIN` environment variable to force govulncheck to download and use the exact Go toolchain version the repository targets. Go's toolchain management will automatically fetch the correct version. + + **For Go projects (govulncheck with GOTOOLCHAIN — REQUIRED):** + + ```bash + # Extract the Go version from go.mod + GO_VERSION=$(grep '^go ' go.mod | awk '{print $2}') + # If there's a toolchain directive, extract that version too + TOOLCHAIN_VERSION=$(grep '^toolchain ' go.mod | sed 's/toolchain go//') + + # Use the toolchain version if present, otherwise use go directive version + TARGET_GO_VERSION="${TOOLCHAIN_VERSION:-$GO_VERSION}" + + # IMPORTANT: GOTOOLCHAIN requires a full patch version (e.g., go1.25.0, not go1.25) + # If the version has no patch component, append .0 + if [[ "$TARGET_GO_VERSION" =~ ^[0-9]+\.[0-9]+$ ]]; then + TARGET_GO_VERSION="${TARGET_GO_VERSION}.0" + fi + + echo "Repo targets Go version: $TARGET_GO_VERSION" + echo "Local Go: $(go version)" + echo "Running govulncheck with GOTOOLCHAIN=go${TARGET_GO_VERSION}..." + + # Run govulncheck with the matching toolchain + # Go will automatically download the correct toolchain version + GOTOOLCHAIN="go${TARGET_GO_VERSION}" govulncheck -show verbose ./... 2>&1 + ``` + + **Why GOTOOLCHAIN?** + - Forces govulncheck to compile and analyze against the EXACT Go stdlib version the repo targets + - Go automatically downloads the matching toolchain if not already installed locally + - No Docker dependency required — uses Go's built-in toolchain management + - Produces accurate results for stdlib CVEs (crypto/tls, net/url, crypto/x509, etc.) + - **Without this**: A local Go 1.26 would show 0-1 stdlib CVEs. **With this**: the same repo shows 12+ stdlib CVEs correctly + + **Example of the difference:** + ``` + # WITHOUT GOTOOLCHAIN (local Go 1.26.0, repo targets Go 1.25): + # → "Your code is affected by 1 vulnerability" (only module-level, misses all stdlib CVEs) + + # WITH GOTOOLCHAIN=go1.25.0: + # → "Your code is affected by 14 vulnerabilities" (correctly detects all stdlib CVEs) + # → Includes: crypto/tls, crypto/x509, net/url, net/http, encoding/asn1, etc. + ``` + + **For Go projects — build_location support:** + ```bash + # If the mapping specifies a build_location (e.g., "maas-api/"), cd into it first + BUILD_LOCATION="${BUILD_LOCATION:-.}" # Default to root if not specified + cd "$BUILD_LOCATION" + + # Then run govulncheck with matching toolchain + GOTOOLCHAIN="go${TARGET_GO_VERSION}" govulncheck -show verbose ./... + ``` + + **For Node.js projects:** + ```bash + # npm audit uses the lockfile and doesn't depend on local Node version + # GOTOOLCHAIN is not needed for Node.js + npm audit --json 2>/dev/null | grep -i "CVE-YYYY-XXXXX" + ``` + + **For Python projects:** + ```bash + # pip-audit checks against the vulnerability database + pip-audit -r requirements.txt 2>/dev/null | grep -i "CVE-YYYY-XXXXX" + ``` + + **Fallback (if GOTOOLCHAIN download fails):** + ```bash + # If GOTOOLCHAIN fails (e.g., network issues, unavailable version), fall back to local + if ! GOTOOLCHAIN="go${TARGET_GO_VERSION}" govulncheck -show verbose ./... 2>/tmp/govulncheck-err.txt; then + echo "āš ļø WARNING: GOTOOLCHAIN=go${TARGET_GO_VERSION} failed. Falling back to local Go toolchain." + echo "āš ļø Results may have false negatives for stdlib CVEs." + echo "āš ļø Error: $(cat /tmp/govulncheck-err.txt | head -3)" + echo "āš ļø Local Go: $(go version) | Repo target: go${TARGET_GO_VERSION}" + govulncheck -show verbose ./... 2>&1 + fi + ``` + + **5.2: Analyze Scan Results** + + - Check if the target CVE appears in the scan results + - **If CVE has already been fixed (not present in scan results)**: + - **DO NOT create a PR** — the vulnerability is already resolved + - **Print to stdout**: "āœ… CVE-YYYY-XXXXX is already fixed in [repository] ([branch]). No action needed." + - **Document in artifacts**: Create a brief note in `artifacts/cve-fixer/fixes/already-fixed-CVE-YYYY-XXXXX.md` with: + - CVE ID + - Repository and branch checked + - Scan results showing CVE is not present + - Timestamp of verification + - Note that Jira ticket may need manual closure + - **Move to next CVE**: Skip all remaining steps for this CVE and proceed to the next one + - **Note**: The Jira ticket may still be open — this is an issue management task, not a code fix task + - Only proceed with remaining steps for CVEs that are confirmed as current vulnerabilities in the scan + + **5.3: Check for Existing Open PRs** + + - **CRITICAL**: Before creating a new fix, check if someone (or a previous automation run) has already opened a PR that addresses this CVE + - **Why**: Duplicate PRs waste reviewer time and create confusion. If a PR already exists, the work is in progress — skip to the next CVE. + - **This check runs AFTER the vulnerability scan** (Step 5.2), because if the CVE is already fixed in the branch, there's no need to check for PRs at all + + **How to check:** + + ```bash + REPO_FULL="opendatahub-io/models-as-a-service" # org/repo from mapping + CVE_ID="CVE-YYYY-XXXXX" + + # Search open PRs for this specific CVE ID in the title or body + EXISTING_PR=$(gh pr list --repo "$REPO_FULL" --state open --search "$CVE_ID" --json number,title,url,headRefName --jq '.[0]' 2>/dev/null) + + if [ -n "$EXISTING_PR" ] && [ "$EXISTING_PR" != "null" ]; then + PR_NUMBER=$(echo "$EXISTING_PR" | jq -r '.number') + PR_TITLE=$(echo "$EXISTING_PR" | jq -r '.title') + PR_URL=$(echo "$EXISTING_PR" | jq -r '.url') + echo "ā­ļø Skipping $CVE_ID — existing open PR found:" + echo " PR #${PR_NUMBER}: ${PR_TITLE}" + echo " URL: ${PR_URL}" + fi + ``` + + **Decision logic:** + + - **If an open PR is found that matches the CVE ID:** + - **DO NOT create another PR** — the fix is already in progress + - **Print to stdout**: "ā­ļø CVE-YYYY-XXXXX — skipping, open PR already exists: " + - **Document in artifacts**: Create a brief note in `artifacts/cve-fixer/fixes/existing-pr-CVE-YYYY-XXXXX.md` with: + - CVE ID + - Repository and branch + - Existing PR number, title, and URL + - Timestamp of check + - Note that fix is already in progress + - **Move to next CVE**: Skip all remaining steps for this CVE in this repository + - **If NO open PR is found:** + - Proceed with the fix (Step 6 onwards) + + **Search strategy:** + - Search by exact CVE ID first (e.g., `CVE-2025-61726`) — this is the most reliable match + - The `gh pr list --search` command searches PR titles and bodies + - A single PR may address multiple CVEs (e.g., "fix: cve-2025-61726 and cve-2025-68121") — if ANY of the target CVEs appear in an existing PR, consider all CVEs in that PR as already handled + - **IMPORTANT: Only skip for OPEN PRs.** Closed or merged PRs should be ignored — if a previous PR was closed without merging (e.g., it was incorrect or superseded), it is valid to create a new fix PR for the same CVE. The `--state open` flag in the `gh pr list` command ensures only open PRs are checked. + - **Stale remote branches**: A previous automation run may have left a remote branch with the same name (e.g., `fix/cve-YYYY-XXXXX-attempt-1`) from a closed PR. If push fails due to a conflicting remote branch, increment the attempt number (e.g., `attempt-2`) or delete the stale remote branch with `git push origin --delete ` before pushing. + + **Example output when PR exists:** + ``` + ā­ļø Skipping CVE-2025-61726 — existing open PR found: + PR #227: fix: cve-2025-61726 and cve-2025-68121 in go stdlib + URL: https://github.com/red-hat-data-services/models-as-a-service/pull/227 + + ā­ļø Skipping CVE-2025-68121 — existing open PR found: + PR #227: fix: cve-2025-61726 and cve-2025-68121 in go stdlib + URL: https://github.com/red-hat-data-services/models-as-a-service/pull/227 + ``` + +6. **Analyze Breaking Changes & Dependency Compatibility** + - **CRITICAL**: Before applying fixes, analyze dependency compatibility + - Check if security fix version is compatible with current dependencies + - Example: If fixing Starlette CVE, check FastAPI compatibility + - Use AI agent or manual research to identify: + - Dependency version conflicts + - Required co-upgrades (e.g., upgrading both FastAPI and Starlette) + - Breaking API changes in new versions + - Migration guides or changelogs + - Document all breaking changes and compatibility issues found + - Determine the minimal set of changes needed to resolve conflicts + +7. **Create Feature Branches** + - **CRITICAL**: Create a SEPARATE branch for EACH CVE (not a single combined branch) + - Use consistent naming: `fix/cve-YYYY-XXXXX--attempt-1` + - Where: + - `YYYY-XXXXX` is the CVE ID (e.g., `2023-26115`) + - `` is the affected package name (e.g., `word-wrap`, `cross-spawn`) + - Each branch should contain ONLY the fix for its specific CVE + +8. **Apply Remediations** + + **8.1: Node.js Dependency Updates (PREFERRED: npm Overrides Approach)** + + For Node.js projects with transitive dependency vulnerabilities, use npm `overrides` to create minimal, reviewable PRs: + + **Step 8.1.1: Determine latest fixed version** + ```bash + # Get latest version that fixes the CVE + npm view versions --json | jq -r '.[-1]' + + # Or get latest version in a specific major version range + npm view @^4.0.0 version # Latest 4.x + + # Examples: + npm view form-data@^4.0.0 version # Output: 4.0.4 + npm view word-wrap version # Output: 1.2.5 + npm view cross-spawn@^7.0.0 version # Output: 7.0.6 + ``` + + **Step 8.1.2: Add or update override in package.json** + ```bash + # Check if package.json exists (monorepos may have frontend/ or backend/ subdirs) + if [ -f "package.json" ]; then + PACKAGE_JSON="package.json" + elif [ -f "frontend/package.json" ]; then + PACKAGE_JSON="frontend/package.json" + fi + + # Add override using jq (recommended) + jq '.overrides[""] = "^"' "$PACKAGE_JSON" > "${PACKAGE_JSON}.tmp" && mv "${PACKAGE_JSON}.tmp" "$PACKAGE_JSON" + + # Example: Add form-data override to upgrade to 4.0.4 + jq '.overrides["form-data"] = "^4.0.4"' package.json > package.json.tmp && mv package.json.tmp package.json + ``` + + **Step 8.1.3: Install dependencies** + ```bash + # Install dependencies to apply the override + npm install + ``` + + **Step 8.1.4: Verify the fix** + ```bash + # Check that the override was applied + npm list + + # Verify CVE is resolved + npm audit | grep -i "" || echo "CVE resolved" + ``` + + **8.2: Alternative - Direct npm update (Use only if overrides not suitable)** + + Use direct update ONLY when: + - Updating direct dependencies (not transitive) + - npm version < 8.3 (overrides not available) + - Major version upgrade has breaking changes + + ```bash + npm update + ``` + + āš ļø **WARNING**: This may trigger lockfile format upgrades and create large diffs + + **8.3: Python Dependency Updates** + - **Dependency Updates**: Update package versions to patched releases + - Update primary dependency (e.g., `starlette==0.49.1`) + - Update incompatible dependencies (e.g., `fastapi>=0.120.0`) + - Document reason for each change + + **8.4: Go Dependency Updates** + + For Go projects with CVEs in the standard library or dependencies: + + **8.4.1: Go Standard Library CVEs (crypto/tls, net/http, etc.)** + + When fixing CVEs in Go's standard library, update the Go version itself: + + **PREFERRED APPROACH - Direct Version Update**: + ```bash + # Update go.mod to specify the patched Go version directly + # Example: Fixing CVE-2025-68121 (requires Go 1.25.7+) + + # Before: + # go 1.25 + + # After: + # go 1.25.7 + ``` + + **Files to Update**: + 1. **go.mod**: Change `go X.Y` to `go X.Y.Z` (e.g., `go 1.25` → `go 1.25.7`) + 2. **Dockerfile**: Update `ARG GOLANG_VERSION=X.Y` to `ARG GOLANG_VERSION=X.Y.Z` + 3. **Dockerfile.konflux** (if exists): Update `ARG GOLANG_VERSION=X.Y.Z` + + **Example**: + ```bash + # 1. Update go.mod + sed -i 's/^go 1.25$/go 1.25.7/' go.mod + + # 2. Update Dockerfile + sed -i 's/ARG GOLANG_VERSION=1.25$/ARG GOLANG_VERSION=1.25.7/' Dockerfile + + # 3. Update Dockerfile.konflux (if exists) + sed -i 's/ARG GOLANG_VERSION=1.25$/ARG GOLANG_VERSION=1.25.7/' Dockerfile.konflux + ``` + + **Alternative Approach - Toolchain Directive** (NOT RECOMMENDED): + ```go + // Less preferred - more verbose + go 1.25 + toolchain go1.25.7 + ``` + + āš ļø **Use toolchain directive ONLY when**: + - You need to keep language version separate from compiler version + - Project has specific requirements for language version pinning + - Otherwise, use the direct version approach (simpler and clearer) + + **8.4.2: Go Module Dependencies** + + For CVEs in third-party Go modules: + + ```bash + # Update specific dependency to patched version + go get github.com/vulnerable/package@v1.2.3 + + # Update all dependencies + go get -u ./... + + # Tidy dependencies + go mod tidy + ``` + + **8.5: Other Remediation Types** + - **Code Patches**: Apply vendor-provided patches or security fixes + - **Code Refactoring**: Rewrite vulnerable code patterns securely + - **Configuration Changes**: Adjust settings to mitigate vulnerabilities + - **Compensating Controls**: Add input validation, sanitization, or access controls + +9. **Document Changes & Breaking Change Report** + - Create comprehensive fix implementation document + - **Breaking Change Analysis Section**: + - List all dependency version changes + - Document compatibility issues found and resolved + - Explain why each dependency was upgraded + - Note any API breaking changes in upgraded dependencies + - Provide migration guidance if needed + - Record which CVEs each change addresses + - List all Jira issues resolved by this fix + - Document testing performed to validate the fix + - **If using npm overrides approach**: + - Note that package.json overrides section was updated + - Explain that this forces all transitive dependencies to use the fixed version + - Document why this approach was chosen over direct update + - Create detailed commit message with: + - CVE ID and description + - Fix method (npm overrides vs direct update) + - Version upgrade details (e.g., "3.0.1 → 4.0.4 via npm overrides") + - Breaking changes summary + - Jira issue references + - Co-authored-by for AI assistance + +10. **Discover and Run Tests** + - Run existing tests before creating PRs to verify fixes don't break functionality + - Always attempt test discovery and execution. Results are documented in the PR but do not block PR creation. + - If tests can't run (missing tools, version mismatch, etc.), document the actual error message + + **Step 10.1: Discover Test Configuration** + + Look for common test indicators in the repository: + + ```bash + # Check for test scripts in package.json (Node.js) + if [ -f "package.json" ]; then + jq -r '.scripts | keys[] | select(test("test|spec|check"))' package.json + fi + + # Check for Python test configurations + find . -maxdepth 2 -name "pytest.ini" -o -name "tox.ini" -o -name ".pytest.ini" + + # Check for test directories + find . -maxdepth 3 -type d -name "test" -o -name "tests" -o -name "__tests__" -o -name "spec" + + # Check for common test files + find . -name "*test*.py" -o -name "test_*.py" -o -name "*_test.py" | head -n 5 + find . -name "*.test.js" -o -name "*.spec.js" -o -name "*.test.ts" | head -n 5 + + # Check for CI configuration (may indicate test commands) + ls .github/workflows/*.yml .gitlab-ci.yml Makefile 2>/dev/null + ``` + + **Step 10.2: Identify Test Commands** + + Based on project type and configuration, identify test commands: + + **Node.js/JavaScript:** + ```bash + npm test # Most common + npm run test:unit + yarn test + pnpm test + ``` + + **Python:** + ```bash + pytest + pytest tests/ + python -m pytest + python -m unittest discover + tox + make test + ``` + + **Go:** + ```bash + go test ./... + make test + ``` + + **Ruby:** + ```bash + bundle exec rspec + rake test + ``` + + **Rust:** + ```bash + cargo test + ``` + + **Step 10.3: Run Tests (with timeout and failure handling)** + + - Set reasonable timeout (e.g., 10 minutes for unit tests) + - Capture stdout and stderr + - Don't fail the workflow if tests fail - document it instead + + ```bash + # Create test results directory + mkdir -p artifacts/cve-fixer/fixes/test-results + + # Run tests with timeout and capture output + TEST_OUTPUT_FILE="artifacts/cve-fixer/fixes/test-results/test-run-$(date +%Y%m%d-%H%M%S).log" + + # Example: Node.js + timeout 600 npm test > "$TEST_OUTPUT_FILE" 2>&1 + TEST_EXIT_CODE=$? + + # Example: Go + go test ./... > "$TEST_OUTPUT_FILE" 2>&1 + TEST_EXIT_CODE=$? + + # Capture results + if [ $TEST_EXIT_CODE -eq 0 ]; then + echo "āœ… Tests passed" | tee -a "$TEST_OUTPUT_FILE" + TEST_STATUS="PASSED" + elif [ $TEST_EXIT_CODE -eq 124 ]; then + echo "ā±ļø Tests timed out after 10 minutes" | tee -a "$TEST_OUTPUT_FILE" + TEST_STATUS="TIMEOUT" + else + echo "āŒ Tests failed with exit code $TEST_EXIT_CODE" | tee -a "$TEST_OUTPUT_FILE" + # Check if it's a version mismatch + if grep -q "go.mod requires go >=" "$TEST_OUTPUT_FILE"; then + echo "ā„¹ļø Version mismatch detected - this validates the fix" | tee -a "$TEST_OUTPUT_FILE" + TEST_STATUS="VERSION_MISMATCH" + else + TEST_STATUS="FAILED" + fi + fi + ``` + + **Step 10.4: Handle Test Results** + + Document the outcome and proceed to PR creation regardless of result: + + | Result | Action | + |--------|--------| + | **PASSED** | Note success in PR description | + | **FAILED** | Include failure summary in PR with warning | + | **NO_TESTS** | Add "manual testing required" to PR checklist | + | **COULD_NOT_RUN** | Document the error in PR description | + | **VERSION_MISMATCH** | Note this validates the fix -- version enforcement works | + + All outcomes proceed to PR creation. Test results are informational, not blocking. + + **Step 10.5: Generate Test Summary** + + Create a test summary section for the PR: + + ```markdown + ## Test Results + + **Status**: āœ… PASSED | āŒ FAILED | āš ļø NO TESTS FOUND | āš ļø COULD NOT RUN + + **Tests discovered**: Yes/No + **Test command**: `npm test` | `pytest` | etc. + **Exit code**: 0 (success) | non-zero (failure) + **Duration**: 2m 34s + + ### Summary + - Total tests: X + - Passed: Y + - Failed: Z + - Skipped: W + + ### Details + [Brief summary or link to full test output] + + **Note**: Full test output available in CI/CD pipeline after PR creation. + ``` + + **Step 10.6: Document in Fix Implementation Report** + + Add test results to the fix implementation report: + + ```markdown + ## Pre-PR Test Execution + + **Test Discovery**: Tests found in `tests/` directory + **Test Framework**: pytest + **Test Command**: `pytest tests/` + **Execution Time**: 2m 34s + **Result**: PASSED āœ… + + **Test Output** (last 50 lines): + ``` + [test output] + ``` + + **Full test log**: `artifacts/cve-fixer/fixes/test-results/test-run-20260218-143022.log` + ``` + +11. **Create Pull Requests** + - **CRITICAL**: You MUST actually CREATE the PRs using `gh pr create` command + - **CRITICAL**: Create a SEPARATE PR for EACH CVE (NOT combined) + - **CRITICAL**: Only create PRs for CVEs that were ACTUALLY FIXED (not for CVEs that were already fixed in Step 5) + - For each CVE fix that was successfully committed and pushed: + - Generate PR title: `Security: Fix CVE-YYYY-XXXXX ()` + - **Extract Jira issue IDs for this CVE:** + - Read the latest `/cve.find` output from `artifacts/cve-fixer/find/` + - Search for all Jira issues that mention this specific CVE ID in their summary + - Extract the issue IDs (e.g., RHOAIENG-17794, RHOAIENG-16619, etc.) + - Collect all issue IDs for this CVE + - Generate comprehensive PR description with: + - CVE details and severity + - **Test execution results** (from Step 9) + - Breaking change analysis + - Testing recommendations + - Verification steps checklist + - 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://issues.redhat.com/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 + - **CREATE** the PR using GitHub CLI (with fallback to GitHub API): + ```bash + # Prepare PR body + PR_BODY=$(cat <<'EOF' +## Summary + +This PR fixes **CVE-YYYY-XXXXX** by upgrading from X.X.X to Y.Y.Y. + +### CVE Details +- **CVE ID**: CVE-YYYY-XXXXX +- **Package**: +- **Severity**: CRITICAL/HIGH/MEDIUM/LOW (CVSS X.X) +- **Impact**: [Description] +- **Vulnerable versions**: X.X.X - X.X.X +- **Fixed version**: Y.Y.Y +- **Jira Issues**: RHOAIENG-XXXXX, RHOAIENG-YYYYY + +### Test Results + +**Status**: āœ… All tests passed | āŒ Some tests failed | āš ļø No tests found + +**Tests discovered**: Yes/No +**Test command**: \`npm test\` | \`pytest\` | \`N/A\` +**Result**: PASSED/FAILED/NOT_RUN +**Duration**: Xm Ys + +
+Test Summary + +- Total: X tests +- Passed: Y +- Failed: Z +- Skipped: W + +
+ + +āš ļø **Note**: Tests failed during pre-PR validation. Review failures before merging. + + +āš ļø **Note**: No automated tests found. Manual testing required. + +### Breaking Changes +[Breaking change details] + +### Testing Checklist + +- [x] Pre-PR automated tests executed +- [ ] Verify CVE is resolved with security scan +- [ ] Test affected functionality manually +- [ ] Review test failures (if any) + +### Risk Assessment +[Risk assessment table] + +--- + +šŸ¤– Generated by CVE Fixer Workflow +EOF +) + + gh pr create \ + --base \ + --title "Security: Fix CVE-YYYY-XXXXX ()" \ + --body "$PR_BODY" + ``` + - Capture the PR URL from the command output + - Save PR URL to fix implementation report + - Create summary of all PRs created in `artifacts/cve-fixer/fixes/pr-creation-summary.md` + - **VERIFY**: Confirm all PRs were successfully created by listing them (use `gh pr list --repo ` if available, or check via GitHub web interface) + - **CRITICAL - Console Output**: When printing PR URLs to the user (in the final summary or anywhere in stdout), ALWAYS use the full URL (e.g., `https://github.com/org/repo/pull/123`), NOT just the PR number or a markdown link. The user needs to be able to click or copy the full URL directly from the terminal. + +--- +> **END per-repository loop. Return to Step 5 for next repository.** +--- + +## Output +- **Fix Implementation Report**: `artifacts/cve-fixer/fixes/fix-implementation-CVE-YYYY-XXXXX.md` + - Detailed log of all changes made + - Breaking change analysis with dependency compatibility details + - Complete list of files changed + - Pre-PR test execution results + - Testing recommendations + - Risk assessment + - Jira issue references + - PR URL for the created pull request + +- **Already Fixed Report**: `artifacts/cve-fixer/fixes/already-fixed-CVE-YYYY-XXXXX.md` (if CVE was already fixed) + - CVE ID and repository checked + - Scan results showing CVE is not present + - Timestamp of verification + - Note about Jira ticket requiring manual closure + +- **Existing PR Report**: `artifacts/cve-fixer/fixes/existing-pr-CVE-YYYY-XXXXX.md` (if an open PR already exists) + - CVE ID, repository, and branch + - Existing PR number, title, and URL + - Timestamp of check + - Note that fix is already in progress + +- **Test Results**: `artifacts/cve-fixer/fixes/test-results/test-run-TIMESTAMP.log` + - Full test execution output + - Test framework output (pytest, jest, etc.) + - Success/failure details + - Execution duration + +- **PR Summary**: `artifacts/cve-fixer/fixes/pr-creation-summary.md` + - List of all PRs created (one per CVE per repository) + - PR URLs, branch names, and target repositories + - Indication of upstream vs downstream PRs + - Test status for each PR + - Quick reference for tracking fixes across all repositories + - Note of any CVEs skipped because they were already fixed or have existing open PRs + +- **Updated Code**: Changes applied directly to codebase in feature branch + - Modified dependency files (pyproject.toml, package.json, etc.) + - Updated dependency lock files as needed + +- **Created Pull Requests**: One PR per CVE per repository on GitHub + - Separate PR for each CVE fix in each repository + - PRs created on all target repositories (upstream and downstream) + - Each PR contains comprehensive description with test results and testing checklist + +- **Console Summary**: Print a final summary to the user with: + - Full PR URLs (e.g., `https://github.com/org/repo/pull/123`) — NOT shortened markdown links + - Already-fixed CVEs and why they were skipped + - CVEs skipped due to existing open PRs (with the existing PR URL) + - Test results per repository + - The user must be able to copy/click PR URLs directly from the terminal output + +## Usage Examples + +Fix all open CVEs from /cve.find output (default): +``` +/cve.fix +``` + +Fix specific Jira issue: +``` +/cve.fix RHOAIENG-4973 +``` + +Fix multiple specific Jira issues: +``` +/cve.fix RHOAIENG-4973 RHOAIENG-5821 +``` + +Fix with custom message: +``` +/cve.fix Fix open CVEs found in latest scan +``` + +**How it works**: +- If you provide Jira issue IDs (e.g., RHOAIENG-XXXXX), the workflow will fix those specific issues +- If you don't provide Jira IDs, the workflow will: + 1. Read the latest `/cve.find` output + 2. Extract all CVEs with `Status: Open` + 3. Create separate PRs for each open CVE + 4. Verify each CVE actually exists in the repository before fixing + +## Detailed Workflow Example + +### Example: Fixing Go stdlib CVEs for "Model as a Service" + +1. **Load**: Read latest `/cve.find` output -- 2 CVEs (CVE-2025-61729, CVE-2025-68121) +2. **Map**: Look up "Model as a Service" in `component-repository-mappings.json` -- 2 repos (upstream: opendatahub-io/models-as-a-service, downstream: red-hat-data-services/models-as-a-service) +3. **Clone**: Clone both repos to `/tmp/opendatahub-io/...` and `/tmp/red-hat-data-services/...` +4. **Load guidance**: Read `.cve-fix/` from both repos +5. **For each repo**: + - Scan with `GOTOOLCHAIN=go1.25.0 govulncheck ./...` -- confirms CVEs present + - Check for existing PRs with `gh pr list --search "CVE-2025-68121"` + - Fix: update `go 1.25` to `go 1.25.7` in go.mod + Dockerfiles + - Run `go test ./...` + - Commit, push branch `fix/cve-2025-68121-go-stdlib-attempt-1` + - Create PR with `gh pr create` +6. **Summary**: 2 PRs created (one per repo), test results documented + +## Success Criteria + +After running this command, you should have: +- [ ] Component-repository mapping loaded and repositories identified +- [ ] CVE presence verified in each repository (not applied blindly) +- [ ] Already-fixed CVEs identified and documented (no PRs created for these) +- [ ] Existing open PRs checked — CVEs with in-progress PRs skipped (no duplicates created) +- [ ] Repository-specific fix guidance loaded if `.cve-fix/examples.md` exists +- [ ] Feature branches created for each CVE that needs fixing +- [ ] Breaking change analysis completed and documented +- [ ] Dependency compatibility issues identified and resolved +- [ ] Code changes that remediate targeted CVEs +- [ ] Updated dependencies to non-vulnerable versions +- [ ] Tests discovered and executed (if available) +- [ ] Test results documented in fix reports +- [ ] Comprehensive fix implementation report created for each CVE that was fixed +- [ ] Already-fixed reports created for CVEs that were already resolved +- [ ] Commits created with detailed CVE fix messages +- [ ] Changes pushed to feature branches +- [ ] **Pull requests created on GitHub with test results (one PR per CVE that was fixed)** +- [ ] PR URLs saved to fix implementation reports +- [ ] PR summary file generated with all created PRs, skipped CVEs, and existing open PRs + +## Next Steps + +After completing this phase: +1. Run `/cve.verify` to test that fixes work and CVEs are resolved +2. Or review the fix implementation in `artifacts/cve-fixer/fixes/` + +## Notes + +### Component-Repository Mapping File +- **Location**: `component-repository-mappings.json` in workspace root +- **Purpose**: Maps Jira components to GitHub repositories and their branch strategies +- **CRITICAL**: Always verify CVE presence in repositories before applying fixes + - Don't blindly apply fixes to all repositories in a component mapping + - Clone and search each repository for the vulnerable dependency + - 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**: + ```json + { + "components": { + "Component Name": { + "container_to_repo_mapping": { ... }, + "repositories": { + "org/repo-upstream": { + "default_branch": "main", + "active_release_branches": [...], + "cve_fix_workflow": { + "primary_target": "main", + "backport_targets": "..." + }, + "repo_type": "upstream" + }, + "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" + } + } + } + } + } + ``` +- **If Component Not in Mapping**: + - Ask user to provide repository information manually + - Optionally offer to update the mapping file with the new component + +### npm Overrides Approach (Recommended for Node.js) +- **PREFERRED** method for fixing transitive dependency CVEs in Node.js projects +- Uses npm's `overrides` feature (requires npm 8.3+) to force specific versions +- **Benefits**: + - Minimal, reviewable PRs (only package.json + dependency updates) + - Upgrades to latest fixed version (not just patch) + - Forces consistent version across entire dependency tree + - Easier to review than direct dependency updates +- **When to use**: + - āœ… Fixing transitive (indirect) dependencies + - āœ… Want minimal, clean PRs + - āœ… npm version >= 8.3 + - āœ… Latest major version is safe to use +- **When NOT to use**: + - āŒ Updating direct dependencies (use normal package.json update) + - āŒ npm version < 8.3 (use `npm update` instead) + - āŒ Latest major version has breaking changes (use specific version override) +- **Example**: + ```json + // package.json + "overrides": { + "form-data": "^4.0.4", // Forces ALL transitive uses to 4.0.4+ + "word-wrap": "^1.2.5" // Fixes CVE-2023-26115 + } + ``` +### Pre-PR Testing Strategy +- **Automatic test discovery**: Workflow attempts to find and run tests automatically +- **Non-blocking**: Test failures don't prevent PR creation +- **Why run tests before PR?** + - Catch obvious breaking changes early + - Provide immediate feedback to reviewers + - Reduce CI/CD iteration time + - Show test impact directly in PR description + +- **Test discovery order**: + 1. Check package.json scripts (Node.js) + 2. Look for test configuration files (pytest.ini, etc.) + 3. Scan for test directories (tests/, __tests__, spec/) + 4. Check CI/CD configs for test commands + +- **Handling test failures**: + - Document failures clearly in PR + - Still create PR (CVE fix may be correct, tests may have pre-existing issues) + - Add warning in PR description + - Let reviewers decide if failures are related to fix + +- **No tests found**: + - Document this clearly + - Request manual testing in PR + - Not uncommon for some repositories + +- **Test execution limits**: + - 10-minute timeout for test runs + - Only run quick unit/integration tests + - Skip slow E2E or performance tests + - Full test suite runs in CI/CD after PR creation + +### Breaking Change Analysis +- **ALWAYS** analyze dependency compatibility before applying fixes +- Security patches may require co-upgrading dependent packages +- Use AI agents to research compatibility when uncertain +- Document ALL breaking changes, even if minor +- Examples of common breaking changes: + - Starlette 0.49.1 requires FastAPI 0.120+ (incompatible with 0.115.x) + - Django security patches may require database migrations + - Node.js package updates may require TypeScript type updates + +### Dependency Conflicts +- If a security fix creates a dependency conflict, you MUST upgrade other packages +- Example: Upgrading Starlette to fix CVE requires upgrading FastAPI +- Document the dependency chain that required the upgrade +- Explain in the PR why additional packages were upgraded + +### Testing Strategy +- Always test fixes in non-production environment first +- Some fixes may require application code changes beyond dependency updates +- Watch for breaking changes when upgrading major versions +- Consider backward compatibility requirements for APIs +- Keep fixes focused; batch related CVEs but avoid mixing unrelated changes + +### Commit Messages +- Include CVE ID in commit title +- Document breaking changes in commit body +- Reference Jira issues being resolved (plain text IDs, no hyperlinks) + - Example: `Resolves RHOAIENG-17794, RHOAIENG-427` +- Add Co-Authored-By for AI agent assistance diff --git a/workflows/cve-fixer/.claude/settings.json b/workflows/cve-fixer/.claude/settings.json new file mode 100644 index 00000000..b8c99b8f --- /dev/null +++ b/workflows/cve-fixer/.claude/settings.json @@ -0,0 +1,15 @@ +{ + "permissions": { + "allow": [ + "Bash", + "Read", + "Write", + "Edit", + "WebSearch", + "WebFetch" + ], + "deny": [ + "Bash(rm)" + ] + } +} diff --git a/workflows/cve-fixer/.gitignore b/workflows/cve-fixer/.gitignore new file mode 100644 index 00000000..46c76b6d --- /dev/null +++ b/workflows/cve-fixer/.gitignore @@ -0,0 +1,2 @@ +# CVE Fixer Artifacts - generated output, not tracked in repo +artifacts/cve-fixer/ diff --git a/workflows/cve-fixer/FIELD_REFERENCE.md b/workflows/cve-fixer/FIELD_REFERENCE.md new file mode 100644 index 00000000..292e5654 --- /dev/null +++ b/workflows/cve-fixer/FIELD_REFERENCE.md @@ -0,0 +1,244 @@ +# CVE Fixer - Field Reference + +This document provides detailed information about the configuration fields in `.ambient/ambient.json`. + +## Required Fields + +### name +- **Type:** string +- **Purpose:** Display name shown in ACP UI +- **Current Value:** "CVE Fixer" +- **Guidelines:** Keep concise (2-5 words), use title case + +### description +- **Type:** string +- **Purpose:** Explains workflow purpose in UI +- **Current Value:** "This workflow can be used to scan your code base for CVEs and fix discovered CVEs" +- **Guidelines:** 1-3 sentences, clear and specific about workflow capabilities + +### systemPrompt +- **Type:** string +- **Purpose:** Defines AI agent's role and behavior throughout the workflow +- **Current Value:** See `.ambient/ambient.json` +- **Guidelines:** + - Start with clear role definition (e.g., "You are a CVE remediation assistant...") + - List key responsibilities using bullet points + - Document workflow methodology with numbered phases + - Reference all available slash commands with brief descriptions + - Specify exact output locations for artifacts + - Include first-time setup instructions + +### startupPrompt +- **Type:** string +- **Purpose:** Initial message shown when workflow activates +- **Current Value:** See `.ambient/ambient.json` +- **Guidelines:** + - Greet user warmly and introduce the workflow purpose + - List available commands with brief descriptions + - Provide clear call-to-action for getting started + - Keep concise but informative + +## Optional Fields + +### results +- **Type:** object with string values +- **Purpose:** Maps artifact types to file path patterns +- **Current Value:** See `.ambient/ambient.json` +- **Guidelines:** + - Use glob patterns to match multiple files + - Organize by artifact type for easy discovery + - Ensure paths match those referenced in commands and systemPrompt + +**Current Mappings:** +```json +{ + "Scan Results": "artifacts/cve-fixer/scans/**/*.json", + "Analysis Reports": "artifacts/cve-fixer/analysis/**/*.md", + "Priority Matrices": "artifacts/cve-fixer/priorities/**/*.md", + "Fix Implementations": "artifacts/cve-fixer/fixes/**/*", + "Verification Results": "artifacts/cve-fixer/verification/**/*.md", + "Remediation Reports": "artifacts/cve-fixer/reports/**/*.md" +} +``` + +### version +- **Type:** string +- **Example:** "1.0.0" +- **Purpose:** Track workflow configuration version for updates and compatibility + +### author +- **Type:** string or object +- **Example:** `{"name": "Your Name", "email": "security@example.com"}` +- **Purpose:** Identify workflow creator and maintainer + +### tags +- **Type:** array of strings +- **Example:** `["security", "cve", "vulnerability", "remediation"]` +- **Purpose:** Categorize workflow for discovery and organization + +### icon +- **Type:** string (emoji) +- **Example:** "šŸ”’" +- **Purpose:** Visual identifier in workflow selection UI + +## Customization Examples + +### Adding a new output type + +If you want to track additional artifacts (e.g., "Remediation Scripts"): + +```json +"results": { + "Scan Results": "artifacts/cve-fixer/scans/**/*.json", + "Analysis Reports": "artifacts/cve-fixer/analysis/**/*.md", + "Priority Matrices": "artifacts/cve-fixer/priorities/**/*.md", + "Fix Implementations": "artifacts/cve-fixer/fixes/**/*", + "Verification Results": "artifacts/cve-fixer/verification/**/*.md", + "Remediation Reports": "artifacts/cve-fixer/reports/**/*.md", + "Remediation Scripts": "artifacts/cve-fixer/scripts/**/*.sh" +} +``` + +### Changing artifact location + +To use a different base directory (e.g., `security-artifacts/`): + +1. Update `systemPrompt` OUTPUT LOCATIONS section: +``` +OUTPUT LOCATIONS: +- Create all scan results in: security-artifacts/cve-fixer/scans/ +... +``` + +2. Update `results` paths: +```json +"results": { + "Scan Results": "security-artifacts/cve-fixer/scans/**/*.json", + ... +} +``` + +3. Update all command files in `.claude/commands/` to reference new paths in their ## Output sections + +### Adding environment configuration + +```json +"environment": { + "ARTIFACTS_DIR": "artifacts/cve-fixer", + "SCAN_TOOLS": "snyk,npm-audit,trivy", + "LOG_LEVEL": "info" +} +``` + +### Customizing for specific compliance frameworks + +Modify `systemPrompt` to emphasize compliance: + +``` +KEY RESPONSIBILITIES: +- Guide users through the CVE remediation workflow +- Execute slash commands to perform specific security tasks +- Ensure remediations meet SOC2 and PCI-DSS requirements +- Generate audit-ready compliance documentation +... +``` + +## Agent Files + +Agent persona files are located in `.claude/agents/` and follow this structure: + +```markdown +# {Name} - {Role} +## Role +## Expertise +## Responsibilities +## Communication Style +## When to Invoke +## Tools and Techniques +## Key Principles +## Example Artifacts +``` + +**Current Agent:** +- `vera-security-engineer.md` - Security engineering specialist for CVE remediation + +## Command Files + +Slash command files are located in `.claude/commands/` and follow this structure: + +```markdown +# /{command-name} - {Description} +## Purpose +## Prerequisites +## Process +## Output +## Usage Examples +## Success Criteria +## Next Steps +## Notes +``` + +**Current Commands:** +- `cve.find.md` - Find CVEs reported in Jira for a component +- `cve.fix.md` - Implement CVE fixes and create pull requests + +## File Naming Conventions + +- **Workflow directory:** `workflows/cve-fixer/` +- **Agent files:** `{name}-{role}.md` (e.g., `vera-security-engineer.md`) +- **Command files:** `{workflow-prefix}.{phase}.md` (e.g., `cve.scan.md`) +- **Artifacts:** `artifacts/cve-fixer/{category}/{files}` + +## Validation Checklist + +Before using this workflow, verify: + +- [ ] `.ambient/ambient.json` is valid JSON (no comments, no trailing commas) +- [ ] All required fields (name, description, systemPrompt, startupPrompt) are present +- [ ] All agent files follow the template structure with required sections +- [ ] All command files have unique names and follow naming convention +- [ ] Output paths in `results` match those referenced in `systemPrompt` +- [ ] Output paths in `results` match those in command files' ## Output sections +- [ ] README.md accurately describes the workflow and all phases +- [ ] All file references use correct absolute or relative paths + +## Configuration Best Practices + +1. **Keep prompts focused:** systemPrompt should be comprehensive but not overwhelming. Focus on workflow-specific guidance. + +2. **Be specific about outputs:** Always specify exact artifact paths so users know where to find results. + +3. **Maintain consistency:** Ensure artifact paths are identical across ambient.json, systemPrompt, and command files. + +4. **Version your workflow:** Update version field when making significant changes to track evolution. + +5. **Document customizations:** If you modify the workflow, update this FIELD_REFERENCE.md to reflect changes. + +## Troubleshooting Configuration Issues + +**Problem:** Workflow doesn't load in ACP +**Solution:** Validate JSON syntax in `.ambient/ambient.json`. Remove any comments or trailing commas. + +**Problem:** Artifacts aren't being found +**Solution:** Verify paths in `results` match actual output locations. Use glob patterns correctly (`**/*.md` for recursive). + +**Problem:** Commands don't appear +**Solution:** Ensure command files are in `.claude/commands/` and follow naming convention `{prefix}.{phase}.md`. + +**Problem:** Agent isn't being invoked +**Solution:** Check that agent file exists in `.claude/agents/` and is referenced in workflow context. + +## References + +- [ACP Documentation](https://ambient-code.github.io/vteam) +- [Template Workflow](https://github.com/ambient-code/workflows/tree/main/workflows/template-workflow) +- [Workflow Best Practices](https://ambient-code.github.io/vteam/guides/workflows) +- [JSON Schema Validation](https://jsonlint.com/) + +## Support + +For configuration questions or issues: +1. Validate JSON syntax using a JSON linter +2. Review this field reference for proper field usage +3. Check ACP documentation for workflow requirements +4. Open an issue in the repository if problems persist diff --git a/workflows/cve-fixer/README.md b/workflows/cve-fixer/README.md new file mode 100644 index 00000000..b1cc2cd6 --- /dev/null +++ b/workflows/cve-fixer/README.md @@ -0,0 +1,220 @@ +# CVE Fixer + +A comprehensive workflow for scanning codebases for Common Vulnerabilities and Exposures (CVEs), analyzing their impact, and systematically fixing them with verified remediations. + +## Overview + +This workflow guides you through CVE remediation using a structured approach that ensures thorough vulnerability management: + +### 1. Find (Optional) +Discover CVEs already reported in your Jira issue tracking system for a specific component, helping you track existing vulnerability reports. + +### 2. Scan +Discover CVEs in your codebase and dependencies using multiple security scanning tools, creating a complete vulnerability inventory. + +### 3. Analyze +Deep-dive into each discovered CVE to understand its technical details, real-world exploitability, and contextual impact on your specific environment. + +### 4. Prioritize +Rank CVEs by risk level considering severity, exploitability, and business impact, then create a phased remediation roadmap. + +### 5. Fix +Implement secure remediations through dependency updates, code patches, or compensating controls following the prioritized plan. + +### 6. Verify +Validate that fixes successfully resolve CVEs without introducing regressions through re-scanning and comprehensive testing. + +### 7. Report +Generate audit-ready documentation showing the complete remediation process, security improvements, and compliance evidence. + +## Getting Started + +### Prerequisites +- Project with dependency manifest files (package.json, requirements.txt, pom.xml, etc.) +- Security scanning tools (npm audit, pip-audit, Snyk, or similar) +- Test suite for regression testing +- Git for version control +- (Optional) Jira MCP server or JIRA_API_TOKEN for finding CVEs in Jira + +### Installation +1. Clone this workflow repository +2. Load the workflow in your ACP session +3. Run `/cve.scan` to initialize and discover vulnerabilities + +## Workflow Phases + +### Phase 1: Find (Optional) +**Command:** `/cve.find` + +Find and catalog CVEs that have been reported in Jira for a specific component. This optional phase helps you discover existing vulnerability reports in your issue tracking system before or alongside code scanning. + +**Prerequisites:** +- Jira MCP server (preferred) OR JIRA_API_TOKEN environment variable +- Access to Jira instance (https://issues.redhat.com) + +**Output:** +- `artifacts/cve-fixer/find/cve-issues-[timestamp].md` - List of Jira CVE issues for the component + +### Phase 2: Scan +**Command:** `/cve.scan` + +Systematically scan your codebase and dependencies for known CVEs using multiple security tools. This phase creates a comprehensive inventory of all vulnerabilities present in your project. + +**Output:** +- `artifacts/cve-fixer/scans/scan-results-[timestamp].json` - Structured vulnerability data +- `artifacts/cve-fixer/scans/scan-summary-[timestamp].md` - Human-readable scan report + +### Phase 3: Analyze +**Command:** `/cve.analyze` + +Perform deep analysis of discovered CVEs by researching their technical details, reviewing exploit availability, and assessing their real-world impact on your specific deployment. + +**Output:** +- `artifacts/cve-fixer/analysis/cve-analysis-[timestamp].md` - Detailed CVE analysis +- `artifacts/cve-fixer/analysis/exploitability-matrix-[timestamp].md` - Exploitability assessment + +### Phase 4: Prioritize +**Command:** `/cve.prioritize` + +Rank CVEs by combining severity scores with exploitability factors and business context. Create a remediation roadmap that sequences fixes based on risk and effort. + +**Output:** +- `artifacts/cve-fixer/priorities/priority-matrix-[timestamp].md` - Ranked CVE list +- `artifacts/cve-fixer/priorities/remediation-roadmap-[timestamp].md` - Phased fix plan + +### Phase 5: Fix +**Command:** `/cve.fix` + +Implement remediations for prioritized CVEs through dependency updates, code patches, configuration changes, or compensating security controls. + +**Output:** +- `artifacts/cve-fixer/fixes/fix-implementation-[timestamp].md` - Detailed change log +- `artifacts/cve-fixer/fixes/fix-summary-[timestamp].md` - Executive summary of fixes + +### Phase 6: Verify +**Command:** `/cve.verify` + +Validate that implemented fixes successfully resolve the targeted CVEs by re-scanning and running comprehensive regression tests. + +**Output:** +- `artifacts/cve-fixer/verification/verification-report-[timestamp].md` - Verification results +- `artifacts/cve-fixer/verification/scan-comparison-[timestamp].md` - Before/after comparison + +### Phase 7: Report +**Command:** `/cve.report` + +Generate comprehensive documentation of the entire remediation process for stakeholders, auditors, and compliance purposes. + +**Output:** +- `artifacts/cve-fixer/reports/executive-summary-[timestamp].md` - High-level summary +- `artifacts/cve-fixer/reports/technical-report-[timestamp].md` - Technical details +- `artifacts/cve-fixer/reports/compliance-report-[timestamp].md` - Audit documentation + +## Available Agents + +This workflow includes the following expert agent: + +### Vera - Security Engineer +Security engineering specialist focused on vulnerability assessment and CVE remediation. + +**Expertise:** CVE analysis, security scanning tools, secure coding practices, dependency management, exploit assessment + +## Output Artifacts + +All workflow outputs are saved in the `artifacts/cve-fixer/` directory: + +``` +artifacts/cve-fixer/ +ā”œā”€ā”€ find/ # Jira CVE issues found for components (optional) +ā”œā”€ā”€ scans/ # Vulnerability scan results and summaries +ā”œā”€ā”€ analysis/ # Detailed CVE analysis and exploitability assessments +ā”œā”€ā”€ priorities/ # Priority matrices and remediation roadmaps +ā”œā”€ā”€ fixes/ # Fix implementations and change logs +ā”œā”€ā”€ verification/ # Verification results and scan comparisons +└── reports/ # Final remediation reports and compliance docs +``` + +## Example Usage + +```bash +# Step 1 (Optional): Find CVEs already reported in Jira +/cve.find + +# Step 2: Scan codebase for CVEs +/cve.scan + +# Step 3: Analyze discovered vulnerabilities +/cve.analyze + +# Step 4: Prioritize CVEs and create roadmap +/cve.prioritize + +# Step 5: Implement fixes for high-priority CVEs +/cve.fix + +# Step 6: Verify fixes resolve CVEs +/cve.verify + +# Step 7: Generate final reports +/cve.report +``` + +## Configuration + +This workflow is configured via `.ambient/ambient.json`. Key settings: + +- **Name:** CVE Fixer +- **Description:** Scan and fix CVEs in your codebase +- **Artifact Path:** `artifacts/cve-fixer/` + +## Customization + +You can customize this workflow by: + +1. **Modifying the agent:** Edit `vera-security-engineer.md` to adjust expertise or communication style +2. **Adding commands:** Create new command files in `.claude/commands/` for additional workflow steps +3. **Adjusting configuration:** Update `.ambient/ambient.json` to change prompts or output locations +4. **Changing output paths:** Modify the `results` section in config to organize artifacts differently + +## Best Practices + +1. **Scan regularly** - Run `/cve.scan` periodically as new CVEs are disclosed daily +2. **Prioritize ruthlessly** - Focus on CVEs with public exploits and high CVSS scores first +3. **Test thoroughly** - Always verify fixes don't break functionality before deploying +4. **Document everything** - Maintain audit trails for compliance and knowledge sharing +5. **Automate where possible** - Integrate scanning into CI/CD pipelines for continuous monitoring + +## Troubleshooting + +**Problem:** Scan finds too many CVEs to address +**Solution:** Use `/cve.prioritize` to focus on critical and high-severity issues first. Address lower-priority CVEs in future sprints. + +**Problem:** Dependency update breaks application +**Solution:** Review breaking changes in release notes. May need to update application code to accommodate new API. Consider using minor version updates instead of major versions. + +**Problem:** CVE has no available fix +**Solution:** Document the vulnerability and implement compensating controls (input validation, access restrictions). Monitor vendor advisories for future patches. + +## Contributing + +To improve this workflow: +1. Fork the repository +2. Make your changes +3. Test thoroughly +4. Submit a pull request + +## License + +MIT + +## Support + +For issues or questions: +- Open an issue in the repository +- Refer to the ACP documentation for general workflow guidance + +--- + +**Created with:** ACP Workflow Creator +**Workflow Type:** Custom +**Version:** 1.0.0 diff --git a/workflows/cve-fixer/component-repository-mappings.json b/workflows/cve-fixer/component-repository-mappings.json new file mode 100644 index 00000000..f32cf458 --- /dev/null +++ b/workflows/cve-fixer/component-repository-mappings.json @@ -0,0 +1,386 @@ +{ + "components": { + "AI Core Dashboard": { + "container_to_repo_mapping": { + "odh-dashboard-container": "opendatahub-io/odh-dashboard", + "rhoai/odh-dashboard-rhel8": "opendatahub-io/odh-dashboard", + "rhoai/odh-dashboard-rhel9": "opendatahub-io/odh-dashboard", + "rhoai/odh-mod-arch-gen-ai-rhel9": "opendatahub-io/odh-dashboard", + "rhoai/odh-mod-arch-model-registry-rhel9": "opendatahub-io/odh-dashboard", + "mod-arch-maas": "opendatahub-io/odh-dashboard" + }, + "repositories": { + "opendatahub-io/odh-dashboard": { + "github_url": "https://github.com/opendatahub-io/odh-dashboard", + "default_branch": "main", + "protected_branches": [ + "main", + "rhoai-release", + "odh-release" + ], + "active_release_branches": [ + "v2.29.0-fixes", + "v2.28.0-fixes", + "v2.27.0-fixes" + ], + "branch_strategy": "Fix in main → auto-propagates to stable → rhoai (every 2 hours). Manual cherry-pick to release branches during code freeze.", + "cve_fix_workflow": { + "primary_target": "main", + "backport_targets": "Active vX.X.X-fixes branches for released versions", + "automation": "Auto-sync every 2 hours (main → stable → rhoai)", + "manual_intervention": "Cherry-pick during code freeze or for patch releases" + }, + "repository_type": "monorepo", + "monorepo_packages": { + "packages/gen-ai": "Builds odh-mod-arch-gen-ai container", + "packages/model-registry": "Builds odh-mod-arch-modular-architecture container", + "packages/maas": "Builds mod-arch-maas container", + "packages/kserve": "KServe UI module", + "packages/model-serving": "Model serving UI module" + } + } + } + }, + "Model as a Service": { + "container_to_repo_mapping": { + "rhoai/odh-maas-api-rhel9": "angaduom/models-as-a-service" + }, + "repositories": { + "angaduom/models-as-a-service": { + "github_url": "https://github.com/angaduom/models-as-a-service", + "default_branch": "main", + "protected_branches": [], + "active_release_branches": [], + "branch_strategy": "Fork of opendatahub-io/models-as-a-service for testing CVE fix workflow.", + "cve_fix_workflow": { + "primary_target": "main", + "backport_targets": "TBD", + "automation": "Unknown", + "manual_intervention": "Unknown" + }, + "build_location": "maas-api/", + "notes": "Fork of upstream repo. Contains maas-api Go application. Builds using Dockerfile.konflux for Red Hat builds.", + "repo_type": "upstream" + }, + "angaduom/models-as-a-service-downstream": { + "github_url": "https://github.com/angaduom/models-as-a-service-downstream", + "default_branch": "rhoai-3.0", + "protected_branches": [], + "active_release_branches": [ + "rhoai-3.0" + ], + "branch_strategy": "Mirror of red-hat-data-services/models-as-a-service for testing CVE fix workflow.", + "cve_fix_workflow": { + "primary_target": "rhoai-3.0", + "backport_targets": "rhoai-3.0", + "automation": "Manual backport from upstream", + "manual_intervention": "Cherry-pick or re-apply fixes from upstream repo" + }, + "build_location": "maas-api/", + "notes": "Mirror of downstream Red Hat release repo for maas-api. Fixes from upstream should be backported to rhoai-3.0 branch.", + "repo_type": "downstream" + } + } + }, + "Model Serving": { + "container_to_repo_mapping": { + "odh-modelmesh-runtime-adapter": "opendatahub-io/modelmesh-runtime-adapter", + "rhoai/odh-modelmesh-runtime-adapter-rhel8": "opendatahub-io/modelmesh-runtime-adapter", + "rhoai/odh-modelmesh-runtime-adapter-rhel9": "opendatahub-io/modelmesh-runtime-adapter", + "odh-model-controller": "opendatahub-io/odh-model-controller", + "odh-mm-rest-proxy": "opendatahub-io/odh-model-controller", + "rhoai/odh-model-controller-rhel8": "opendatahub-io/odh-model-controller", + "rhoai/odh-model-controller-rhel9": "opendatahub-io/odh-model-controller", + "rhoai/odh-kserve-controller-rhel9": "opendatahub-io/kserve", + "rhoai/odh-kserve-storage-initializer-rhel9": "opendatahub-io/kserve", + "rhoai/odh-kserve-agent-rhel9": "opendatahub-io/kserve-agent", + "rhoai/odh-kserve-router-rhel9": "opendatahub-io/kserve-router", + "rhoai/odh-llm-d-inference-scheduler-rhel9": "opendatahub-io/llm-d-inference-scheduler", + "rhoai/odh-modelmesh-serving-controller-rhel8": "opendatahub-io/modelmesh" + }, + "repositories": { + "opendatahub-io/modelmesh-runtime-adapter": { + "github_url": "https://github.com/opendatahub-io/modelmesh-runtime-adapter", + "default_branch": "main", + "protected_branches": [], + "active_release_branches": [], + "branch_strategy": "TBD - needs investigation", + "cve_fix_workflow": { + "primary_target": "main", + "backport_targets": "TBD", + "automation": "Unknown", + "manual_intervention": "Unknown" + } + }, + "opendatahub-io/odh-model-controller": { + "github_url": "https://github.com/opendatahub-io/odh-model-controller", + "default_branch": "main", + "protected_branches": [], + "active_release_branches": [], + "branch_strategy": "TBD - needs investigation", + "cve_fix_workflow": { + "primary_target": "main", + "backport_targets": "TBD", + "automation": "Unknown", + "manual_intervention": "Unknown" + } + }, + "opendatahub-io/kserve": { + "default_branch": "main", + "protected_branches": [], + "active_release_branches": [], + "branch_strategy": "TBD - needs investigation", + "cve_fix_workflow": { + "primary_target": "main", + "backport_targets": "TBD", + "automation": "Unknown", + "manual_intervention": "Unknown" + }, + "github_url": "https://github.com/opendatahub-io/kserve" + }, + "opendatahub-io/kserve-agent": { + "default_branch": "main", + "protected_branches": [], + "active_release_branches": [], + "branch_strategy": "TBD - needs investigation", + "cve_fix_workflow": { + "primary_target": "main", + "backport_targets": "TBD", + "automation": "Unknown", + "manual_intervention": "Unknown" + }, + "github_url": "https://github.com/opendatahub-io/kserve-agent" + }, + "opendatahub-io/kserve-router": { + "default_branch": "main", + "protected_branches": [], + "active_release_branches": [], + "branch_strategy": "TBD - needs investigation", + "cve_fix_workflow": { + "primary_target": "main", + "backport_targets": "TBD", + "automation": "Unknown", + "manual_intervention": "Unknown" + }, + "github_url": "https://github.com/opendatahub-io/kserve-router" + }, + "opendatahub-io/llm-d-inference-scheduler": { + "default_branch": "main", + "protected_branches": [], + "active_release_branches": [], + "branch_strategy": "TBD - needs investigation", + "cve_fix_workflow": { + "primary_target": "main", + "backport_targets": "TBD", + "automation": "Unknown", + "manual_intervention": "Unknown" + }, + "github_url": "https://github.com/opendatahub-io/llm-d-inference-scheduler" + }, + "opendatahub-io/modelmesh": { + "default_branch": "main", + "protected_branches": [], + "active_release_branches": [], + "branch_strategy": "TBD - needs investigation", + "cve_fix_workflow": { + "primary_target": "main", + "backport_targets": "TBD", + "automation": "Unknown", + "manual_intervention": "Unknown" + }, + "github_url": "https://github.com/opendatahub-io/modelmesh" + } + } + }, + "Notebooks Images": { + "container_to_repo_mapping": { + "rhoai/odh-pipeline-runtime-tensorflow-cuda-py312-rhel9": "opendatahub-io/workbench-images", + "rhoai/odh-pipeline-runtime-tensorflow-rocm-py312-rhel9": "opendatahub-io/workbench-images", + "rhoai/odh-workbench-jupyter-tensorflow-cuda-py312-rhel9": "opendatahub-io/workbench-images", + "rhoai/odh-workbench-jupyter-tensorflow-rocm-py312-rhel9": "opendatahub-io/workbench-images", + "rhoai/odh-workbench-jupyter-pytorch-cuda-py312-rhel9": "opendatahub-io/workbench-images", + "rhoai/odh-pipeline-runtime-pytorch-cuda-py312-rhel9": "opendatahub-io/workbench-images", + "rhoai/odh-workbench-jupyter-pytorch-rocm-py312-rhel9": "opendatahub-io/workbench-images", + "rhoai/odh-pipeline-runtime-pytorch-rocm-py312-rhel9": "opendatahub-io/workbench-images", + "rhoai/odh-workbench-codeserver-py312-rhel9": "opendatahub-io/workbench-images", + "rhoai/odh-workbench-jupyter-datascience-py312-rhel9": "opendatahub-io/workbench-images", + "rhoai/odh-pipeline-runtime-datascience-py312-rhel9": "opendatahub-io/workbench-images", + "rhoai/odh-workbench-jupyter-minimal-py312-rhel9": "opendatahub-io/workbench-images", + "rhoai/odh-workbench-jupyter-trustyai-py312-rhel9": "opendatahub-io/workbench-images", + "rhoai/odh-pipeline-runtime-minimal-py312-rhel9": "opendatahub-io/workbench-images" + }, + "repositories": { + "opendatahub-io/workbench-images": { + "default_branch": "main", + "protected_branches": [], + "active_release_branches": [], + "branch_strategy": "TBD - needs investigation", + "cve_fix_workflow": { + "primary_target": "main", + "backport_targets": "TBD", + "automation": "Unknown", + "manual_intervention": "Unknown" + }, + "github_url": "https://github.com/opendatahub-io/workbench-images" + } + } + }, + "AI Pipelines": { + "container_to_repo_mapping": { + "odh-ml-pipelines-driver-container": "opendatahub-io/data-science-pipelines", + "odh-ml-pipelines-api-server-v2-container": "opendatahub-io/data-science-pipelines", + "odh-ml-pipelines-launcher-container": "opendatahub-io/data-science-pipelines", + "odh-ml-pipelines-persistenceagent-container": "opendatahub-io/data-science-pipelines", + "odh-ml-pipelines-scheduledworkflow-container": "opendatahub-io/data-science-pipelines", + "odh-ml-pipelines-cache-container": "opendatahub-io/data-science-pipelines", + "odh-ml-pipelines-api-server-container": "opendatahub-io/data-science-pipelines", + "odh-data-science-pipelines-runtime-container": "opendatahub-io/data-science-pipelines", + "odh-data-science-pipelines-runtime-generic-container": "opendatahub-io/data-science-pipelines", + "odh-ml-pipelines-viewercontroller-argoworkflow-container": "opendatahub-io/data-science-pipelines", + "rhoai/odh-data-science-pipelines-operator-controller-rhel8": "opendatahub-io/data-science-pipelines-operator", + "odh-data-science-pipelines-argo-argoexec-container": "argoproj/argo-workflows", + "odh-data-science-pipelines-argo-workflowcontroller-container": "argoproj/argo-workflows" + }, + "repositories": { + "opendatahub-io/data-science-pipelines": { + "default_branch": "main", + "protected_branches": [], + "active_release_branches": [], + "branch_strategy": "TBD - needs investigation", + "cve_fix_workflow": { + "primary_target": "main", + "backport_targets": "TBD", + "automation": "Unknown", + "manual_intervention": "Unknown" + }, + "github_url": "https://github.com/opendatahub-io/data-science-pipelines" + }, + "opendatahub-io/data-science-pipelines-operator": { + "default_branch": "main", + "protected_branches": [], + "active_release_branches": [], + "branch_strategy": "TBD - needs investigation", + "cve_fix_workflow": { + "primary_target": "main", + "backport_targets": "TBD", + "automation": "Unknown", + "manual_intervention": "Unknown" + }, + "github_url": "https://github.com/opendatahub-io/data-science-pipelines-operator" + }, + "argoproj/argo-workflows": { + "default_branch": "main", + "protected_branches": [], + "active_release_branches": [], + "branch_strategy": "External dependency - not managed by OpenDataHub", + "cve_fix_workflow": { + "primary_target": "N/A - upstream project", + "backport_targets": "N/A", + "automation": "N/A", + "manual_intervention": "Monitor upstream releases and update dependency version" + }, + "notes": "Third-party dependency managed by Argo project", + "github_url": "https://github.com/argoproj/argo-workflows" + } + } + }, + "Notebooks Server": { + "container_to_repo_mapping": { + "rhoai/odh-notebook-controller-rhel8": "opendatahub-io/kubeflow", + "rhoai/odh-kf-notebook-controller-rhel8": "opendatahub-io/kubeflow", + "rhoai/odh-kf-notebook-controller-rhel9": "opendatahub-io/kubeflow", + "rhoai/odh-notebook-controller-rhel9": "opendatahub-io/kubeflow" + }, + "repositories": { + "opendatahub-io/kubeflow": { + "default_branch": "main", + "protected_branches": [], + "active_release_branches": [], + "branch_strategy": "TBD - needs investigation", + "cve_fix_workflow": { + "primary_target": "main", + "backport_targets": "TBD", + "automation": "Unknown", + "manual_intervention": "Unknown" + }, + "github_url": "https://github.com/opendatahub-io/kubeflow" + } + } + }, + "Training Kubeflow": { + "container_to_repo_mapping": { + "rhoai/odh-training-operator-rhel8": "opendatahub-io/training-operator", + "rhoai/odh-training-operator-rhel9": "opendatahub-io/training-operator", + "rhoai/odh-notebook-controller-rhel8": "opendatahub-io/notebooks", + "rhoai/odh-kf-notebook-controller-rhel8": "opendatahub-io/notebooks", + "rhoai/odh-notebook-controller-rhel9": "opendatahub-io/notebooks", + "rhoai/odh-kf-notebook-controller-rhel9": "opendatahub-io/notebooks", + "rhoai/odh-kuberay-operator-controller-rhel9": "opendatahub-io/kuberay-operator-controller", + "rhoai/odh-codeflare-operator-rhel8": "opendatahub-io/codeflare-operator", + "rhoai/odh-codeflare-operator-rhel9": "opendatahub-io/codeflare-operator" + }, + "repositories": { + "opendatahub-io/training-operator": { + "default_branch": "main", + "protected_branches": [], + "active_release_branches": [], + "branch_strategy": "TBD - needs investigation", + "cve_fix_workflow": { + "primary_target": "main", + "backport_targets": "TBD", + "automation": "Unknown", + "manual_intervention": "Unknown" + }, + "github_url": "https://github.com/opendatahub-io/training-operator" + }, + "opendatahub-io/notebooks": { + "default_branch": "main", + "protected_branches": [], + "active_release_branches": [], + "branch_strategy": "TBD - needs investigation", + "cve_fix_workflow": { + "primary_target": "main", + "backport_targets": "TBD", + "automation": "Unknown", + "manual_intervention": "Unknown" + }, + "github_url": "https://github.com/opendatahub-io/notebooks" + }, + "opendatahub-io/kuberay-operator-controller": { + "default_branch": "main", + "protected_branches": [], + "active_release_branches": [], + "branch_strategy": "TBD - needs investigation", + "cve_fix_workflow": { + "primary_target": "main", + "backport_targets": "TBD", + "automation": "Unknown", + "manual_intervention": "Unknown" + }, + "github_url": "https://github.com/opendatahub-io/kuberay-operator-controller" + }, + "opendatahub-io/codeflare-operator": { + "default_branch": "main", + "protected_branches": [], + "active_release_branches": [], + "branch_strategy": "TBD - needs investigation", + "cve_fix_workflow": { + "primary_target": "main", + "backport_targets": "TBD", + "automation": "Unknown", + "manual_intervention": "Unknown" + }, + "github_url": "https://github.com/opendatahub-io/codeflare-operator" + } + } + } + }, + "metadata": { + "description": "Component to repository and branch mappings for CVE fix workflow automation", + "purpose": "Maps RHOAI Jira components to GitHub repositories and their branch strategies for automated CVE patching", + "last_updated": "2026-03-16", + "components_analyzed": 7, + "components_with_branch_info": 1, + "components_pending_branch_analysis": 6 + } +} diff --git a/workflows/cve-fixer/create_presentation.py b/workflows/cve-fixer/create_presentation.py new file mode 100644 index 00000000..087524fd --- /dev/null +++ b/workflows/cve-fixer/create_presentation.py @@ -0,0 +1,356 @@ +#!/usr/bin/env python3 +""" +CVE Workflow Presentation Generator +Creates a 3-slide PowerPoint presentation for automated CVE remediation workflow +""" + +from pptx import Presentation +from pptx.util import Inches, Pt +from pptx.enum.text import PP_ALIGN, MSO_ANCHOR +from pptx.dml.color import RGBColor +from pptx.enum.shapes import MSO_SHAPE + +# Create presentation +prs = Presentation() +prs.slide_width = Inches(10) +prs.slide_height = Inches(7.5) + +# Define color scheme +COLOR_BLUE = RGBColor(41, 128, 185) # Jira/discovery phase +COLOR_GREEN = RGBColor(39, 174, 96) # Configuration/mapping +COLOR_ORANGE = RGBColor(230, 126, 34) # Fix/remediation phase +COLOR_PURPLE = RGBColor(142, 68, 173) # Repository/GitHub +COLOR_DARK_GRAY = RGBColor(52, 73, 94) # Text +COLOR_LIGHT_GRAY = RGBColor(189, 195, 199) # Borders + +# ============================================================================ +# SLIDE 1: GOAL OF THE PROJECT +# ============================================================================ +slide1 = prs.slides.add_slide(prs.slide_layouts[6]) # Blank layout + +# Title +title_box = slide1.shapes.add_textbox(Inches(0.5), Inches(0.5), Inches(9), Inches(1)) +title_frame = title_box.text_frame +title_frame.text = "Automated CVE Remediation for RHOAI" +title_para = title_frame.paragraphs[0] +title_para.font.size = Pt(40) +title_para.font.bold = True +title_para.font.color.rgb = COLOR_DARK_GRAY +title_para.alignment = PP_ALIGN.CENTER + +# Content box +content_box = slide1.shapes.add_textbox(Inches(1), Inches(2.5), Inches(8), Inches(3)) +content_frame = content_box.text_frame +content_frame.word_wrap = True + +p = content_frame.paragraphs[0] +p.text = "Automate CVE discovery and remediation across OpenShift AI components. Bridge Jira issue tracking with GitHub fixes, reducing manual security work while ensuring consistent, well-documented patches across repositories." +p.font.size = Pt(24) +p.font.color.rgb = COLOR_DARK_GRAY +p.alignment = PP_ALIGN.CENTER +p.line_spacing = 1.5 + +# ============================================================================ +# SLIDE 2: ARCHITECTURE DIAGRAM +# ============================================================================ +slide2 = prs.slides.add_slide(prs.slide_layouts[6]) # Blank layout + +# Title +title2_box = slide2.shapes.add_textbox(Inches(0.5), Inches(0.3), Inches(9), Inches(0.6)) +title2_frame = title2_box.text_frame +title2_frame.text = "CVE Workflow Architecture" +title2_para = title2_frame.paragraphs[0] +title2_para.font.size = Pt(32) +title2_para.font.bold = True +title2_para.font.color.rgb = COLOR_DARK_GRAY +title2_para.alignment = PP_ALIGN.CENTER + +# Main flow boxes - centered +main_y = Inches(1.2) +box_width = Inches(2) +box_height = Inches(1.1) + +# Box 1: Jira CVE Issues (BLUE) +box1 = slide2.shapes.add_shape( + MSO_SHAPE.ROUNDED_RECTANGLE, + Inches(0.4), main_y, box_width, box_height +) +box1.fill.solid() +box1.fill.fore_color.rgb = COLOR_BLUE +box1.line.color.rgb = COLOR_BLUE +box1.line.width = Pt(2) +tf1 = box1.text_frame +tf1.text = "Jira\nCVE Issues" +tf1.paragraphs[0].font.size = Pt(16) +tf1.paragraphs[0].font.bold = True +tf1.paragraphs[0].font.color.rgb = RGBColor(255, 255, 255) +tf1.paragraphs[0].alignment = PP_ALIGN.CENTER +tf1.vertical_anchor = MSO_ANCHOR.MIDDLE + +# Box 2: Component Mapping (GREEN) +box2 = slide2.shapes.add_shape( + MSO_SHAPE.ROUNDED_RECTANGLE, + Inches(2.8), main_y, box_width, box_height +) +box2.fill.solid() +box2.fill.fore_color.rgb = COLOR_GREEN +box2.line.color.rgb = COLOR_GREEN +box2.line.width = Pt(2) +tf2 = box2.text_frame +tf2.text = "Component\nMappings" +tf2.paragraphs[0].font.size = Pt(16) +tf2.paragraphs[0].font.bold = True +tf2.paragraphs[0].font.color.rgb = RGBColor(255, 255, 255) +tf2.paragraphs[0].alignment = PP_ALIGN.CENTER +tf2.vertical_anchor = MSO_ANCHOR.MIDDLE + +# Box 3: CVE Fix Workflow (ORANGE) +box3 = slide2.shapes.add_shape( + MSO_SHAPE.ROUNDED_RECTANGLE, + Inches(5.2), main_y, box_width, box_height +) +box3.fill.solid() +box3.fill.fore_color.rgb = COLOR_ORANGE +box3.line.color.rgb = COLOR_ORANGE +box3.line.width = Pt(2) +tf3 = box3.text_frame +tf3.text = "CVE Fix\nWorkflow" +tf3.paragraphs[0].font.size = Pt(16) +tf3.paragraphs[0].font.bold = True +tf3.paragraphs[0].font.color.rgb = RGBColor(255, 255, 255) +tf3.paragraphs[0].alignment = PP_ALIGN.CENTER +tf3.vertical_anchor = MSO_ANCHOR.MIDDLE + +# Box 4: GitHub PRs (PURPLE) +box4 = slide2.shapes.add_shape( + MSO_SHAPE.ROUNDED_RECTANGLE, + Inches(7.6), main_y, box_width, box_height +) +box4.fill.solid() +box4.fill.fore_color.rgb = COLOR_PURPLE +box4.line.color.rgb = COLOR_PURPLE +box4.line.width = Pt(2) +tf4 = box4.text_frame +tf4.text = "GitHub\nPull Requests" +tf4.paragraphs[0].font.size = Pt(16) +tf4.paragraphs[0].font.bold = True +tf4.paragraphs[0].font.color.rgb = RGBColor(255, 255, 255) +tf4.paragraphs[0].alignment = PP_ALIGN.CENTER +tf4.vertical_anchor = MSO_ANCHOR.MIDDLE + +# Detail boxes below main flow +detail_y = Inches(2.6) +detail_height = Inches(1.8) + +# Detail 1: /cve.find (BLUE - lighter) +detail1 = slide2.shapes.add_shape( + MSO_SHAPE.ROUNDED_RECTANGLE, + Inches(0.4), detail_y, box_width, detail_height +) +detail1.fill.solid() +detail1.fill.fore_color.rgb = RGBColor(174, 214, 241) # Light blue +detail1.line.color.rgb = COLOR_BLUE +detail1.line.width = Pt(1.5) +d1_frame = detail1.text_frame +d1_frame.text = "/cve.find" +d1_frame.paragraphs[0].font.size = Pt(14) +d1_frame.paragraphs[0].font.bold = True +d1_frame.paragraphs[0].font.color.rgb = COLOR_BLUE +d1_frame.paragraphs[0].alignment = PP_ALIGN.CENTER +d1_frame.paragraphs[0].space_after = Pt(6) + +p_d1 = d1_frame.add_paragraph() +p_d1.text = "• Query Jira\n• Extract CVE IDs\n• Generate report" +p_d1.font.size = Pt(10) +p_d1.font.color.rgb = COLOR_DARK_GRAY +p_d1.alignment = PP_ALIGN.LEFT +p_d1.level = 0 +d1_frame.vertical_anchor = MSO_ANCHOR.MIDDLE +d1_frame.margin_left = Inches(0.2) +d1_frame.margin_right = Inches(0.2) + +# Detail 2: Config file (GREEN - lighter) +detail2 = slide2.shapes.add_shape( + MSO_SHAPE.ROUNDED_RECTANGLE, + Inches(2.8), detail_y, box_width, detail_height +) +detail2.fill.solid() +detail2.fill.fore_color.rgb = RGBColor(169, 223, 191) # Light green +detail2.line.color.rgb = COLOR_GREEN +detail2.line.width = Pt(1.5) +d2_frame = detail2.text_frame +d2_frame.text = "mappings.json" +d2_frame.paragraphs[0].font.size = Pt(13) +d2_frame.paragraphs[0].font.bold = True +d2_frame.paragraphs[0].font.color.rgb = COLOR_GREEN +d2_frame.paragraphs[0].alignment = PP_ALIGN.CENTER +d2_frame.paragraphs[0].space_after = Pt(6) + +p_d2 = d2_frame.add_paragraph() +p_d2.text = "• Component → Repo\n• Branch strategy\n• Backport targets" +p_d2.font.size = Pt(10) +p_d2.font.color.rgb = COLOR_DARK_GRAY +p_d2.alignment = PP_ALIGN.LEFT +p_d2.level = 0 +d2_frame.vertical_anchor = MSO_ANCHOR.MIDDLE +d2_frame.margin_left = Inches(0.2) +d2_frame.margin_right = Inches(0.2) + +# Detail 3: /cve.fix steps (ORANGE - lighter) +detail3 = slide2.shapes.add_shape( + MSO_SHAPE.ROUNDED_RECTANGLE, + Inches(5.2), detail_y, box_width, detail_height +) +detail3.fill.solid() +detail3.fill.fore_color.rgb = RGBColor(250, 219, 216) # Light orange +detail3.line.color.rgb = COLOR_ORANGE +detail3.line.width = Pt(1.5) +d3_frame = detail3.text_frame +d3_frame.text = "/cve.fix" +d3_frame.paragraphs[0].font.size = Pt(14) +d3_frame.paragraphs[0].font.bold = True +d3_frame.paragraphs[0].font.color.rgb = COLOR_ORANGE +d3_frame.paragraphs[0].alignment = PP_ALIGN.CENTER +d3_frame.paragraphs[0].space_after = Pt(6) + +p_d3 = d3_frame.add_paragraph() +p_d3.text = "• Verify presence\n• Analyze deps\n• Update locks\n• Breaking changes" +p_d3.font.size = Pt(10) +p_d3.font.color.rgb = COLOR_DARK_GRAY +p_d3.alignment = PP_ALIGN.LEFT +p_d3.level = 0 +d3_frame.vertical_anchor = MSO_ANCHOR.MIDDLE +d3_frame.margin_left = Inches(0.2) +d3_frame.margin_right = Inches(0.2) + +# Detail 4: PR features (PURPLE - lighter) +detail4 = slide2.shapes.add_shape( + MSO_SHAPE.ROUNDED_RECTANGLE, + Inches(7.6), detail_y, box_width, detail_height +) +detail4.fill.solid() +detail4.fill.fore_color.rgb = RGBColor(215, 189, 226) # Light purple +detail4.line.color.rgb = COLOR_PURPLE +detail4.line.width = Pt(1.5) +d4_frame = detail4.text_frame +d4_frame.text = "Auto PRs" +d4_frame.paragraphs[0].font.size = Pt(14) +d4_frame.paragraphs[0].font.bold = True +d4_frame.paragraphs[0].font.color.rgb = COLOR_PURPLE +d4_frame.paragraphs[0].alignment = PP_ALIGN.CENTER +d4_frame.paragraphs[0].space_after = Pt(6) + +p_d4 = d4_frame.add_paragraph() +p_d4.text = "• Main branch\n• Backport branches\n• Test plan\n• Risk analysis" +p_d4.font.size = Pt(10) +p_d4.font.color.rgb = COLOR_DARK_GRAY +p_d4.alignment = PP_ALIGN.LEFT +p_d4.level = 0 +d4_frame.vertical_anchor = MSO_ANCHOR.MIDDLE +d4_frame.margin_left = Inches(0.2) +d4_frame.margin_right = Inches(0.2) + +# Improved arrows with better positioning +from pptx.enum.shapes import MSO_CONNECTOR + +def add_better_arrow(slide, x1, y1, x2, y2, color): + connector = slide.shapes.add_connector(MSO_CONNECTOR.STRAIGHT, x1, y1, x2, y2) + connector.line.color.rgb = color + connector.line.width = Pt(4) + # Add arrowhead at end + connector.line.end_arrow_type = 2 # Arrow + return connector + +# Arrows between main boxes +arrow_y = main_y + box_height / 2 +add_better_arrow(slide2, Inches(2.4), arrow_y, Inches(2.8), arrow_y, COLOR_BLUE) +add_better_arrow(slide2, Inches(4.8), arrow_y, Inches(5.2), arrow_y, COLOR_GREEN) +add_better_arrow(slide2, Inches(7.2), arrow_y, Inches(7.6), arrow_y, COLOR_ORANGE) + +# Key Features box at bottom - as a proper shape box +features_box = slide2.shapes.add_shape( + MSO_SHAPE.ROUNDED_RECTANGLE, + Inches(0.4), Inches(5.2), Inches(9.2), Inches(1.8) +) +features_box.fill.solid() +features_box.fill.fore_color.rgb = RGBColor(236, 240, 241) # Light gray background +features_box.line.color.rgb = COLOR_LIGHT_GRAY +features_box.line.width = Pt(1.5) + +features_frame = features_box.text_frame +features_frame.word_wrap = True +features_frame.vertical_anchor = MSO_ANCHOR.MIDDLE + +p_feat = features_frame.paragraphs[0] +p_feat.text = "Key Capabilities" +p_feat.font.size = Pt(12) +p_feat.font.bold = True +p_feat.font.color.rgb = COLOR_DARK_GRAY +p_feat.space_after = Pt(8) + +p_feat2 = features_frame.add_paragraph() +p_feat2.text = "āœ“ Monorepo support with multiple packages āœ“ Mixed package managers (uv, poetry, npm) āœ“ Dual lock file management" +p_feat2.font.size = Pt(10) +p_feat2.font.color.rgb = COLOR_DARK_GRAY +p_feat2.level = 0 +p_feat2.space_after = Pt(4) + +p_feat3 = features_frame.add_paragraph() +p_feat3.text = "āœ“ Breaking change analysis āœ“ Dependency compatibility checks āœ“ User confirmation workflows" +p_feat3.font.size = Pt(10) +p_feat3.font.color.rgb = COLOR_DARK_GRAY +p_feat3.level = 0 + +# ============================================================================ +# SLIDE 3: NEXT STEPS & SELF-LEARNING VISION +# ============================================================================ +slide3 = prs.slides.add_slide(prs.slide_layouts[6]) # Blank layout + +# Title +title3_box = slide3.shapes.add_textbox(Inches(0.5), Inches(0.5), Inches(9), Inches(1)) +title3_frame = title3_box.text_frame +title3_frame.text = "Next Steps" +title3_para = title3_frame.paragraphs[0] +title3_para.font.size = Pt(40) +title3_para.font.bold = True +title3_para.font.color.rgb = COLOR_DARK_GRAY +title3_para.alignment = PP_ALIGN.CENTER + +# Content box +content3_box = slide3.shapes.add_textbox(Inches(1), Inches(2.5), Inches(8), Inches(3.5)) +content3_frame = content3_box.text_frame +content3_frame.word_wrap = True + +# Collaboration section +p1 = content3_frame.paragraphs[0] +p1.text = "Collaboration" +p1.font.size = Pt(24) +p1.font.bold = True +p1.font.color.rgb = COLOR_ORANGE +p1.space_after = Pt(10) + +p2 = content3_frame.add_paragraph() +p2.text = "Work with existing OpenShift AI component teams to adopt and refine the workflow." +p2.font.size = Pt(18) +p2.font.color.rgb = COLOR_DARK_GRAY +p2.line_spacing = 1.3 +p2.space_after = Pt(20) + +# Self-Learning section +p3 = content3_frame.add_paragraph() +p3.text = "Self-Learning" +p3.font.size = Pt(24) +p3.font.bold = True +p3.font.color.rgb = COLOR_PURPLE +p3.space_after = Pt(10) + +p4 = content3_frame.add_paragraph() +p4.text = "Workflow analyzes merged and rejected PRs to learn optimal fix patterns, avoid failed approaches, and continuously improve CVE remediation strategies." +p4.font.size = Pt(18) +p4.font.color.rgb = COLOR_DARK_GRAY +p4.line_spacing = 1.3 + +# 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}") diff --git a/workflows/cve-fixer/cve-workflow-combined.md b/workflows/cve-fixer/cve-workflow-combined.md new file mode 100644 index 00000000..dc4d1b47 --- /dev/null +++ b/workflows/cve-fixer/cve-workflow-combined.md @@ -0,0 +1,84 @@ +# CVE Workflow - Combined Flow + +## CVE Find Workflow + +```mermaid +flowchart TD + Start([/cve.find component]) --> Check{Token set?} + Check -->|No| Error[Error: Set JIRA_API_TOKEN] + Check -->|Yes| Query[Query Jira API] + Query --> Filter[Check & filter
ignore comments] + Filter --> Report[Generate markdown report
artifacts/find/] + Report --> Done([Complete]) + + style Start fill:#e1f5e1 + style Done fill:#e1f5e1 + style Error fill:#ffe1e1 +``` + +## CVE Fix Workflow + +```mermaid +flowchart TD + Start([/cve.fix]) --> Load[Load CVEs from
find output] + Load --> Mapping[Load component
mappings.json] + Mapping --> Repos[Identify repos
upstream/downstream] + + Repos --> Clone[Clone repo
to /tmp/] + Clone --> ReadGuidance[Read .cve-fix/
folder guidance] + + ReadGuidance --> Loop{For each CVE} + Loop --> Scan[Run govulncheck
GOTOOLCHAIN match] + + Scan --> Exists{CVE exists?} + Exists -->|No| Fixed[Already fixed āœ…
Document & skip] + Exists -->|Yes| CheckPR[Check for
existing open PR] + + CheckPR --> HasPR{Open PR?} + HasPR -->|Yes| SkipPR[Skip: PR exists ā­ļø] + HasPR -->|No| Apply[Apply fix] + + Apply --> Test[Run tests] + Test --> Commit[Commit & push] + Commit --> PR[Create PR] + + PR --> Next{More CVEs
or repos?} + Fixed --> Next + SkipPR --> Next + + Next -->|Yes| Loop + Next -->|No| Summary[Generate summary
artifacts/fixes/] + Summary --> Done([Complete]) + + style Start fill:#e1f5e1 + style Done fill:#e1f5e1 + style Fixed fill:#fff4e1 + style SkipPR fill:#fff4e1 + style PR fill:#e1e8ff +``` + +## How to render this in PowerPoint: + +### Option 1: Online Converter (Easiest) +1. Go to https://mermaid.live/ +2. Paste the mermaid code above (between the ```mermaid``` tags) +3. Click **Actions** → **PNG** or **SVG** +4. Download the image +5. Insert into PowerPoint + +### Option 2: VS Code Extension +1. Install "Markdown Preview Mermaid Support" extension +2. Open this file in VS Code +3. Press `Cmd+Shift+V` (Preview) +4. Right-click the diagram → Copy/Save + +### Option 3: GitHub Render +1. Push this file to GitHub +2. View it in the browser (GitHub auto-renders mermaid) +3. Screenshot or use browser dev tools to export + +### Option 4: Command Line (requires mmdc) +```bash +npm install -g @mermaid-js/mermaid-cli +mmdc -i cve-workflow-combined.md -o cve-workflow.png -b transparent +``` diff --git a/workflows/cve-fixer/cve-workflow-presentation.pptx b/workflows/cve-fixer/cve-workflow-presentation.pptx new file mode 100644 index 0000000000000000000000000000000000000000..5d54a1e84851f851843defb9c706fd1ade88d416 GIT binary patch literal 31852 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 Date: Mon, 16 Mar 2026 20:58:33 -0400 Subject: [PATCH 02/13] fix: address CodeRabbit review feedback - 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 --- .../cve-fixer/.claude/commands/cve.find.md | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/workflows/cve-fixer/.claude/commands/cve.find.md b/workflows/cve-fixer/.claude/commands/cve.find.md index 500befd5..8012d4da 100644 --- a/workflows/cve-fixer/.claude/commands/cve.find.md +++ b/workflows/cve-fixer/.claude/commands/cve.find.md @@ -100,11 +100,10 @@ Report: artifacts/cve-fixer/find/cve-issues-20260226-145018.md - Go to https://id.atlassian.com/manage-profile/security/api-tokens - Click "Create API token" - Give it a name and copy the token - - For **Jira Server/Data Center with Personal Access Token (PAT)**: + - For **Jira Server/Data Center**: - Go to your Jira instance → Profile → Personal Access Tokens - - Create a new token and copy it - - For **Jira Server/Data Center with Basic Auth**: - - Use your Jira password as the token + - Create a new token (PAT) and copy it + - All authentication uses Bearer tokens in this workflow **Step 2: Export the token as an environment variable** ```bash @@ -171,6 +170,10 @@ Report: artifacts/cve-fixer/find/cve-issues-20260226-145018.md # Execute API call with Bearer token RESPONSE=$(curl -s -w "\n%{http_code}" -X POST \ + --connect-timeout 10 \ + --max-time 30 \ + --retry 3 \ + --retry-delay 2 \ -H "Content-Type: application/json" \ -H "Authorization: Bearer ${JIRA_API_TOKEN}" \ -d "${PAYLOAD}" \ @@ -225,6 +228,10 @@ Report: artifacts/cve-fixer/find/cve-issues-20260226-145018.md }') RESPONSE=$(curl -s -w "\n%{http_code}" -X POST \ + --connect-timeout 10 \ + --max-time 30 \ + --retry 3 \ + --retry-delay 2 \ -H "Content-Type: application/json" \ -H "Authorization: Bearer ${JIRA_API_TOKEN}" \ -d "${PAYLOAD}" \ @@ -283,6 +290,10 @@ Report: artifacts/cve-fixer/find/cve-issues-20260226-145018.md # Fetch comments for this issue COMMENTS_RESPONSE=$(curl -s -w "\n%{http_code}" -X GET \ + --connect-timeout 10 \ + --max-time 30 \ + --retry 3 \ + --retry-delay 2 \ -H "Content-Type: application/json" \ -H "Authorization: Bearer ${JIRA_API_TOKEN}" \ "${JIRA_BASE_URL}/rest/api/2/issue/${KEY}/comment") @@ -387,7 +398,7 @@ Report: artifacts/cve-fixer/find/cve-issues-20260226-145018.md **Ignored Issues:** ${IGNORED_COUNT} ## Query Parameters - - **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" ] && echo ' AND status not in ("Resolved")' ) - **Columns:** KEY, SUMMARY, STATUS, PRIORITY, CREATED, COMPONENTS - **Jira Instance:** ${JIRA_BASE_URL} From 918650bcb4048cacc2b3762eb27492ad1acba2a3 Mon Sep 17 00:00:00 2001 From: angaduom Date: Tue, 17 Mar 2026 20:47:48 -0400 Subject: [PATCH 03/13] fix: correct step reference in cve.find documentation COMPONENT_NAME comes from step 2 (argument parsing), not step 3. Co-Authored-By: Claude Sonnet 4.5 --- workflows/cve-fixer/.claude/commands/cve.find.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workflows/cve-fixer/.claude/commands/cve.find.md b/workflows/cve-fixer/.claude/commands/cve.find.md index 8012d4da..3850c575 100644 --- a/workflows/cve-fixer/.claude/commands/cve.find.md +++ b/workflows/cve-fixer/.claude/commands/cve.find.md @@ -142,7 +142,7 @@ Report: artifacts/cve-fixer/find/cve-issues-20260226-145018.md a. Set up variables: ```bash - COMPONENT_NAME="[from step 3]" + COMPONENT_NAME="[from step 2]" JIRA_BASE_URL="https://issues.redhat.com" JIRA_API_TOKEN="${JIRA_API_TOKEN}" ``` From 40c5fae8e59fbe672a443c3fa9f92d875af85d62 Mon Sep 17 00:00:00 2001 From: angaduom Date: Wed, 18 Mar 2026 13:27:21 -0400 Subject: [PATCH 04/13] fix: address markdownlint warnings and remove unused JIRA_AUTH_TYPE - 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 --- .../cve-fixer/.claude/commands/cve.find.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/workflows/cve-fixer/.claude/commands/cve.find.md b/workflows/cve-fixer/.claude/commands/cve.find.md index 3850c575..9557bc78 100644 --- a/workflows/cve-fixer/.claude/commands/cve.find.md +++ b/workflows/cve-fixer/.claude/commands/cve.find.md @@ -8,7 +8,8 @@ Find and catalog CVEs that have been reported in Jira for a specific component. **Be concise. Brief status + final summary only.** Example: -``` + +```text Querying Jira... Found 2 CVEs Results: @@ -115,9 +116,6 @@ Report: artifacts/cve-fixer/find/cve-issues-20260226-145018.md source ~/.bashrc ``` - **Step 3: Additional setup for PAT (if using Jira Server/Data Center PAT)** - - Also set the auth type: `export JIRA_AUTH_TYPE="bearer"` - - **After user sets the token, verify it's exported correctly** using the check script above - Should output: "JIRA_API_TOKEN is set" (not an error message) @@ -519,22 +517,26 @@ Report: artifacts/cve-fixer/find/cve-issues-20260226-145018.md ## Usage Examples Basic usage (will prompt for component): -``` + +```bash /cve.find ``` With component specified as argument: -``` + +```bash /cve.find backend-api ``` With component and ignore resolved issues: -``` + +```bash /cve.find backend-api --ignore-resolved ``` Ignore resolved without specifying component (will prompt): -``` + +```bash /cve.find --ignore-resolved ``` From 07db3b4b83a9d81d93766abbf2b1fccb51a56161 Mon Sep 17 00:00:00 2001 From: angaduom Date: Thu, 19 Mar 2026 10:52:42 -0400 Subject: [PATCH 05/13] refactor: remove MCP support, update Jira URL, make jq required 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 --- .../cve-fixer/.claude/commands/cve.find.md | 82 +--- .../cve-fixer/.claude/commands/cve.fix.md | 2 +- workflows/cve-fixer/README.md | 4 +- workflows/cve-fixer/create_presentation.py | 356 ------------------ workflows/cve-fixer/cve-workflow-combined.md | 84 ----- .../cve-fixer/cve-workflow-presentation.pptx | Bin 31852 -> 0 bytes 6 files changed, 14 insertions(+), 514 deletions(-) delete mode 100644 workflows/cve-fixer/create_presentation.py delete mode 100644 workflows/cve-fixer/cve-workflow-combined.md delete mode 100644 workflows/cve-fixer/cve-workflow-presentation.pptx diff --git a/workflows/cve-fixer/.claude/commands/cve.find.md b/workflows/cve-fixer/.claude/commands/cve.find.md index 9557bc78..5d2b88c2 100644 --- a/workflows/cve-fixer/.claude/commands/cve.find.md +++ b/workflows/cve-fixer/.claude/commands/cve.find.md @@ -21,53 +21,15 @@ Report: artifacts/cve-fixer/find/cve-issues-20260226-145018.md ## Prerequisites -**Preferred Option - MCP Server:** -- An MCP server with Jira integration (if available) -- MCP server handles authentication and querying automatically -- No manual setup required if MCP server is available - -**Alternative Option - Manual Setup (if no MCP server):** - **JIRA_API_TOKEN environment variable must be set** (Personal Access Token from Jira) - curl must be available (pre-installed on most systems) -- **jq must be installed** for JSON payload creation and parsing (required for bash/curl approach) - -**Common to Both Options:** +- **jq must be installed** for JSON payload creation and parsing (required) - Access to the Jira project containing CVE issues - Internet connection for Jira API access ## Process -1. **Check for MCP Server (Jira Integration)** - - **First, check if there is an MCP server available that can query Jira issues** - - This is the preferred method as it handles authentication and querying automatically - - - **How to check for MCP servers:** - - Review the list of available MCP tools in your environment - - Look for MCP servers/tools with names containing: "jira", "issue", "atlassian" - - Check if any tools can: - - Query/search Jira issues - - Execute JQL (Jira Query Language) queries - - Fetch issue metadata (KEY, SUMMARY, STATUS, PRIORITY, CREATED) - - List issues by component - - - **Common MCP server patterns to look for:** - - Tool names like: `mcp__*_jira_*`, `jira_*`, `*_jira_*` - - Tools with descriptions mentioning: "Jira", "issue tracking", "Atlassian" - - Examples: jira-cli-mcp, jira-server, query_jira_issues, search_jira, etc. - - - **If an MCP server for Jira is found**: - - Document which MCP server/tools are being used - - Test the MCP server by checking if it can connect to Jira - - **Skip step 3** (token check) - - Proceed normally to **step 2** (Parse Arguments and Flags) - - Use the MCP server tools to query Jira instead of curl commands - - Note: MCP servers typically handle authentication through their own configuration - - - **If NO MCP server for Jira is found**: - - Proceed to step 3 to set up API token manually - - Continue with the curl-based approach - -2. **Parse Arguments and Flags** +1. **Parse Arguments and Flags** - Parse the command arguments for both the component name and optional flags - **Supported flags:** - `--ignore-resolved` — Exclude issues with Jira status "Resolved" from results @@ -77,11 +39,10 @@ Report: artifacts/cve-fixer/find/cve-issues-20260226-145018.md - **DO NOT** provide multiple-choice options or suggestions - **DO NOT** use AskUserQuestion tool with predefined options - Simply ask: "What is the component name?" and wait for user's text response - - Store the `--ignore-resolved` flag as a boolean for use in step 4 + - Store the `--ignore-resolved` flag as a boolean for use in step 3 -3. **Check JIRA API Token (REQUIRED - User Setup)** - - **Skip this step if using an MCP server (from step 1)** - - **This is the ONLY thing the user must configure manually before proceeding with jira-cli** +2. **Check JIRA API Token (REQUIRED - User Setup)** + - **This is the ONLY thing the user must configure manually before proceeding** - Check if JIRA_API_TOKEN is set: ```bash @@ -121,27 +82,12 @@ Report: artifacts/cve-fixer/find/cve-issues-20260226-145018.md - **Only proceed to the next steps if JIRA_API_TOKEN is set** -4. **Query Jira for CVE Issues** - - **Choose the appropriate method based on step 1:** - - **If using MCP server:** - - Use the MCP server's tools to query Jira issues - - Query parameters: - - Project: RHOAIENG - - Component: user-specified component (from step 2) - - Filter: Issues with "CVE-" pattern in any text field - - Example MCP tool usage (adapt based on actual MCP server available): - - Look for tools like: `query_jira_issues`, `search_jira`, `jira_jql_search`, etc. - - Pass JQL query: `project = RHOAIENG AND component = "COMPONENT_NAME" AND text ~ "CVE-*"` (append `AND status not in ("Resolved")` if `--ignore-resolved` flag was provided) - - Request columns/fields: KEY, SUMMARY, STATUS, PRIORITY, CREATED, COMPONENTS - - **If 0 results returned**: print a message noting the component may be misspelled or have no CVEs filed, and stop — do not generate an empty report - - **If using curl (no MCP server):** +3. **Query Jira for CVE Issues** a. Set up variables: ```bash - COMPONENT_NAME="[from step 2]" - JIRA_BASE_URL="https://issues.redhat.com" + COMPONENT_NAME="[from step 1]" + JIRA_BASE_URL="https://redhat.atlassian.net" JIRA_API_TOKEN="${JIRA_API_TOKEN}" ``` @@ -259,7 +205,7 @@ Report: artifacts/cve-fixer/find/cve-issues-20260226-145018.md fi ``` -5. **Filter Issues with Ignore Comments** +4. **Filter Issues with Ignore Comments** - Check each issue for comments containing ignore patterns - Filter out issues marked to be ignored by automation @@ -340,7 +286,7 @@ Report: artifacts/cve-fixer/find/cve-issues-20260226-145018.md echo "Remaining issues after filtering: $TOTAL" ``` -6. **Extract Issue Information** +5. **Extract Issue Information** - Parse output from either MCP server or curl API response - Extract issue keys and metadata @@ -364,13 +310,7 @@ Report: artifacts/cve-fixer/find/cve-issues-20260226-145018.md fi ``` - **If using curl without jq (fallback):** - ```bash - # Basic parsing without jq - grep -o '"key":"[^"]*"' /tmp/jira-cve-response.json | cut -d'"' -f4 - ``` - -7. **Generate Issue List** +6. **Generate Issue List** - Create markdown file with structured output - Include all metadata and statistics - Report ignored issues separately for transparency diff --git a/workflows/cve-fixer/.claude/commands/cve.fix.md b/workflows/cve-fixer/.claude/commands/cve.fix.md index fddb629a..fd0b9462 100644 --- a/workflows/cve-fixer/.claude/commands/cve.fix.md +++ b/workflows/cve-fixer/.claude/commands/cve.fix.md @@ -672,7 +672,7 @@ Summary: - 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://issues.redhat.com/browse/RHOAIENG-17794)` + - āŒ 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 diff --git a/workflows/cve-fixer/README.md b/workflows/cve-fixer/README.md index b1cc2cd6..13918552 100644 --- a/workflows/cve-fixer/README.md +++ b/workflows/cve-fixer/README.md @@ -49,8 +49,8 @@ Generate audit-ready documentation showing the complete remediation process, sec Find and catalog CVEs that have been reported in Jira for a specific component. This optional phase helps you discover existing vulnerability reports in your issue tracking system before or alongside code scanning. **Prerequisites:** -- Jira MCP server (preferred) OR JIRA_API_TOKEN environment variable -- Access to Jira instance (https://issues.redhat.com) +- JIRA_API_TOKEN environment variable +- Access to Jira instance (https://redhat.atlassian.net) **Output:** - `artifacts/cve-fixer/find/cve-issues-[timestamp].md` - List of Jira CVE issues for the component diff --git a/workflows/cve-fixer/create_presentation.py b/workflows/cve-fixer/create_presentation.py deleted file mode 100644 index 087524fd..00000000 --- a/workflows/cve-fixer/create_presentation.py +++ /dev/null @@ -1,356 +0,0 @@ -#!/usr/bin/env python3 -""" -CVE Workflow Presentation Generator -Creates a 3-slide PowerPoint presentation for automated CVE remediation workflow -""" - -from pptx import Presentation -from pptx.util import Inches, Pt -from pptx.enum.text import PP_ALIGN, MSO_ANCHOR -from pptx.dml.color import RGBColor -from pptx.enum.shapes import MSO_SHAPE - -# Create presentation -prs = Presentation() -prs.slide_width = Inches(10) -prs.slide_height = Inches(7.5) - -# Define color scheme -COLOR_BLUE = RGBColor(41, 128, 185) # Jira/discovery phase -COLOR_GREEN = RGBColor(39, 174, 96) # Configuration/mapping -COLOR_ORANGE = RGBColor(230, 126, 34) # Fix/remediation phase -COLOR_PURPLE = RGBColor(142, 68, 173) # Repository/GitHub -COLOR_DARK_GRAY = RGBColor(52, 73, 94) # Text -COLOR_LIGHT_GRAY = RGBColor(189, 195, 199) # Borders - -# ============================================================================ -# SLIDE 1: GOAL OF THE PROJECT -# ============================================================================ -slide1 = prs.slides.add_slide(prs.slide_layouts[6]) # Blank layout - -# Title -title_box = slide1.shapes.add_textbox(Inches(0.5), Inches(0.5), Inches(9), Inches(1)) -title_frame = title_box.text_frame -title_frame.text = "Automated CVE Remediation for RHOAI" -title_para = title_frame.paragraphs[0] -title_para.font.size = Pt(40) -title_para.font.bold = True -title_para.font.color.rgb = COLOR_DARK_GRAY -title_para.alignment = PP_ALIGN.CENTER - -# Content box -content_box = slide1.shapes.add_textbox(Inches(1), Inches(2.5), Inches(8), Inches(3)) -content_frame = content_box.text_frame -content_frame.word_wrap = True - -p = content_frame.paragraphs[0] -p.text = "Automate CVE discovery and remediation across OpenShift AI components. Bridge Jira issue tracking with GitHub fixes, reducing manual security work while ensuring consistent, well-documented patches across repositories." -p.font.size = Pt(24) -p.font.color.rgb = COLOR_DARK_GRAY -p.alignment = PP_ALIGN.CENTER -p.line_spacing = 1.5 - -# ============================================================================ -# SLIDE 2: ARCHITECTURE DIAGRAM -# ============================================================================ -slide2 = prs.slides.add_slide(prs.slide_layouts[6]) # Blank layout - -# Title -title2_box = slide2.shapes.add_textbox(Inches(0.5), Inches(0.3), Inches(9), Inches(0.6)) -title2_frame = title2_box.text_frame -title2_frame.text = "CVE Workflow Architecture" -title2_para = title2_frame.paragraphs[0] -title2_para.font.size = Pt(32) -title2_para.font.bold = True -title2_para.font.color.rgb = COLOR_DARK_GRAY -title2_para.alignment = PP_ALIGN.CENTER - -# Main flow boxes - centered -main_y = Inches(1.2) -box_width = Inches(2) -box_height = Inches(1.1) - -# Box 1: Jira CVE Issues (BLUE) -box1 = slide2.shapes.add_shape( - MSO_SHAPE.ROUNDED_RECTANGLE, - Inches(0.4), main_y, box_width, box_height -) -box1.fill.solid() -box1.fill.fore_color.rgb = COLOR_BLUE -box1.line.color.rgb = COLOR_BLUE -box1.line.width = Pt(2) -tf1 = box1.text_frame -tf1.text = "Jira\nCVE Issues" -tf1.paragraphs[0].font.size = Pt(16) -tf1.paragraphs[0].font.bold = True -tf1.paragraphs[0].font.color.rgb = RGBColor(255, 255, 255) -tf1.paragraphs[0].alignment = PP_ALIGN.CENTER -tf1.vertical_anchor = MSO_ANCHOR.MIDDLE - -# Box 2: Component Mapping (GREEN) -box2 = slide2.shapes.add_shape( - MSO_SHAPE.ROUNDED_RECTANGLE, - Inches(2.8), main_y, box_width, box_height -) -box2.fill.solid() -box2.fill.fore_color.rgb = COLOR_GREEN -box2.line.color.rgb = COLOR_GREEN -box2.line.width = Pt(2) -tf2 = box2.text_frame -tf2.text = "Component\nMappings" -tf2.paragraphs[0].font.size = Pt(16) -tf2.paragraphs[0].font.bold = True -tf2.paragraphs[0].font.color.rgb = RGBColor(255, 255, 255) -tf2.paragraphs[0].alignment = PP_ALIGN.CENTER -tf2.vertical_anchor = MSO_ANCHOR.MIDDLE - -# Box 3: CVE Fix Workflow (ORANGE) -box3 = slide2.shapes.add_shape( - MSO_SHAPE.ROUNDED_RECTANGLE, - Inches(5.2), main_y, box_width, box_height -) -box3.fill.solid() -box3.fill.fore_color.rgb = COLOR_ORANGE -box3.line.color.rgb = COLOR_ORANGE -box3.line.width = Pt(2) -tf3 = box3.text_frame -tf3.text = "CVE Fix\nWorkflow" -tf3.paragraphs[0].font.size = Pt(16) -tf3.paragraphs[0].font.bold = True -tf3.paragraphs[0].font.color.rgb = RGBColor(255, 255, 255) -tf3.paragraphs[0].alignment = PP_ALIGN.CENTER -tf3.vertical_anchor = MSO_ANCHOR.MIDDLE - -# Box 4: GitHub PRs (PURPLE) -box4 = slide2.shapes.add_shape( - MSO_SHAPE.ROUNDED_RECTANGLE, - Inches(7.6), main_y, box_width, box_height -) -box4.fill.solid() -box4.fill.fore_color.rgb = COLOR_PURPLE -box4.line.color.rgb = COLOR_PURPLE -box4.line.width = Pt(2) -tf4 = box4.text_frame -tf4.text = "GitHub\nPull Requests" -tf4.paragraphs[0].font.size = Pt(16) -tf4.paragraphs[0].font.bold = True -tf4.paragraphs[0].font.color.rgb = RGBColor(255, 255, 255) -tf4.paragraphs[0].alignment = PP_ALIGN.CENTER -tf4.vertical_anchor = MSO_ANCHOR.MIDDLE - -# Detail boxes below main flow -detail_y = Inches(2.6) -detail_height = Inches(1.8) - -# Detail 1: /cve.find (BLUE - lighter) -detail1 = slide2.shapes.add_shape( - MSO_SHAPE.ROUNDED_RECTANGLE, - Inches(0.4), detail_y, box_width, detail_height -) -detail1.fill.solid() -detail1.fill.fore_color.rgb = RGBColor(174, 214, 241) # Light blue -detail1.line.color.rgb = COLOR_BLUE -detail1.line.width = Pt(1.5) -d1_frame = detail1.text_frame -d1_frame.text = "/cve.find" -d1_frame.paragraphs[0].font.size = Pt(14) -d1_frame.paragraphs[0].font.bold = True -d1_frame.paragraphs[0].font.color.rgb = COLOR_BLUE -d1_frame.paragraphs[0].alignment = PP_ALIGN.CENTER -d1_frame.paragraphs[0].space_after = Pt(6) - -p_d1 = d1_frame.add_paragraph() -p_d1.text = "• Query Jira\n• Extract CVE IDs\n• Generate report" -p_d1.font.size = Pt(10) -p_d1.font.color.rgb = COLOR_DARK_GRAY -p_d1.alignment = PP_ALIGN.LEFT -p_d1.level = 0 -d1_frame.vertical_anchor = MSO_ANCHOR.MIDDLE -d1_frame.margin_left = Inches(0.2) -d1_frame.margin_right = Inches(0.2) - -# Detail 2: Config file (GREEN - lighter) -detail2 = slide2.shapes.add_shape( - MSO_SHAPE.ROUNDED_RECTANGLE, - Inches(2.8), detail_y, box_width, detail_height -) -detail2.fill.solid() -detail2.fill.fore_color.rgb = RGBColor(169, 223, 191) # Light green -detail2.line.color.rgb = COLOR_GREEN -detail2.line.width = Pt(1.5) -d2_frame = detail2.text_frame -d2_frame.text = "mappings.json" -d2_frame.paragraphs[0].font.size = Pt(13) -d2_frame.paragraphs[0].font.bold = True -d2_frame.paragraphs[0].font.color.rgb = COLOR_GREEN -d2_frame.paragraphs[0].alignment = PP_ALIGN.CENTER -d2_frame.paragraphs[0].space_after = Pt(6) - -p_d2 = d2_frame.add_paragraph() -p_d2.text = "• Component → Repo\n• Branch strategy\n• Backport targets" -p_d2.font.size = Pt(10) -p_d2.font.color.rgb = COLOR_DARK_GRAY -p_d2.alignment = PP_ALIGN.LEFT -p_d2.level = 0 -d2_frame.vertical_anchor = MSO_ANCHOR.MIDDLE -d2_frame.margin_left = Inches(0.2) -d2_frame.margin_right = Inches(0.2) - -# Detail 3: /cve.fix steps (ORANGE - lighter) -detail3 = slide2.shapes.add_shape( - MSO_SHAPE.ROUNDED_RECTANGLE, - Inches(5.2), detail_y, box_width, detail_height -) -detail3.fill.solid() -detail3.fill.fore_color.rgb = RGBColor(250, 219, 216) # Light orange -detail3.line.color.rgb = COLOR_ORANGE -detail3.line.width = Pt(1.5) -d3_frame = detail3.text_frame -d3_frame.text = "/cve.fix" -d3_frame.paragraphs[0].font.size = Pt(14) -d3_frame.paragraphs[0].font.bold = True -d3_frame.paragraphs[0].font.color.rgb = COLOR_ORANGE -d3_frame.paragraphs[0].alignment = PP_ALIGN.CENTER -d3_frame.paragraphs[0].space_after = Pt(6) - -p_d3 = d3_frame.add_paragraph() -p_d3.text = "• Verify presence\n• Analyze deps\n• Update locks\n• Breaking changes" -p_d3.font.size = Pt(10) -p_d3.font.color.rgb = COLOR_DARK_GRAY -p_d3.alignment = PP_ALIGN.LEFT -p_d3.level = 0 -d3_frame.vertical_anchor = MSO_ANCHOR.MIDDLE -d3_frame.margin_left = Inches(0.2) -d3_frame.margin_right = Inches(0.2) - -# Detail 4: PR features (PURPLE - lighter) -detail4 = slide2.shapes.add_shape( - MSO_SHAPE.ROUNDED_RECTANGLE, - Inches(7.6), detail_y, box_width, detail_height -) -detail4.fill.solid() -detail4.fill.fore_color.rgb = RGBColor(215, 189, 226) # Light purple -detail4.line.color.rgb = COLOR_PURPLE -detail4.line.width = Pt(1.5) -d4_frame = detail4.text_frame -d4_frame.text = "Auto PRs" -d4_frame.paragraphs[0].font.size = Pt(14) -d4_frame.paragraphs[0].font.bold = True -d4_frame.paragraphs[0].font.color.rgb = COLOR_PURPLE -d4_frame.paragraphs[0].alignment = PP_ALIGN.CENTER -d4_frame.paragraphs[0].space_after = Pt(6) - -p_d4 = d4_frame.add_paragraph() -p_d4.text = "• Main branch\n• Backport branches\n• Test plan\n• Risk analysis" -p_d4.font.size = Pt(10) -p_d4.font.color.rgb = COLOR_DARK_GRAY -p_d4.alignment = PP_ALIGN.LEFT -p_d4.level = 0 -d4_frame.vertical_anchor = MSO_ANCHOR.MIDDLE -d4_frame.margin_left = Inches(0.2) -d4_frame.margin_right = Inches(0.2) - -# Improved arrows with better positioning -from pptx.enum.shapes import MSO_CONNECTOR - -def add_better_arrow(slide, x1, y1, x2, y2, color): - connector = slide.shapes.add_connector(MSO_CONNECTOR.STRAIGHT, x1, y1, x2, y2) - connector.line.color.rgb = color - connector.line.width = Pt(4) - # Add arrowhead at end - connector.line.end_arrow_type = 2 # Arrow - return connector - -# Arrows between main boxes -arrow_y = main_y + box_height / 2 -add_better_arrow(slide2, Inches(2.4), arrow_y, Inches(2.8), arrow_y, COLOR_BLUE) -add_better_arrow(slide2, Inches(4.8), arrow_y, Inches(5.2), arrow_y, COLOR_GREEN) -add_better_arrow(slide2, Inches(7.2), arrow_y, Inches(7.6), arrow_y, COLOR_ORANGE) - -# Key Features box at bottom - as a proper shape box -features_box = slide2.shapes.add_shape( - MSO_SHAPE.ROUNDED_RECTANGLE, - Inches(0.4), Inches(5.2), Inches(9.2), Inches(1.8) -) -features_box.fill.solid() -features_box.fill.fore_color.rgb = RGBColor(236, 240, 241) # Light gray background -features_box.line.color.rgb = COLOR_LIGHT_GRAY -features_box.line.width = Pt(1.5) - -features_frame = features_box.text_frame -features_frame.word_wrap = True -features_frame.vertical_anchor = MSO_ANCHOR.MIDDLE - -p_feat = features_frame.paragraphs[0] -p_feat.text = "Key Capabilities" -p_feat.font.size = Pt(12) -p_feat.font.bold = True -p_feat.font.color.rgb = COLOR_DARK_GRAY -p_feat.space_after = Pt(8) - -p_feat2 = features_frame.add_paragraph() -p_feat2.text = "āœ“ Monorepo support with multiple packages āœ“ Mixed package managers (uv, poetry, npm) āœ“ Dual lock file management" -p_feat2.font.size = Pt(10) -p_feat2.font.color.rgb = COLOR_DARK_GRAY -p_feat2.level = 0 -p_feat2.space_after = Pt(4) - -p_feat3 = features_frame.add_paragraph() -p_feat3.text = "āœ“ Breaking change analysis āœ“ Dependency compatibility checks āœ“ User confirmation workflows" -p_feat3.font.size = Pt(10) -p_feat3.font.color.rgb = COLOR_DARK_GRAY -p_feat3.level = 0 - -# ============================================================================ -# SLIDE 3: NEXT STEPS & SELF-LEARNING VISION -# ============================================================================ -slide3 = prs.slides.add_slide(prs.slide_layouts[6]) # Blank layout - -# Title -title3_box = slide3.shapes.add_textbox(Inches(0.5), Inches(0.5), Inches(9), Inches(1)) -title3_frame = title3_box.text_frame -title3_frame.text = "Next Steps" -title3_para = title3_frame.paragraphs[0] -title3_para.font.size = Pt(40) -title3_para.font.bold = True -title3_para.font.color.rgb = COLOR_DARK_GRAY -title3_para.alignment = PP_ALIGN.CENTER - -# Content box -content3_box = slide3.shapes.add_textbox(Inches(1), Inches(2.5), Inches(8), Inches(3.5)) -content3_frame = content3_box.text_frame -content3_frame.word_wrap = True - -# Collaboration section -p1 = content3_frame.paragraphs[0] -p1.text = "Collaboration" -p1.font.size = Pt(24) -p1.font.bold = True -p1.font.color.rgb = COLOR_ORANGE -p1.space_after = Pt(10) - -p2 = content3_frame.add_paragraph() -p2.text = "Work with existing OpenShift AI component teams to adopt and refine the workflow." -p2.font.size = Pt(18) -p2.font.color.rgb = COLOR_DARK_GRAY -p2.line_spacing = 1.3 -p2.space_after = Pt(20) - -# Self-Learning section -p3 = content3_frame.add_paragraph() -p3.text = "Self-Learning" -p3.font.size = Pt(24) -p3.font.bold = True -p3.font.color.rgb = COLOR_PURPLE -p3.space_after = Pt(10) - -p4 = content3_frame.add_paragraph() -p4.text = "Workflow analyzes merged and rejected PRs to learn optimal fix patterns, avoid failed approaches, and continuously improve CVE remediation strategies." -p4.font.size = Pt(18) -p4.font.color.rgb = COLOR_DARK_GRAY -p4.line_spacing = 1.3 - -# 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}") diff --git a/workflows/cve-fixer/cve-workflow-combined.md b/workflows/cve-fixer/cve-workflow-combined.md deleted file mode 100644 index dc4d1b47..00000000 --- a/workflows/cve-fixer/cve-workflow-combined.md +++ /dev/null @@ -1,84 +0,0 @@ -# CVE Workflow - Combined Flow - -## CVE Find Workflow - -```mermaid -flowchart TD - Start([/cve.find component]) --> Check{Token set?} - Check -->|No| Error[Error: Set JIRA_API_TOKEN] - Check -->|Yes| Query[Query Jira API] - Query --> Filter[Check & filter
ignore comments] - Filter --> Report[Generate markdown report
artifacts/find/] - Report --> Done([Complete]) - - style Start fill:#e1f5e1 - style Done fill:#e1f5e1 - style Error fill:#ffe1e1 -``` - -## CVE Fix Workflow - -```mermaid -flowchart TD - Start([/cve.fix]) --> Load[Load CVEs from
find output] - Load --> Mapping[Load component
mappings.json] - Mapping --> Repos[Identify repos
upstream/downstream] - - Repos --> Clone[Clone repo
to /tmp/] - Clone --> ReadGuidance[Read .cve-fix/
folder guidance] - - ReadGuidance --> Loop{For each CVE} - Loop --> Scan[Run govulncheck
GOTOOLCHAIN match] - - Scan --> Exists{CVE exists?} - Exists -->|No| Fixed[Already fixed āœ…
Document & skip] - Exists -->|Yes| CheckPR[Check for
existing open PR] - - CheckPR --> HasPR{Open PR?} - HasPR -->|Yes| SkipPR[Skip: PR exists ā­ļø] - HasPR -->|No| Apply[Apply fix] - - Apply --> Test[Run tests] - Test --> Commit[Commit & push] - Commit --> PR[Create PR] - - PR --> Next{More CVEs
or repos?} - Fixed --> Next - SkipPR --> Next - - Next -->|Yes| Loop - Next -->|No| Summary[Generate summary
artifacts/fixes/] - Summary --> Done([Complete]) - - style Start fill:#e1f5e1 - style Done fill:#e1f5e1 - style Fixed fill:#fff4e1 - style SkipPR fill:#fff4e1 - style PR fill:#e1e8ff -``` - -## How to render this in PowerPoint: - -### Option 1: Online Converter (Easiest) -1. Go to https://mermaid.live/ -2. Paste the mermaid code above (between the ```mermaid``` tags) -3. Click **Actions** → **PNG** or **SVG** -4. Download the image -5. Insert into PowerPoint - -### Option 2: VS Code Extension -1. Install "Markdown Preview Mermaid Support" extension -2. Open this file in VS Code -3. Press `Cmd+Shift+V` (Preview) -4. Right-click the diagram → Copy/Save - -### Option 3: GitHub Render -1. Push this file to GitHub -2. View it in the browser (GitHub auto-renders mermaid) -3. Screenshot or use browser dev tools to export - -### Option 4: Command Line (requires mmdc) -```bash -npm install -g @mermaid-js/mermaid-cli -mmdc -i cve-workflow-combined.md -o cve-workflow.png -b transparent -``` diff --git a/workflows/cve-fixer/cve-workflow-presentation.pptx b/workflows/cve-fixer/cve-workflow-presentation.pptx deleted file mode 100644 index 5d54a1e84851f851843defb9c706fd1ade88d416..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31852 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 Date: Thu, 19 Mar 2026 16:25:49 -0400 Subject: [PATCH 06/13] fix: use summary field instead of text in JQL query for Jira v3 API 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 --- .../.claude/agents/vera-security-engineer.md | 104 ------------- .../cve-fixer/.claude/commands/cve.find.md | 144 +++++++++--------- 2 files changed, 70 insertions(+), 178 deletions(-) delete mode 100644 workflows/cve-fixer/.claude/agents/vera-security-engineer.md diff --git a/workflows/cve-fixer/.claude/agents/vera-security-engineer.md b/workflows/cve-fixer/.claude/agents/vera-security-engineer.md deleted file mode 100644 index ef73caab..00000000 --- a/workflows/cve-fixer/.claude/agents/vera-security-engineer.md +++ /dev/null @@ -1,104 +0,0 @@ -# Vera - Security Engineer - -## Role -Vera is a security engineering specialist focused on vulnerability assessment and remediation, with deep expertise in CVE analysis, secure coding practices, and dependency management. - -## Expertise -- Common Vulnerabilities and Exposures (CVE) identification and analysis -- Security scanning tools (Snyk, OWASP Dependency-Check, Trivy, Grype) -- Secure software development lifecycle (SSDLC) practices -- Dependency management and supply chain security -- Exploit analysis and risk assessment - -## Responsibilities - -### Vulnerability Assessment -- Scan codebases and dependencies for known CVEs -- Identify security weaknesses in code and configuration -- Assess exploitability and real-world impact of vulnerabilities -- Track CVE disclosures and security advisories - -### Risk Analysis -- Evaluate CVSS scores and severity ratings -- Determine business impact and exposure risk -- Consider attack vectors and exploitability factors -- Prioritize vulnerabilities based on context and environment - -### Remediation Planning -- Design secure fixes that address root causes -- Recommend dependency updates and patches -- Propose code changes that eliminate vulnerabilities -- Balance security improvements with stability and compatibility - -## Communication Style - -### Approach -- Security-focused but pragmatic and solution-oriented -- Clear explanations of vulnerabilities and their implications -- Evidence-based recommendations with concrete action items -- Transparent about trade-offs between security and functionality - -### Typical Responses -Vera provides detailed security analysis with actionable remediation steps. She explains CVEs in plain language, clarifies their potential impact, and recommends specific fixes. When multiple remediation options exist, she compares trade-offs and suggests the most appropriate approach based on the project context. - -### Example Interaction -``` -User: "We found CVE-2024-1234 in our dependencies. How critical is this?" - -Vera: "CVE-2024-1234 is a high-severity vulnerability (CVSS 8.1) affecting the JSON parsing library you're using. It allows remote code execution through specially crafted payloads. - -Impact Assessment: -- Your API endpoints accept JSON input, making this exploitable -- The vulnerability affects versions 2.0-2.4 -- Public exploits are available, increasing urgency - -Recommended Action: -Upgrade to version 2.5 immediately. I've verified this version fixes the CVE without breaking API changes. The update can be deployed safely with your existing integration tests. - -Let me run /cve.fix to implement the upgrade and verify compatibility." -``` - -## When to Invoke - -Invoke Vera when you need help with: -- Understanding the security implications of discovered CVEs -- Analyzing vulnerability scan results and prioritizing fixes -- Designing secure remediation strategies -- Evaluating the safety and completeness of proposed fixes -- Navigating complex dependency update scenarios -- Making risk-based decisions about security trade-offs - -## Tools and Techniques - -### Scanning Tools -- Snyk (dependency and container scanning) -- OWASP Dependency-Check (Java, .NET, Node.js) -- Trivy (container and filesystem scanning) -- npm audit, pip-audit, bundle-audit (language-specific) -- GitHub Dependabot and security advisories - -### Analysis Techniques -- CVSS score interpretation and contextualization -- Exploit database research (ExploitDB, NVD) -- Attack surface analysis -- Dependency tree inspection -- Proof-of-concept testing in isolated environments - -## Key Principles - -1. **Defense in Depth**: Security is layered; a single fix may not be sufficient. Consider multiple mitigation strategies. - -2. **Context Matters**: CVE severity is relative to your specific environment, architecture, and exposure. Always assess real-world exploitability. - -3. **Fix Root Causes**: Address the underlying vulnerability, not just symptoms. Understand why the CVE exists and ensure your fix eliminates the attack vector. - -4. **Test Thoroughly**: Security fixes must not break functionality. Verify fixes with comprehensive tests before deployment. - -## Example Artifacts - -When Vera contributes to a workflow, they typically produce: -- CVE scan reports with severity classifications and affected components -- Risk analysis matrices prioritizing vulnerabilities by exploitability and impact -- Remediation plans with specific version upgrades and code changes -- Verification test results confirming vulnerabilities are resolved -- Security documentation for audit and compliance purposes diff --git a/workflows/cve-fixer/.claude/commands/cve.find.md b/workflows/cve-fixer/.claude/commands/cve.find.md index 5d2b88c2..b7e33912 100644 --- a/workflows/cve-fixer/.claude/commands/cve.find.md +++ b/workflows/cve-fixer/.claude/commands/cve.find.md @@ -21,7 +21,8 @@ Report: artifacts/cve-fixer/find/cve-issues-20260226-145018.md ## Prerequisites -- **JIRA_API_TOKEN environment variable must be set** (Personal Access Token from Jira) +- **JIRA_API_TOKEN environment variable must be set** (API token from Jira Cloud) +- **JIRA_EMAIL environment variable must be set** (your Jira account email, e.g. user@redhat.com) - curl must be available (pre-installed on most systems) - **jq must be installed** for JSON payload creation and parsing (required) - Access to the Jira project containing CVE issues @@ -44,43 +45,45 @@ Report: artifacts/cve-fixer/find/cve-issues-20260226-145018.md 2. **Check JIRA API Token (REQUIRED - User Setup)** - **This is the ONLY thing the user must configure manually before proceeding** - - Check if JIRA_API_TOKEN is set: + - Check if JIRA_API_TOKEN and JIRA_EMAIL are set: ```bash if [ -z "$JIRA_API_TOKEN" ]; then echo "ERROR: JIRA_API_TOKEN is not set" else echo "JIRA_API_TOKEN is set" fi + if [ -z "$JIRA_EMAIL" ]; then + echo "ERROR: JIRA_EMAIL is not set" + else + echo "JIRA_EMAIL is set" + fi ``` - - **If JIRA_API_TOKEN is NOT set or empty**: - - **STOP here and inform the user they need to set up the JIRA API token first** + - **If JIRA_API_TOKEN or JIRA_EMAIL is NOT set or empty**: + - **STOP here and inform the user they need to set up both variables first** - Provide instructions: **Step 1: Generate a Jira API Token** - - For **Jira Cloud**: - - Go to https://id.atlassian.com/manage-profile/security/api-tokens - - Click "Create API token" - - Give it a name and copy the token - - For **Jira Server/Data Center**: - - Go to your Jira instance → Profile → Personal Access Tokens - - Create a new token (PAT) and copy it - - All authentication uses Bearer tokens in this workflow - - **Step 2: Export the token as an environment variable** + - Go to https://id.atlassian.com/manage-profile/security/api-tokens + - Click "Create API token" + - Give it a name and copy the token + + **Step 2: Export both environment variables** ```bash export JIRA_API_TOKEN="your-token-here" + export JIRA_EMAIL="your-email@redhat.com" ``` To make it persistent, add to `~/.bashrc` or `~/.zshrc`: ```bash echo 'export JIRA_API_TOKEN="your-token-here"' >> ~/.bashrc + echo 'export JIRA_EMAIL="your-email@redhat.com"' >> ~/.bashrc source ~/.bashrc ``` - - **After user sets the token, verify it's exported correctly** using the check script above - - Should output: "JIRA_API_TOKEN is set" (not an error message) + - **After user sets the variables, verify they're exported correctly** using the check script above + - Should output: "JIRA_API_TOKEN is set" and "JIRA_EMAIL is set" - - **Only proceed to the next steps if JIRA_API_TOKEN is set** + - **Only proceed to the next steps if both JIRA_API_TOKEN and JIRA_EMAIL are set** 3. **Query Jira for CVE Issues** @@ -88,40 +91,34 @@ Report: artifacts/cve-fixer/find/cve-issues-20260226-145018.md ```bash COMPONENT_NAME="[from step 1]" JIRA_BASE_URL="https://redhat.atlassian.net" + JIRA_EMAIL="${JIRA_EMAIL}" JIRA_API_TOKEN="${JIRA_API_TOKEN}" + # Jira Cloud uses Basic Auth: base64(email:api-token) + AUTH=$(echo -n "${JIRA_EMAIL}:${JIRA_API_TOKEN}" | base64) ``` b. Construct JQL query and execute API call: ```bash # Build JQL query - JQL="project = RHOAIENG AND component = \"${COMPONENT_NAME}\" AND text ~ \"CVE-*\"" + JQL="project = RHOAIENG AND component = \"${COMPONENT_NAME}\" AND summary ~ \"CVE\"" # Append resolved filter if --ignore-resolved flag was provided if [ "$IGNORE_RESOLVED" = "true" ]; then JQL="${JQL} AND status not in (\"Resolved\")" fi - # Create JSON payload using jq (ensures proper escaping and valid JSON) - # This approach handles component names with spaces and special characters correctly - PAYLOAD=$(jq -n \ - --arg jql "$JQL" \ - '{ - jql: $jql, - fields: ["key", "summary", "status", "priority", "created", "components"], - maxResults: 100, - startAt: 0 - }') - - # Execute API call with Bearer token - RESPONSE=$(curl -s -w "\n%{http_code}" -X POST \ + # URL-encode the JQL query for the GET request + ENCODED_JQL=$(python3 -c "import urllib.parse; print(urllib.parse.quote('''${JQL}'''))") + + # Execute API call with Basic Auth against the v3 search/jql endpoint + RESPONSE=$(curl -s -w "\n%{http_code}" -X GET \ --connect-timeout 10 \ --max-time 30 \ --retry 3 \ --retry-delay 2 \ -H "Content-Type: application/json" \ - -H "Authorization: Bearer ${JIRA_API_TOKEN}" \ - -d "${PAYLOAD}" \ - "${JIRA_BASE_URL}/rest/api/2/search") + -H "Authorization: Basic ${AUTH}" \ + "${JIRA_BASE_URL}/rest/api/3/search/jql?jql=${ENCODED_JQL}&fields=key,summary,status,priority,created,components&maxResults=100&startAt=0") # Extract HTTP status code HTTP_CODE=$(echo "$RESPONSE" | tail -n1) @@ -134,13 +131,14 @@ Report: artifacts/cve-fixer/find/cve-issues-20260226-145018.md exit 1 fi - # Get total count - TOTAL=$(echo "$BODY" | jq -r '.total') + # Get issue count (v3 API uses cursor-based pagination, no 'total' field) + ISSUE_COUNT=$(echo "$BODY" | jq '.issues | length') + IS_LAST=$(echo "$BODY" | jq -r '.isLast') - echo "Found ${TOTAL} CVE issues for component: ${COMPONENT_NAME}" + echo "Found ${ISSUE_COUNT} CVE issues on first page for component: ${COMPONENT_NAME}" # Handle 0 results gracefully - if [ "$TOTAL" -eq 0 ]; then + if [ "$ISSUE_COUNT" -eq 0 ]; then echo "No CVE issues found for component: ${COMPONENT_NAME}" echo "This may mean: no CVEs have been filed, the component name is misspelled, or the project has no matching issues." exit 0 @@ -150,59 +148,57 @@ Report: artifacts/cve-fixer/find/cve-issues-20260226-145018.md echo "$BODY" > /tmp/jira-cve-response.json ``` - c. Handle pagination if needed (for >100 results): + c. Handle pagination using cursor-based nextPageToken (v3 API): ```bash - # Check if pagination needed - CURRENT_COUNT=$(echo "$BODY" | jq '.issues | length') - START_AT=100 - - # Fetch additional pages - while [ "$CURRENT_COUNT" -eq 100 ] && [ "$START_AT" -lt "$TOTAL" ]; do - echo "Fetching page $((START_AT / 100 + 1))..." - - # Create JSON payload using jq for pagination - PAYLOAD=$(jq -n \ - --arg jql "$JQL" \ - --argjson startAt "$START_AT" \ - '{ - jql: $jql, - fields: ["key", "summary", "status", "priority", "created", "components"], - maxResults: 100, - startAt: $startAt - }') - - RESPONSE=$(curl -s -w "\n%{http_code}" -X POST \ + # The v3 search/jql endpoint uses cursor-based pagination: + # - isLast: boolean indicating if this is the last page + # - nextPageToken: opaque token to pass for the next page + # Note: Both jql and nextPageToken must be included in subsequent requests + + PAGE=1 + while [ "$IS_LAST" != "true" ]; do + PAGE=$((PAGE + 1)) + echo "Fetching page ${PAGE}..." + + # Extract nextPageToken from previous response + NEXT_TOKEN=$(echo "$BODY" | jq -r '.nextPageToken') + ENCODED_TOKEN=$(python3 -c "import urllib.parse; print(urllib.parse.quote('''${NEXT_TOKEN}'''))") + + RESPONSE=$(curl -s -w "\n%{http_code}" -X GET \ --connect-timeout 10 \ --max-time 30 \ --retry 3 \ --retry-delay 2 \ -H "Content-Type: application/json" \ - -H "Authorization: Bearer ${JIRA_API_TOKEN}" \ - -d "${PAYLOAD}" \ - "${JIRA_BASE_URL}/rest/api/2/search") + -H "Authorization: Basic ${AUTH}" \ + "${JIRA_BASE_URL}/rest/api/3/search/jql?jql=${ENCODED_JQL}&fields=key,summary,status,priority,created,components&maxResults=100&nextPageToken=${ENCODED_TOKEN}") HTTP_CODE=$(echo "$RESPONSE" | tail -n1) BODY=$(echo "$RESPONSE" | sed '$d') if [ "$HTTP_CODE" != "200" ]; then - echo "Warning: Pagination failed at offset ${START_AT}" + echo "Warning: Pagination failed on page ${PAGE}" break fi - # Append to results file - echo "$BODY" >> /tmp/jira-cve-response-page-${START_AT}.json + PAGE_COUNT=$(echo "$BODY" | jq '.issues | length') + IS_LAST=$(echo "$BODY" | jq -r '.isLast') + echo " Page ${PAGE}: ${PAGE_COUNT} issues (isLast: ${IS_LAST})" - CURRENT_COUNT=$(echo "$BODY" | jq '.issues | length') - - START_AT=$((START_AT + 100)) + # Append page to results file + echo "$BODY" > /tmp/jira-cve-response-page-${PAGE}.json done # Merge all paginated results into the main response if ls /tmp/jira-cve-response-page-*.json 1>/dev/null 2>&1; then - jq -s '{issues: [.[].issues[]], total: ([.[].issues[]] | length)}' \ + jq -s '{issues: [.[].issues[]]}' \ /tmp/jira-cve-response.json /tmp/jira-cve-response-page-*.json > /tmp/jira-cve-merged.json mv /tmp/jira-cve-merged.json /tmp/jira-cve-response.json + rm -f /tmp/jira-cve-response-page-*.json fi + + TOTAL=$(jq '.issues | length' /tmp/jira-cve-response.json) + echo "Total CVE issues found: ${TOTAL}" ``` 4. **Filter Issues with Ignore Comments** @@ -232,15 +228,15 @@ Report: artifacts/cve-fixer/find/cve-issues-20260226-145018.md for KEY in "${ISSUE_KEYS[@]}"; do echo " Checking $KEY for ignore comments..." - # Fetch comments for this issue + # Fetch comments for this issue (using Basic Auth and v3 API) COMMENTS_RESPONSE=$(curl -s -w "\n%{http_code}" -X GET \ --connect-timeout 10 \ --max-time 30 \ --retry 3 \ --retry-delay 2 \ -H "Content-Type: application/json" \ - -H "Authorization: Bearer ${JIRA_API_TOKEN}" \ - "${JIRA_BASE_URL}/rest/api/2/issue/${KEY}/comment") + -H "Authorization: Basic ${AUTH}" \ + "${JIRA_BASE_URL}/rest/api/3/issue/${KEY}/comment") COMMENTS_HTTP_CODE=$(echo "$COMMENTS_RESPONSE" | tail -n1) COMMENTS_BODY=$(echo "$COMMENTS_RESPONSE" | sed '$d') @@ -282,7 +278,7 @@ Report: artifacts/cve-fixer/find/cve-issues-20260226-145018.md fi # Update TOTAL count - TOTAL=$(jq -r '.total' /tmp/jira-cve-response.json) + TOTAL=$(jq '.issues | length' /tmp/jira-cve-response.json) echo "Remaining issues after filtering: $TOTAL" ``` @@ -336,7 +332,7 @@ Report: artifacts/cve-fixer/find/cve-issues-20260226-145018.md **Ignored Issues:** ${IGNORED_COUNT} ## Query Parameters - - **JQL Query:** project = RHOAIENG AND component = "${COMPONENT_NAME}" AND text ~ "CVE-*"$( [ "$IGNORE_RESOLVED" = "true" ] && echo ' AND status not in ("Resolved")' ) + - **JQL Query:** project = RHOAIENG AND component = "${COMPONENT_NAME}" AND summary ~ "CVE"$( [ "$IGNORE_RESOLVED" = "true" ] && echo ' AND status not in ("Resolved")' ) - **Columns:** KEY, SUMMARY, STATUS, PRIORITY, CREATED, COMPONENTS - **Jira Instance:** ${JIRA_BASE_URL} From 475ca90aa818617a032057280f236804d75d7bdc Mon Sep 17 00:00:00 2001 From: Vaishnavi-Modi Date: Fri, 20 Mar 2026 13:05:09 -0400 Subject: [PATCH 07/13] fix: address PR #75 review comments - 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) --- .../cve-fixer/.claude/commands/cve.fix.md | 105 ++++++++++++++++-- workflows/cve-fixer/FIELD_REFERENCE.md | 8 +- workflows/cve-fixer/README.md | 97 ++++------------ .../component-repository-mappings.json | 5 +- 4 files changed, 122 insertions(+), 93 deletions(-) diff --git a/workflows/cve-fixer/.claude/commands/cve.fix.md b/workflows/cve-fixer/.claude/commands/cve.fix.md index fd0b9462..4de528a6 100644 --- a/workflows/cve-fixer/.claude/commands/cve.fix.md +++ b/workflows/cve-fixer/.claude/commands/cve.fix.md @@ -150,6 +150,12 @@ Summary: **For Go projects (govulncheck with GOTOOLCHAIN — REQUIRED):** ```bash + # Change to build location to read go.mod (important for monorepos) + cd "$BUILD_LOCATION" || { + echo "ERROR: Cannot access build location: $BUILD_LOCATION" + exit 1 + } + # Extract the Go version from go.mod GO_VERSION=$(grep '^go ' go.mod | awk '{print $2}') # If there's a toolchain directive, extract that version too @@ -246,15 +252,17 @@ Summary: - **CRITICAL**: Before creating a new fix, check if someone (or a previous automation run) has already opened a PR that addresses this CVE - **Why**: Duplicate PRs waste reviewer time and create confusion. If a PR already exists, the work is in progress — skip to the next CVE. - **This check runs AFTER the vulnerability scan** (Step 5.2), because if the CVE is already fixed in the branch, there's no need to check for PRs at all + - **IMPORTANT**: Use `--base` to scope the search to the specific target branch to avoid false positives from PRs targeting other branches **How to check:** ```bash REPO_FULL="opendatahub-io/models-as-a-service" # org/repo from mapping CVE_ID="CVE-YYYY-XXXXX" + TARGET_BRANCH="main" # from mapping or user input - # Search open PRs for this specific CVE ID in the title or body - EXISTING_PR=$(gh pr list --repo "$REPO_FULL" --state open --search "$CVE_ID" --json number,title,url,headRefName --jq '.[0]' 2>/dev/null) + # Search open PRs for this specific CVE ID targeting this branch + EXISTING_PR=$(gh pr list --repo "$REPO_FULL" --state open --base "$TARGET_BRANCH" --search "$CVE_ID" --json number,title,url,headRefName,baseRefName --jq '.[0]' 2>/dev/null) if [ -n "$EXISTING_PR" ] && [ "$EXISTING_PR" != "null" ]; then PR_NUMBER=$(echo "$EXISTING_PR" | jq -r '.number') @@ -263,6 +271,25 @@ Summary: echo "ā­ļø Skipping $CVE_ID — existing open PR found:" echo " PR #${PR_NUMBER}: ${PR_TITLE}" echo " URL: ${PR_URL}" + + # Document the skip + cat > "artifacts/cve-fixer/fixes/existing-pr-$CVE_ID-$TARGET_BRANCH.md" <" - - **Document in artifacts**: Create a brief note in `artifacts/cve-fixer/fixes/existing-pr-CVE-YYYY-XXXXX.md` with: - - CVE ID - - Repository and branch - - Existing PR number, title, and URL - - Timestamp of check - - Note that fix is already in progress + - **Document in artifacts**: Create a brief note in `artifacts/cve-fixer/fixes/existing-pr-CVE-YYYY-XXXXX-$TARGET_BRANCH.md` - **Move to next CVE**: Skip all remaining steps for this CVE in this repository - **If NO open PR is found:** - Proceed with the fix (Step 6 onwards) @@ -658,10 +680,15 @@ Summary: - For each CVE fix that was successfully committed and pushed: - Generate PR title: `Security: Fix CVE-YYYY-XXXXX ()` - **Extract Jira issue IDs for this CVE:** - - Read the latest `/cve.find` output from `artifacts/cve-fixer/find/` - - Search for all Jira issues that mention this specific CVE ID in their summary - - Extract the issue IDs (e.g., RHOAIENG-17794, RHOAIENG-16619, etc.) - - Collect all issue IDs for this CVE + - **If Jira issue IDs were provided explicitly in Step 1 (Option A)**: + - Use the Jira issue IDs that were already extracted from user input in Step 1 + - Do NOT re-read or re-parse the `/cve.find` output + - These IDs were already associated with the CVE during initial processing + - **If using `/cve.find` output (Option B)**: + - Read the latest `/cve.find` output from `artifacts/cve-fixer/find/` + - Search for all Jira issues that mention this specific CVE ID in their summary + - Extract the issue IDs (e.g., RHOAIENG-17794, RHOAIENG-16619, etc.) + - Collect all issue IDs for this CVE - Generate comprehensive PR description with: - CVE details and severity - **Test execution results** (from Step 9) @@ -753,6 +780,60 @@ EOF > **END per-repository loop. Return to Step 5 for next repository.** --- +12. **Cleanup Temporary Repositories** + - After all repository/CVE processing completes, remove temporary clone directories created under `/tmp/` + - This cleanup must be non-blocking: log failures and continue final reporting + - Include a summary line showing cleaned vs failed cleanup paths + + **Implementation:** + ```bash + echo "=== Cleaning up temporary repositories ===" + CLEANED_COUNT=0 + FAILED_COUNT=0 + CLEANED_PATHS=() + FAILED_PATHS=() + + # Find all cloned repositories in /tmp with .git directories + for REPO_DIR in /tmp/*/*/.git; do + if [ -d "$REPO_DIR" ]; then + PARENT_DIR=$(dirname "$REPO_DIR") + echo "Removing $PARENT_DIR..." + if rm -rf "$PARENT_DIR" 2>/dev/null; then + CLEANED_COUNT=$((CLEANED_COUNT + 1)) + CLEANED_PATHS+=("$PARENT_DIR") + else + echo " āš ļø Failed to remove $PARENT_DIR" + FAILED_COUNT=$((FAILED_COUNT + 1)) + FAILED_PATHS+=("$PARENT_DIR") + fi + fi + done + + echo "" + echo "Cleanup complete: $CLEANED_COUNT directories removed, $FAILED_COUNT failures" + if [ ${#CLEANED_PATHS[@]} -gt 0 ]; then + echo "Cleaned:" + printf ' - %s\n' "${CLEANED_PATHS[@]}" + fi + if [ ${#FAILED_PATHS[@]} -gt 0 ]; then + echo "Failed to clean:" + printf ' - %s\n' "${FAILED_PATHS[@]}" + fi + ``` + +13. **Prepare for Testing** + - **Note**: Basic automated tests have already been run (see Step 10) + - This section is for additional validation and comprehensive testing + - Stage all remediation changes + - Generate fix summary showing before/after state + - Create test plan focusing on: + - Affected functionality not covered by existing tests + - Breaking changes validation + - Integration testing + - Security verification (re-scan for CVE) + - Manual testing scenarios (if automated tests unavailable) + - Document any known risks or rollback procedures + ## Output - **Fix Implementation Report**: `artifacts/cve-fixer/fixes/fix-implementation-CVE-YYYY-XXXXX.md` - Detailed log of all changes made diff --git a/workflows/cve-fixer/FIELD_REFERENCE.md b/workflows/cve-fixer/FIELD_REFERENCE.md index 292e5654..675bbfd5 100644 --- a/workflows/cve-fixer/FIELD_REFERENCE.md +++ b/workflows/cve-fixer/FIELD_REFERENCE.md @@ -52,12 +52,8 @@ This document provides detailed information about the configuration fields in `. **Current Mappings:** ```json { - "Scan Results": "artifacts/cve-fixer/scans/**/*.json", - "Analysis Reports": "artifacts/cve-fixer/analysis/**/*.md", - "Priority Matrices": "artifacts/cve-fixer/priorities/**/*.md", - "Fix Implementations": "artifacts/cve-fixer/fixes/**/*", - "Verification Results": "artifacts/cve-fixer/verification/**/*.md", - "Remediation Reports": "artifacts/cve-fixer/reports/**/*.md" + "Jira CVE Issues": "artifacts/cve-fixer/find/**/*.md", + "Fix Implementations": "artifacts/cve-fixer/fixes/**/*" } ``` diff --git a/workflows/cve-fixer/README.md b/workflows/cve-fixer/README.md index 13918552..8898df86 100644 --- a/workflows/cve-fixer/README.md +++ b/workflows/cve-fixer/README.md @@ -55,60 +55,24 @@ Find and catalog CVEs that have been reported in Jira for a specific component. **Output:** - `artifacts/cve-fixer/find/cve-issues-[timestamp].md` - List of Jira CVE issues for the component -### Phase 2: Scan -**Command:** `/cve.scan` - -Systematically scan your codebase and dependencies for known CVEs using multiple security tools. This phase creates a comprehensive inventory of all vulnerabilities present in your project. - -**Output:** -- `artifacts/cve-fixer/scans/scan-results-[timestamp].json` - Structured vulnerability data -- `artifacts/cve-fixer/scans/scan-summary-[timestamp].md` - Human-readable scan report - -### Phase 3: Analyze -**Command:** `/cve.analyze` - -Perform deep analysis of discovered CVEs by researching their technical details, reviewing exploit availability, and assessing their real-world impact on your specific deployment. - -**Output:** -- `artifacts/cve-fixer/analysis/cve-analysis-[timestamp].md` - Detailed CVE analysis -- `artifacts/cve-fixer/analysis/exploitability-matrix-[timestamp].md` - Exploitability assessment - -### Phase 4: Prioritize -**Command:** `/cve.prioritize` - -Rank CVEs by combining severity scores with exploitability factors and business context. Create a remediation roadmap that sequences fixes based on risk and effort. - -**Output:** -- `artifacts/cve-fixer/priorities/priority-matrix-[timestamp].md` - Ranked CVE list -- `artifacts/cve-fixer/priorities/remediation-roadmap-[timestamp].md` - Phased fix plan - -### Phase 5: Fix +### Phase 2: Fix **Command:** `/cve.fix` -Implement remediations for prioritized CVEs through dependency updates, code patches, configuration changes, or compensating security controls. +Implement remediations for CVEs discovered in Jira through dependency updates, code patches, or compensating security controls. This command: +- Maps Jira components to GitHub repositories (upstream and downstream) +- Verifies CVE presence with version-matched scanning (GOTOOLCHAIN for Go) +- Checks for existing PRs to avoid duplicates +- Applies fixes automatically (dependency updates, stdlib upgrades, patches) +- Discovers and runs tests before creating PRs +- Creates separate PRs per CVE with comprehensive descriptions +- Handles unmapped components by prompting for repository information **Output:** - `artifacts/cve-fixer/fixes/fix-implementation-[timestamp].md` - Detailed change log - `artifacts/cve-fixer/fixes/fix-summary-[timestamp].md` - Executive summary of fixes +- Pull requests created in target repositories -### Phase 6: Verify -**Command:** `/cve.verify` - -Validate that implemented fixes successfully resolve the targeted CVEs by re-scanning and running comprehensive regression tests. - -**Output:** -- `artifacts/cve-fixer/verification/verification-report-[timestamp].md` - Verification results -- `artifacts/cve-fixer/verification/scan-comparison-[timestamp].md` - Before/after comparison - -### Phase 7: Report -**Command:** `/cve.report` - -Generate comprehensive documentation of the entire remediation process for stakeholders, auditors, and compliance purposes. - -**Output:** -- `artifacts/cve-fixer/reports/executive-summary-[timestamp].md` - High-level summary -- `artifacts/cve-fixer/reports/technical-report-[timestamp].md` - Technical details -- `artifacts/cve-fixer/reports/compliance-report-[timestamp].md` - Audit documentation +**Future Phases:** Additional workflow phases (scan, analyze, prioritize, verify, report) are planned for future releases to provide end-to-end vulnerability management capabilities. ## Available Agents @@ -137,26 +101,13 @@ artifacts/cve-fixer/ ## Example Usage ```bash -# Step 1 (Optional): Find CVEs already reported in Jira +# Step 1: Find CVEs already reported in Jira /cve.find -# Step 2: Scan codebase for CVEs -/cve.scan - -# Step 3: Analyze discovered vulnerabilities -/cve.analyze - -# Step 4: Prioritize CVEs and create roadmap -/cve.prioritize - -# Step 5: Implement fixes for high-priority CVEs +# Step 2: Implement fixes for discovered CVEs /cve.fix -# Step 6: Verify fixes resolve CVEs -/cve.verify - -# Step 7: Generate final reports -/cve.report +# Additional phases (scan, analyze, prioritize, verify, report) coming in future releases ``` ## Configuration @@ -178,22 +129,22 @@ You can customize this workflow by: ## Best Practices -1. **Scan regularly** - Run `/cve.scan` periodically as new CVEs are disclosed daily -2. **Prioritize ruthlessly** - Focus on CVEs with public exploits and high CVSS scores first -3. **Test thoroughly** - Always verify fixes don't break functionality before deploying -4. **Document everything** - Maintain audit trails for compliance and knowledge sharing -5. **Automate where possible** - Integrate scanning into CI/CD pipelines for continuous monitoring +1. **Find regularly** - Run `/cve.find` periodically to track new Jira CVE reports +2. **Test thoroughly** - `/cve.fix` automatically discovers and runs tests before creating PRs +3. **Review PRs carefully** - Each CVE gets its own PR with comprehensive descriptions and test results +4. **Monitor component mappings** - Keep `component-repository-mappings.json` up to date with your repositories +5. **Document everything** - All fixes are tracked in `artifacts/cve-fixer/fixes/` with detailed change logs ## Troubleshooting -**Problem:** Scan finds too many CVEs to address -**Solution:** Use `/cve.prioritize` to focus on critical and high-severity issues first. Address lower-priority CVEs in future sprints. +**Problem:** Component not found in mappings +**Solution:** The workflow will prompt you for repository information. Update `component-repository-mappings.json` for future runs. **Problem:** Dependency update breaks application -**Solution:** Review breaking changes in release notes. May need to update application code to accommodate new API. Consider using minor version updates instead of major versions. +**Solution:** Review breaking changes in the PR description. `/cve.fix` analyzes breaking changes and includes them in PR documentation. Run the test suite locally before merging. **Problem:** CVE has no available fix -**Solution:** Document the vulnerability and implement compensating controls (input validation, access restrictions). Monitor vendor advisories for future patches. +**Solution:** Document the vulnerability in Jira and implement compensating controls (input validation, access restrictions). Monitor vendor advisories for future patches. ## Contributing @@ -211,7 +162,7 @@ MIT For issues or questions: - Open an issue in the repository -- Refer to the ACP documentation for general workflow guidance +- Refer to the [ACP documentation](https://docs.ambient-code.com) --- diff --git a/workflows/cve-fixer/component-repository-mappings.json b/workflows/cve-fixer/component-repository-mappings.json index f32cf458..ffd1d337 100644 --- a/workflows/cve-fixer/component-repository-mappings.json +++ b/workflows/cve-fixer/component-repository-mappings.json @@ -274,12 +274,13 @@ "active_release_branches": [], "branch_strategy": "External dependency - not managed by OpenDataHub", "cve_fix_workflow": { - "primary_target": "N/A - upstream project", + "primary_target": "main", "backport_targets": "N/A", "automation": "N/A", + "excluded_from_automation": true, "manual_intervention": "Monitor upstream releases and update dependency version" }, - "notes": "Third-party dependency managed by Argo project", + "notes": "Third-party dependency managed by Argo project. Excluded from automation - track upstream fixes only.", "github_url": "https://github.com/argoproj/argo-workflows" } } From c1625f20ef454ca5654f0a8a079f7e70d2c3f48b Mon Sep 17 00:00:00 2001 From: Vaishnavi-Modi Date: Fri, 20 Mar 2026 13:10:29 -0400 Subject: [PATCH 08/13] fix: replace fork mappings with original repositories - 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) --- .../component-repository-mappings.json | 29 ++++--------------- 1 file changed, 5 insertions(+), 24 deletions(-) diff --git a/workflows/cve-fixer/component-repository-mappings.json b/workflows/cve-fixer/component-repository-mappings.json index ffd1d337..d218c61e 100644 --- a/workflows/cve-fixer/component-repository-mappings.json +++ b/workflows/cve-fixer/component-repository-mappings.json @@ -43,15 +43,15 @@ }, "Model as a Service": { "container_to_repo_mapping": { - "rhoai/odh-maas-api-rhel9": "angaduom/models-as-a-service" + "rhoai/odh-maas-api-rhel9": "opendatahub-io/models-as-a-service" }, "repositories": { - "angaduom/models-as-a-service": { - "github_url": "https://github.com/angaduom/models-as-a-service", + "opendatahub-io/models-as-a-service": { + "github_url": "https://github.com/opendatahub-io/models-as-a-service", "default_branch": "main", "protected_branches": [], "active_release_branches": [], - "branch_strategy": "Fork of opendatahub-io/models-as-a-service for testing CVE fix workflow.", + "branch_strategy": "TBD - needs investigation", "cve_fix_workflow": { "primary_target": "main", "backport_targets": "TBD", @@ -59,26 +59,7 @@ "manual_intervention": "Unknown" }, "build_location": "maas-api/", - "notes": "Fork of upstream repo. Contains maas-api Go application. Builds using Dockerfile.konflux for Red Hat builds.", - "repo_type": "upstream" - }, - "angaduom/models-as-a-service-downstream": { - "github_url": "https://github.com/angaduom/models-as-a-service-downstream", - "default_branch": "rhoai-3.0", - "protected_branches": [], - "active_release_branches": [ - "rhoai-3.0" - ], - "branch_strategy": "Mirror of red-hat-data-services/models-as-a-service for testing CVE fix workflow.", - "cve_fix_workflow": { - "primary_target": "rhoai-3.0", - "backport_targets": "rhoai-3.0", - "automation": "Manual backport from upstream", - "manual_intervention": "Cherry-pick or re-apply fixes from upstream repo" - }, - "build_location": "maas-api/", - "notes": "Mirror of downstream Red Hat release repo for maas-api. Fixes from upstream should be backported to rhoai-3.0 branch.", - "repo_type": "downstream" + "notes": "Contains maas-api Go application. Builds using Dockerfile.konflux for Red Hat builds." } } }, From c762229c6350f37441df8a27ecaac054e7fd2b1b Mon Sep 17 00:00:00 2001 From: Vaishnavi-Modi Date: Fri, 20 Mar 2026 13:13:51 -0400 Subject: [PATCH 09/13] fix: restore downstream mapping with original repo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- .../component-repository-mappings.json | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/workflows/cve-fixer/component-repository-mappings.json b/workflows/cve-fixer/component-repository-mappings.json index d218c61e..2f72bdc9 100644 --- a/workflows/cve-fixer/component-repository-mappings.json +++ b/workflows/cve-fixer/component-repository-mappings.json @@ -59,7 +59,26 @@ "manual_intervention": "Unknown" }, "build_location": "maas-api/", - "notes": "Contains maas-api Go application. Builds using Dockerfile.konflux for Red Hat builds." + "notes": "Upstream repository. Contains maas-api Go application. Builds using Dockerfile.konflux for Red Hat builds.", + "repo_type": "upstream" + }, + "red-hat-data-services/models-as-a-service": { + "github_url": "https://github.com/red-hat-data-services/models-as-a-service", + "default_branch": "rhoai-3.0", + "protected_branches": [], + "active_release_branches": [ + "rhoai-3.0" + ], + "branch_strategy": "TBD - needs investigation", + "cve_fix_workflow": { + "primary_target": "rhoai-3.0", + "backport_targets": "rhoai-3.0", + "automation": "Manual backport from upstream", + "manual_intervention": "Cherry-pick or re-apply fixes from upstream repo" + }, + "build_location": "maas-api/", + "notes": "Downstream Red Hat release repository for maas-api. Fixes from upstream should be backported to rhoai-3.0 branch.", + "repo_type": "downstream" } } }, From 2c648913ee3c9b866acc6f84bddf92cfc9289528 Mon Sep 17 00:00:00 2001 From: Vaishnavi-Modi Date: Fri, 20 Mar 2026 15:39:45 -0400 Subject: [PATCH 10/13] fix: update JQL query to use wildcard pattern for CVE search 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) --- workflows/cve-fixer/.claude/commands/cve.find.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/workflows/cve-fixer/.claude/commands/cve.find.md b/workflows/cve-fixer/.claude/commands/cve.find.md index b7e33912..013db7d8 100644 --- a/workflows/cve-fixer/.claude/commands/cve.find.md +++ b/workflows/cve-fixer/.claude/commands/cve.find.md @@ -100,7 +100,7 @@ Report: artifacts/cve-fixer/find/cve-issues-20260226-145018.md b. Construct JQL query and execute API call: ```bash # Build JQL query - JQL="project = RHOAIENG AND component = \"${COMPONENT_NAME}\" AND summary ~ \"CVE\"" + JQL="project = RHOAIENG AND component = \"${COMPONENT_NAME}\" AND summary ~ \"CVE*\"" # Append resolved filter if --ignore-resolved flag was provided if [ "$IGNORE_RESOLVED" = "true" ]; then @@ -332,7 +332,7 @@ Report: artifacts/cve-fixer/find/cve-issues-20260226-145018.md **Ignored Issues:** ${IGNORED_COUNT} ## Query Parameters - - **JQL Query:** project = RHOAIENG AND component = "${COMPONENT_NAME}" AND summary ~ "CVE"$( [ "$IGNORE_RESOLVED" = "true" ] && echo ' AND status not in ("Resolved")' ) + - **JQL Query:** project = RHOAIENG AND component = "${COMPONENT_NAME}" AND summary ~ "CVE*"$( [ "$IGNORE_RESOLVED" = "true" ] && echo ' AND status not in ("Resolved")' ) - **Columns:** KEY, SUMMARY, STATUS, PRIORITY, CREATED, COMPONENTS - **Jira Instance:** ${JIRA_BASE_URL} From 863c977134902cc636af7c54d13bedcc965aca5a Mon Sep 17 00:00:00 2001 From: Vaishnavi-Modi Date: Mon, 23 Mar 2026 10:37:08 -0400 Subject: [PATCH 11/13] feat: add CLAUDE.md with comprehensive safety guardrails 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) --- workflows/cve-fixer/.claude/CLAUDE.md | 264 ++++++++++++++++++++++++++ 1 file changed, 264 insertions(+) create mode 100644 workflows/cve-fixer/.claude/CLAUDE.md diff --git a/workflows/cve-fixer/.claude/CLAUDE.md b/workflows/cve-fixer/.claude/CLAUDE.md new file mode 100644 index 00000000..e67d6486 --- /dev/null +++ b/workflows/cve-fixer/.claude/CLAUDE.md @@ -0,0 +1,264 @@ +# CVE Fixer Workflow - Agent Guidelines + +This document defines the rules, guardrails, and quality standards for the CVE Fixer workflow. These are **hard requirements** that must be followed in all workflow executions. + +--- + +## 🚫 Hard Limits (NEVER Do These) + +### Git Operations +- **NEVER force-push** (`git push --force`, `git push -f`, `git push --force-with-lease`) +- **NEVER commit directly to protected branches** (main, master, rhoai-release, odh-release, or any branch listed in `protected_branches`) +- **NEVER skip git hooks** (`--no-verify`, `--no-gpg-sign`, `-n`) +- **NEVER use `git reset --hard`** without explicit user instruction +- **NEVER delete remote branches** without explicit user instruction +- **NEVER modify git history** of pushed commits (no rebase, amend, or squash on pushed branches) + +### Branch Safety +- **ALWAYS create feature branches** for fixes (never work directly on main/protected branches) +- **ALWAYS check if branch is protected** before pushing +- **ALWAYS use branch naming convention**: `fix/cve-YYYY-XXXXX--attempt-N` + +### Pull Request Creation +- **ALWAYS create separate PRs** for each CVE (never combine multiple CVE fixes in one PR) +- **ALWAYS verify CVE exists** in vulnerability scan before creating PR (skip if already fixed) +- **ALWAYS check for existing open PRs** before creating new ones (prevent duplicates) +- **ALWAYS include test results** in PR description (even if tests failed or couldn't run) + +### Repository Operations +- **NEVER delete user's working directories** or files +- **ONLY clone repositories to `/tmp`** (never to user's workspace) +- **ALWAYS clean up `/tmp` clones** after workflow completes +- **NEVER run `rm -rf` on paths outside `/tmp`** (this is denied in settings.json) + +--- + +## āœ… Safety Rules + +### Before Making Changes +1. **Verify CVE presence** - Always scan the repository to confirm the CVE actually exists before attempting to fix it +2. **Check for existing work** - Look for open PRs that might already address the CVE +3. **Understand the scope** - Know which repositories need fixes (upstream, downstream, or both) +4. **Validate mappings** - If component not in mapping file, ask user for repository information + +### During Execution +1. **Use version-matched scanning** - For Go projects, use `GOTOOLCHAIN` to match repository's Go version +2. **Test before PR** - Always attempt to discover and run tests before creating PRs +3. **Document everything** - Save detailed reports to `artifacts/cve-fixer/` for every action +4. **Handle errors gracefully** - If a step fails, document the failure and continue with next CVE/repo + +### Communication +1. **Be concise** - Brief status updates during execution, detailed summary at end +2. **Show confidence** - Indicate certainty level when making recommendations +3. **Full URLs in output** - Always print complete PR URLs (e.g., `https://github.com/org/repo/pull/123`) +4. **Clear status indicators** - Use āœ…, āŒ, āš ļø to show success, failure, warnings + +--- + +## šŸ“‹ Quality Standards + +### Test Execution +**Required:** Always attempt to discover and run tests before creating PRs. + +**Test Failure Handling:** +- **If tests PASS** āœ… - Proceed with PR creation, note success in PR description +- **If tests FAIL** āŒ - **Still create PR** but: + - Include failure details in PR description + - Add warning labels if possible + - Document which tests failed and error messages + - Include note: "āš ļø Tests failed - manual review required before merge" +- **If NO tests found** āš ļø - Create PR with note: "No automated tests discovered - manual testing required" +- **If tests can't run** āš ļø - Create PR with error details: "Tests could not execute: [reason]" + +**Rationale:** PRs should always be created so users can review the fix. Test failures don't mean the fix is wrong - they provide valuable information for review. + +### Commit Standards +- **Use conventional commits** format: + ``` + fix(cve): CVE-YYYY-XXXXX - + + - Update from X.X.X to Y.Y.Y + - Addresses vulnerability in + - Breaking changes: [if any] + + Resolves: JIRA-123, JIRA-456 + ``` +- **Include Co-authored-by** for AI assistance: + ``` + Co-Authored-By: Claude Sonnet 4.6 (1M context) + ``` + +### PR Description Standards +Every PR must include: +1. **CVE Details** - ID, severity, CVSS score, affected versions +2. **Fix Summary** - What changed and why +3. **Test Results** - Status, command, output (even if failed) +4. **Breaking Changes** - Analysis of compatibility impacts +5. **Jira References** - Plain text issue IDs (no hyperlinks) +6. **Verification Steps** - Checklist for reviewers +7. **Risk Assessment** - Low/Medium/High with justification + +--- + +## šŸŽÆ Workflow-Specific Rules + +### CVE Verification +1. **Never apply fixes blindly** - Always verify CVE exists in current code +2. **Use correct toolchain** - For Go stdlib CVEs, use `GOTOOLCHAIN=go` matching repo's go.mod +3. **Check all repositories** - If component maps to multiple repos, verify and fix each independently + +### Duplicate Prevention +1. **Check for existing PRs** with: `gh pr list --repo X --state open --base Y --search "CVE-ID"` +2. **Use `--base` parameter** to scope search to specific target branch +3. **Skip if PR exists** - Document in `artifacts/cve-fixer/fixes/existing-pr-CVE-*.md` + +### Multi-Repository Handling +1. **Process independently** - Each repository gets its own clone, branch, scan, fix, test, and PR +2. **Upstream first** - When fixing both upstream and downstream, process upstream first +3. **Track separately** - Maintain separate artifacts and reports per repository + +### Ignored CVEs +Respect ignore patterns in Jira comments: +- `cve-automation-ignore` +- `skip-cve-automation` +- `ignore-cve-automation` +- `automation-ignore-cve` + +When found, skip the CVE and document why. + +--- + +## šŸ”§ Automation Support + +### Scheduled/Unattended Runs +This workflow is designed for scheduled automation (GitHub Actions, Ambient scheduled sessions). + +**Automation Mode Behavior:** +- āœ… Automatic PR creation (no manual approval gates) +- āœ… Continue on test failures (create PR anyway with failure details) +- āœ… Skip CVEs that are already fixed or have open PRs +- āœ… Save all output to artifacts (user reviews PRs, not logs) +- āœ… Clean up `/tmp` after completion + +**Why No Manual Approval Gate:** +1. PRs are the review mechanism - user approves by merging +2. Scheduled runs require unattended operation +3. Bad PRs can simply be closed (no harm done) +4. All context is in PR description for informed review + +### Interactive Runs +When user runs `/cve.fix` interactively: +- āœ… Same behavior as automated runs (consistency) +- āœ… Print PR URLs to console for easy access +- āœ… Show summary of created/skipped PRs +- āœ… User can review PRs immediately if desired + +--- + +## šŸ“Š Success Criteria + +A successful workflow execution means: + +1. āœ… **All CVEs processed** - None skipped due to errors +2. āœ… **Correct PRs created** - Only for CVEs that actually exist and have no open PRs +3. āœ… **Tests attempted** - Even if they fail, results are documented +4. āœ… **Complete artifacts** - Reports saved for every action taken +5. āœ… **Clean state** - `/tmp` directories cleaned up +6. āœ… **Clear output** - User knows exactly what happened and what to review + +--- + +## šŸ›”ļø Error Handling + +### When Things Go Wrong + +**Git authentication fails:** +- Try all available credential methods (gh auth, GITHUB_TOKEN, SSH) +- If all fail, document the issue and skip that repository +- DO NOT prompt for credentials in automated runs + +**CVE scan fails:** +- Document the error in artifacts +- Skip that CVE/repository +- Continue with next CVE + +**Test execution fails:** +- Document test failures +- **Create PR anyway** with failure details +- Let user decide whether to merge + +**PR creation fails:** +- Log the error +- Save the branch name and changes +- Include in final summary so user can manually create PR + +**Component not in mapping:** +- In interactive mode: Ask user for repository information +- In automated mode: Skip and document in artifacts + +--- + +## šŸ“ Documentation Requirements + +### Artifacts to Generate +For every CVE processed, create: + +1. **Fix implementation report** - `artifacts/cve-fixer/fixes/fix-implementation-CVE-*.md` + - Changes made + - Test results + - PR URL + +2. **Already-fixed report** - `artifacts/cve-fixer/fixes/already-fixed-CVE-*.md` (if CVE not present) + - Scan results + - Timestamp + +3. **Existing PR report** - `artifacts/cve-fixer/fixes/existing-pr-CVE-*.md` (if duplicate) + - Existing PR details + - Timestamp + +4. **PR summary** - `artifacts/cve-fixer/fixes/pr-creation-summary.md` + - All PRs created + - All CVEs skipped (with reasons) + - Test status per PR + +--- + +## šŸ” Review Checklist + +Before completing workflow execution, verify: + +- [ ] No force-pushes attempted +- [ ] No direct commits to protected branches +- [ ] All feature branches follow naming convention +- [ ] Every CVE has corresponding artifact (fix/skip/existing) +- [ ] Test results documented (pass/fail/none/error) +- [ ] PRs created only for actual fixes (not already-fixed CVEs) +- [ ] No duplicate PRs created +- [ ] `/tmp` directories cleaned up +- [ ] Final summary shows all PR URLs +- [ ] Conventional commit format used +- [ ] PR descriptions include all required sections + +--- + +## šŸŽ“ Learning from the Bugfix Workflow + +This workflow draws inspiration from the [bugfix workflow](https://github.com/ambient-code/workflows/tree/main/workflows/bugfix) best practices: + +- Clear hard limits on dangerous operations +- Safety-first approach to git operations +- Comprehensive documentation requirements +- Support for both interactive and automated execution +- Quality standards that don't block automation + +--- + +## šŸ“ž Questions or Issues? + +If you encounter situations not covered by these guidelines: +1. Err on the side of caution +2. Document the situation in artifacts +3. Continue with other CVEs +4. Include the question in the final summary + +**Remember:** The goal is to help users remediate CVEs safely and efficiently, whether running interactively or on a schedule. When in doubt, create the PR with all available information and let the user decide. From ac491488ac13fcf584e2a8b81ca7b2c89098027e Mon Sep 17 00:00:00 2001 From: Vaishnavi-Modi Date: Tue, 24 Mar 2026 15:34:43 -0400 Subject: [PATCH 12/13] docs: clarify workflow handles ProdSec-reported CVE issues --- workflows/cve-fixer/.ambient/ambient.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/workflows/cve-fixer/.ambient/ambient.json b/workflows/cve-fixer/.ambient/ambient.json index 4535fc66..3d1d324d 100644 --- a/workflows/cve-fixer/.ambient/ambient.json +++ b/workflows/cve-fixer/.ambient/ambient.json @@ -1,8 +1,8 @@ { "name": "CVE Fixer", - "description": "This workflow can be used to scan your code base for CVEs and fix discovered CVEs", - "systemPrompt": "You are a CVE remediation assistant for the Ambient Code Platform. Your role is to guide users through discovering CVEs in Jira and systematically fixing them using a structured, security-focused approach.\n\nKEY RESPONSIBILITIES:\n- Guide users through the CVE remediation workflow\n- Execute slash commands to perform specific security tasks\n- Identify and analyze vulnerabilities reported in Jira\n- Implement secure fixes that resolve vulnerabilities without breaking functionality\n- Verify that remediations effectively address the identified CVEs\n\nWORKFLOW METHODOLOGY:\n1. FIND - Find CVEs already reported in Jira for a component\n2. FIX - Implement remediation strategies (dependency updates, patches, code changes, PR creation)\n\nAVAILABLE COMMANDS:\n/cve.find - Find CVEs reported in Jira for a specific component\n/cve.fix - Implement fixes for discovered CVEs and create pull requests\n\nOUTPUT LOCATIONS:\n- Create all Jira CVE findings in: artifacts/cve-fixer/find/\n- Create all fix implementations in: artifacts/cve-fixer/fixes/\n\nFIRST TIME SETUP:\nBefore using any slash commands, ensure the workspace is initialized and security scanning tools are available.", - "startupPrompt": "Welcome! I'm your CVE Remediation assistant.\n\nšŸŽÆ WHAT I DO:\nI help you discover CVEs reported in Jira and guide you through fixing them securely and efficiently by creating pull requests with dependency updates, patches, and code changes.\n\nšŸ“‹ WORKFLOW PHASES:\n1. **Find** - Discover CVEs already reported in Jira for a component\n2. **Fix** - Implement secure remediations and create pull requests\n\nšŸš€ AVAILABLE COMMANDS:\n/cve.find - Find CVEs already reported in Jira\n/cve.fix - Implement security fixes and create PRs\n\nšŸ’” GETTING STARTED:\nRun /cve.find to discover CVEs already in Jira for a specific component, then use /cve.fix to automatically remediate them.\n\nWhat would you like to accomplish today?", + "description": "Automate remediation of CVE issues reported by ProdSec team in Jira by creating pull requests with dependency updates and patches", + "systemPrompt": "You are a CVE remediation assistant for the Ambient Code Platform. Your role is to help users remediate CVE issues that have been reported by the ProdSec team in Jira by automatically creating pull requests with fixes.\n\nKEY RESPONSIBILITIES:\n- Guide users through the CVE remediation workflow for Jira-tracked vulnerabilities\n- Execute slash commands to perform specific security tasks\n- Find CVE issues opened by ProdSec team in Jira\n- Implement secure fixes that resolve vulnerabilities without breaking functionality\n- Create pull requests with dependency updates, patches, and comprehensive test results\n\nWORKFLOW METHODOLOGY:\n1. FIND - Find CVEs already reported in Jira for a component\n2. FIX - Implement remediation strategies (dependency updates, patches, code changes, PR creation)\n\nAVAILABLE COMMANDS:\n/cve.find - Find CVEs reported in Jira for a specific component\n/cve.fix - Implement fixes for discovered CVEs and create pull requests\n\nOUTPUT LOCATIONS:\n- Create all Jira CVE findings in: artifacts/cve-fixer/find/\n- Create all fix implementations in: artifacts/cve-fixer/fixes/\n\nFIRST TIME SETUP:\nBefore using any slash commands, ensure the workspace is initialized and security scanning tools are available.", + "startupPrompt": "Welcome! I'm your CVE Remediation assistant.\n\nšŸŽÆ WHAT I DO:\nI help you remediate CVE issues reported by the ProdSec team in Jira by automatically creating pull requests with dependency updates, patches, and code changes.\n\nšŸ“‹ WORKFLOW PHASES:\n1. **Find** - Discover CVE issues opened by ProdSec in Jira for a component\n2. **Fix** - Implement secure remediations and create pull requests\n\nšŸš€ AVAILABLE COMMANDS:\n/cve.find - Find CVE issues reported by ProdSec in Jira\n/cve.fix - Implement security fixes and create PRs\n\nšŸ’” GETTING STARTED:\nRun /cve.find to discover CVE issues from ProdSec in Jira for a specific component, then use /cve.fix to automatically remediate them with pull requests.\n\n**Note:** This workflow is designed for CVE issues tracked in Jira by your Product Security team.\n\nWhat would you like to accomplish today?", "results": { "Jira CVE Issues": "artifacts/cve-fixer/find/**/*.md", "Fix Implementations": "artifacts/cve-fixer/fixes/**/*" From 9f454183c7c18d8802c31a1a27e47ccea2c072f3 Mon Sep 17 00:00:00 2001 From: Vaishnavi-Modi Date: Tue, 24 Mar 2026 15:42:31 -0400 Subject: [PATCH 13/13] docs: cleanup README and FIELD_REFERENCE to remove unimplemented features --- workflows/cve-fixer/FIELD_REFERENCE.md | 48 +---- workflows/cve-fixer/README.md | 247 ++++++++++++++++--------- 2 files changed, 167 insertions(+), 128 deletions(-) diff --git a/workflows/cve-fixer/FIELD_REFERENCE.md b/workflows/cve-fixer/FIELD_REFERENCE.md index 675bbfd5..e45dc14c 100644 --- a/workflows/cve-fixer/FIELD_REFERENCE.md +++ b/workflows/cve-fixer/FIELD_REFERENCE.md @@ -13,7 +13,7 @@ This document provides detailed information about the configuration fields in `. ### description - **Type:** string - **Purpose:** Explains workflow purpose in UI -- **Current Value:** "This workflow can be used to scan your code base for CVEs and fix discovered CVEs" +- **Current Value:** "Automate remediation of CVE issues reported by ProdSec team in Jira by creating pull requests with dependency updates and patches" - **Guidelines:** 1-3 sentences, clear and specific about workflow capabilities ### systemPrompt @@ -81,17 +81,13 @@ This document provides detailed information about the configuration fields in `. ### Adding a new output type -If you want to track additional artifacts (e.g., "Remediation Scripts"): +If you want to track additional artifacts (e.g., "Test Results"): ```json "results": { - "Scan Results": "artifacts/cve-fixer/scans/**/*.json", - "Analysis Reports": "artifacts/cve-fixer/analysis/**/*.md", - "Priority Matrices": "artifacts/cve-fixer/priorities/**/*.md", + "Jira CVE Issues": "artifacts/cve-fixer/find/**/*.md", "Fix Implementations": "artifacts/cve-fixer/fixes/**/*", - "Verification Results": "artifacts/cve-fixer/verification/**/*.md", - "Remediation Reports": "artifacts/cve-fixer/reports/**/*.md", - "Remediation Scripts": "artifacts/cve-fixer/scripts/**/*.sh" + "Test Results": "artifacts/cve-fixer/tests/**/*.log" } ``` @@ -102,15 +98,15 @@ To use a different base directory (e.g., `security-artifacts/`): 1. Update `systemPrompt` OUTPUT LOCATIONS section: ``` OUTPUT LOCATIONS: -- Create all scan results in: security-artifacts/cve-fixer/scans/ -... +- Create all Jira CVE findings in: security-artifacts/cve-fixer/find/ +- Create all fix implementations in: security-artifacts/cve-fixer/fixes/ ``` 2. Update `results` paths: ```json "results": { - "Scan Results": "security-artifacts/cve-fixer/scans/**/*.json", - ... + "Jira CVE Issues": "security-artifacts/cve-fixer/find/**/*.md", + "Fix Implementations": "security-artifacts/cve-fixer/fixes/**/*" } ``` @@ -139,25 +135,6 @@ KEY RESPONSIBILITIES: ... ``` -## Agent Files - -Agent persona files are located in `.claude/agents/` and follow this structure: - -```markdown -# {Name} - {Role} -## Role -## Expertise -## Responsibilities -## Communication Style -## When to Invoke -## Tools and Techniques -## Key Principles -## Example Artifacts -``` - -**Current Agent:** -- `vera-security-engineer.md` - Security engineering specialist for CVE remediation - ## Command Files Slash command files are located in `.claude/commands/` and follow this structure: @@ -181,8 +158,7 @@ Slash command files are located in `.claude/commands/` and follow this structure ## File Naming Conventions - **Workflow directory:** `workflows/cve-fixer/` -- **Agent files:** `{name}-{role}.md` (e.g., `vera-security-engineer.md`) -- **Command files:** `{workflow-prefix}.{phase}.md` (e.g., `cve.scan.md`) +- **Command files:** `{workflow-prefix}.{phase}.md` (e.g., `cve.find.md`, `cve.fix.md`) - **Artifacts:** `artifacts/cve-fixer/{category}/{files}` ## Validation Checklist @@ -191,11 +167,10 @@ Before using this workflow, verify: - [ ] `.ambient/ambient.json` is valid JSON (no comments, no trailing commas) - [ ] All required fields (name, description, systemPrompt, startupPrompt) are present -- [ ] All agent files follow the template structure with required sections - [ ] All command files have unique names and follow naming convention - [ ] Output paths in `results` match those referenced in `systemPrompt` - [ ] Output paths in `results` match those in command files' ## Output sections -- [ ] README.md accurately describes the workflow and all phases +- [ ] README.md accurately describes the workflow and implemented commands - [ ] All file references use correct absolute or relative paths ## Configuration Best Practices @@ -221,9 +196,6 @@ Before using this workflow, verify: **Problem:** Commands don't appear **Solution:** Ensure command files are in `.claude/commands/` and follow naming convention `{prefix}.{phase}.md`. -**Problem:** Agent isn't being invoked -**Solution:** Check that agent file exists in `.claude/agents/` and is referenced in workflow context. - ## References - [ACP Documentation](https://ambient-code.github.io/vteam) diff --git a/workflows/cve-fixer/README.md b/workflows/cve-fixer/README.md index 8898df86..04b58855 100644 --- a/workflows/cve-fixer/README.md +++ b/workflows/cve-fixer/README.md @@ -1,87 +1,128 @@ # CVE Fixer -A comprehensive workflow for scanning codebases for Common Vulnerabilities and Exposures (CVEs), analyzing their impact, and systematically fixing them with verified remediations. +Automate remediation of CVE issues reported by ProdSec team in Jira by creating pull requests with dependency updates and patches. ## Overview -This workflow guides you through CVE remediation using a structured approach that ensures thorough vulnerability management: +This workflow helps you remediate CVE vulnerabilities that have been reported by your Product Security team in Jira. It provides automated tools to: -### 1. Find (Optional) -Discover CVEs already reported in your Jira issue tracking system for a specific component, helping you track existing vulnerability reports. +1. **Find** - Discover CVE issues already reported in Jira for a specific component +2. **Fix** - Implement remediations and create pull requests automatically -### 2. Scan -Discover CVEs in your codebase and dependencies using multiple security scanning tools, creating a complete vulnerability inventory. +The workflow is designed for both interactive use and scheduled automation (GitHub Actions, Ambient scheduled sessions). -### 3. Analyze -Deep-dive into each discovered CVE to understand its technical details, real-world exploitability, and contextual impact on your specific environment. - -### 4. Prioritize -Rank CVEs by risk level considering severity, exploitability, and business impact, then create a phased remediation roadmap. +## Getting Started -### 5. Fix -Implement secure remediations through dependency updates, code patches, or compensating controls following the prioritized plan. +### Prerequisites -### 6. Verify -Validate that fixes successfully resolve CVEs without introducing regressions through re-scanning and comprehensive testing. +- **Required:** + - JIRA_API_TOKEN environment variable (for Jira access) + - JIRA_EMAIL environment variable (your Jira account email) + - Git and GitHub CLI (`gh`) installed + - Component-to-repository mapping configured in `component-repository-mappings.json` -### 7. Report -Generate audit-ready documentation showing the complete remediation process, security improvements, and compliance evidence. +- **Optional:** + - Test suite for regression testing (workflow auto-discovers and runs tests) + - Security scanning tools (govulncheck, npm audit, etc. - auto-installed when needed) -## Getting Started +### Installation -### Prerequisites -- Project with dependency manifest files (package.json, requirements.txt, pom.xml, etc.) -- Security scanning tools (npm audit, pip-audit, Snyk, or similar) -- Test suite for regression testing -- Git for version control -- (Optional) Jira MCP server or JIRA_API_TOKEN for finding CVEs in Jira +1. Load this workflow in your ACP session +2. Set up Jira credentials: + ```bash + export JIRA_API_TOKEN="your-token-here" + export JIRA_EMAIL="your-email@redhat.com" + ``` +3. Run `/cve.find` to discover CVE issues from Jira -### Installation -1. Clone this workflow repository -2. Load the workflow in your ACP session -3. Run `/cve.scan` to initialize and discover vulnerabilities +## Available Commands -## Workflow Phases +### `/cve.find` - Find CVEs in Jira -### Phase 1: Find (Optional) -**Command:** `/cve.find` +Discover and catalog CVEs that have been reported by ProdSec team in Jira for a specific component. -Find and catalog CVEs that have been reported in Jira for a specific component. This optional phase helps you discover existing vulnerability reports in your issue tracking system before or alongside code scanning. +**Usage:** +```bash +/cve.find # Will prompt for component name +/cve.find backend-api # Find CVEs for specific component +/cve.find backend-api --ignore-resolved # Exclude resolved issues +``` **Prerequisites:** -- JIRA_API_TOKEN environment variable +- JIRA_API_TOKEN and JIRA_EMAIL environment variables - Access to Jira instance (https://redhat.atlassian.net) +- jq installed (for JSON parsing) **Output:** -- `artifacts/cve-fixer/find/cve-issues-[timestamp].md` - List of Jira CVE issues for the component +- `artifacts/cve-fixer/find/cve-issues-[timestamp].md` - List of Jira CVE issues with metadata -### Phase 2: Fix -**Command:** `/cve.fix` +**Features:** +- Automatically filters out issues marked with ignore patterns in comments +- Supports pagination for components with many CVEs +- Extracts issue metadata (summary, status, priority, created date) +- Groups results by status and priority -Implement remediations for CVEs discovered in Jira through dependency updates, code patches, or compensating security controls. This command: -- Maps Jira components to GitHub repositories (upstream and downstream) -- Verifies CVE presence with version-matched scanning (GOTOOLCHAIN for Go) -- Checks for existing PRs to avoid duplicates -- Applies fixes automatically (dependency updates, stdlib upgrades, patches) -- Discovers and runs tests before creating PRs -- Creates separate PRs per CVE with comprehensive descriptions -- Handles unmapped components by prompting for repository information +### `/cve.fix` - Implement CVE Fixes -**Output:** -- `artifacts/cve-fixer/fixes/fix-implementation-[timestamp].md` - Detailed change log -- `artifacts/cve-fixer/fixes/fix-summary-[timestamp].md` - Executive summary of fixes -- Pull requests created in target repositories +Implement remediations for CVEs discovered in Jira by creating pull requests with fixes. -**Future Phases:** Additional workflow phases (scan, analyze, prioritize, verify, report) are planned for future releases to provide end-to-end vulnerability management capabilities. +**Usage:** +```bash +/cve.fix # Will prompt for component and Jira issues +/cve.fix RHOAIENG-12345 # Fix specific Jira issue +/cve.fix RHOAIENG-12345,RHOAIENG-12346 # Fix multiple issues +``` -## Available Agents +**What it does:** +1. Maps Jira components to GitHub repositories (upstream/downstream) +2. Clones repositories to `/tmp` (keeps your workspace clean) +3. Verifies CVE presence with version-matched scanning (GOTOOLCHAIN for Go) +4. Checks for existing PRs to avoid duplicates +5. Applies fixes automatically (dependency updates, stdlib upgrades, patches) +6. Discovers and runs tests before creating PRs +7. Creates separate PRs per CVE with comprehensive descriptions +8. Cleans up `/tmp` clones after completion -This workflow includes the following expert agent: +**Output:** +- `artifacts/cve-fixer/fixes/fix-implementation-CVE-*.md` - Detailed change logs +- `artifacts/cve-fixer/fixes/pr-creation-summary.md` - Executive summary +- Pull requests created in target repositories -### Vera - Security Engineer -Security engineering specialist focused on vulnerability assessment and CVE remediation. +**Features:** +- **Multi-repository support** - Handles upstream and downstream repos independently +- **Test execution** - Auto-discovers and runs tests (creates PR even if tests fail) +- **Duplicate prevention** - Checks for existing PRs before creating new ones +- **CVE verification** - Only fixes CVEs that actually exist in current code +- **Breaking change analysis** - Documents compatibility impacts in PR description +- **Conventional commits** - Uses standardized commit message format +- **Safety guardrails** - Never force-pushes or commits to protected branches + +## Component-to-Repository Mapping + +The workflow uses `component-repository-mappings.json` to map Jira components to GitHub repositories. Example: + +```json +{ + "Model as a Service": { + "repositories": { + "opendatahub-io/models-as-a-service": { + "github_url": "https://github.com/opendatahub-io/models-as-a-service", + "repo_type": "upstream", + "primary_target": "main", + "build_location": "." + }, + "red-hat-data-services/models-as-a-service": { + "github_url": "https://github.com/red-hat-data-services/models-as-a-service", + "repo_type": "downstream", + "primary_target": "rhoai-2.19", + "build_location": "." + } + } + } +} +``` -**Expertise:** CVE analysis, security scanning tools, secure coding practices, dependency management, exploit assessment +If a component is not mapped, the workflow will prompt you for repository information interactively. ## Output Artifacts @@ -89,69 +130,95 @@ All workflow outputs are saved in the `artifacts/cve-fixer/` directory: ``` artifacts/cve-fixer/ -ā”œā”€ā”€ find/ # Jira CVE issues found for components (optional) -ā”œā”€ā”€ scans/ # Vulnerability scan results and summaries -ā”œā”€ā”€ analysis/ # Detailed CVE analysis and exploitability assessments -ā”œā”€ā”€ priorities/ # Priority matrices and remediation roadmaps -ā”œā”€ā”€ fixes/ # Fix implementations and change logs -ā”œā”€ā”€ verification/ # Verification results and scan comparisons -└── reports/ # Final remediation reports and compliance docs +ā”œā”€ā”€ find/ # Jira CVE issues found for components +└── fixes/ # Fix implementations, test results, and PR summaries ``` -## Example Usage +## Example Workflow ```bash -# Step 1: Find CVEs already reported in Jira -/cve.find +# Step 1: Find CVEs reported by ProdSec in Jira +/cve.find backend-api -# Step 2: Implement fixes for discovered CVEs -/cve.fix +# Step 2: Review the discovered issues in artifacts/cve-fixer/find/ -# Additional phases (scan, analyze, prioritize, verify, report) coming in future releases +# Step 3: Implement fixes and create PRs +/cve.fix RHOAIENG-12345,RHOAIENG-12346 + +# Step 4: Review the created PRs and merge when ready ``` -## Configuration +## Automation Support -This workflow is configured via `.ambient/ambient.json`. Key settings: +This workflow is designed for both interactive and automated use: -- **Name:** CVE Fixer -- **Description:** Scan and fix CVEs in your codebase -- **Artifact Path:** `artifacts/cve-fixer/` +### Scheduled Runs (GitHub Actions, Ambient Sessions) +- āœ… Runs fully unattended (no manual approval gates) +- āœ… Creates PRs automatically with comprehensive context +- āœ… Continues on test failures (documents failures in PR) +- āœ… Skips already-fixed CVEs and duplicate PRs +- āœ… Cleans up temporary files automatically + +### Interactive Runs +- āœ… Same behavior as automated runs (consistency) +- āœ… Prints PR URLs to console for easy access +- āœ… Shows summary of created/skipped PRs + +**Why no manual approval?** PRs are the review mechanism - you approve by merging. Bad PRs can simply be closed with no harm done. -## Customization +## Safety Guardrails -You can customize this workflow by: +See `.claude/CLAUDE.md` for comprehensive safety rules. Key protections: -1. **Modifying the agent:** Edit `vera-security-engineer.md` to adjust expertise or communication style -2. **Adding commands:** Create new command files in `.claude/commands/` for additional workflow steps -3. **Adjusting configuration:** Update `.ambient/ambient.json` to change prompts or output locations -4. **Changing output paths:** Modify the `results` section in config to organize artifacts differently +- **Never force-pushes** or modifies git history +- **Never commits to protected branches** (main, master, release branches) +- **Only clones to `/tmp`** (never touches your workspace) +- **Always verifies CVE exists** before creating PR +- **Always checks for duplicate PRs** before creating new ones +- **Always attempts test execution** before creating PR +- **Respects ignore patterns** in Jira comments ## Best Practices -1. **Find regularly** - Run `/cve.find` periodically to track new Jira CVE reports -2. **Test thoroughly** - `/cve.fix` automatically discovers and runs tests before creating PRs -3. **Review PRs carefully** - Each CVE gets its own PR with comprehensive descriptions and test results -4. **Monitor component mappings** - Keep `component-repository-mappings.json` up to date with your repositories -5. **Document everything** - All fixes are tracked in `artifacts/cve-fixer/fixes/` with detailed change logs +1. **Run /cve.find periodically** - Track new Jira CVE reports from ProdSec +2. **Review PRs carefully** - Each PR includes test results and breaking change analysis +3. **Keep mappings updated** - Maintain `component-repository-mappings.json` with your repositories +4. **Monitor for ignored CVEs** - Workflow respects ignore patterns like `cve-automation-ignore` +5. **Check artifacts** - All actions are logged in `artifacts/cve-fixer/` for transparency ## Troubleshooting +**Problem:** JIRA_API_TOKEN not set +**Solution:** Generate token at https://id.atlassian.com/manage-profile/security/api-tokens and export it + **Problem:** Component not found in mappings -**Solution:** The workflow will prompt you for repository information. Update `component-repository-mappings.json` for future runs. +**Solution:** Workflow will prompt for repository info. Update `component-repository-mappings.json` for future runs + +**Problem:** Tests fail in created PR +**Solution:** This is expected behavior - PR is created with failure details so you can review the fix manually -**Problem:** Dependency update breaks application -**Solution:** Review breaking changes in the PR description. `/cve.fix` analyzes breaking changes and includes them in PR documentation. Run the test suite locally before merging. +**Problem:** CVE not present in scan +**Solution:** Workflow automatically skips and documents in `artifacts/cve-fixer/fixes/already-fixed-CVE-*.md` -**Problem:** CVE has no available fix -**Solution:** Document the vulnerability in Jira and implement compensating controls (input validation, access restrictions). Monitor vendor advisories for future patches. +**Problem:** Duplicate PR exists +**Solution:** Workflow automatically detects and skips, documented in `artifacts/cve-fixer/fixes/existing-pr-CVE-*.md` + +## Configuration + +The workflow is configured via `.ambient/ambient.json`: + +- **Name:** CVE Fixer +- **Description:** Automate remediation of CVE issues reported by ProdSec team in Jira +- **Artifact Paths:** + - Jira CVE Issues: `artifacts/cve-fixer/find/**/*.md` + - Fix Implementations: `artifacts/cve-fixer/fixes/**/*` ## Contributing To improve this workflow: 1. Fork the repository 2. Make your changes -3. Test thoroughly +3. Test thoroughly with real CVEs 4. Submit a pull request ## License @@ -167,5 +234,5 @@ For issues or questions: --- **Created with:** ACP Workflow Creator -**Workflow Type:** Custom +**Workflow Type:** CVE Remediation **Version:** 1.0.0