From 8af42d0a4d5c32df5f93a468d2df826a6022faec Mon Sep 17 00:00:00 2001 From: Gabriel Bernal Date: Mon, 29 Jun 2026 22:38:36 +0200 Subject: [PATCH 1/3] feat: add script to extract CVE info from projects Signed-off-by: Gabriel Bernal --- .../component-mapping.md | 18 + .../script-to-extract-fixed-cves/config.yaml | 67 + .../script-to-extract-fixed-cves/execution.md | 169 ++ .../extract-fixed-cves.sh | 482 ++++++ tasks/script-to-extract-fixed-cves/plan.md | 358 +++++ tasks/script-to-extract-fixed-cves/report.md | 981 ++++++++++++ .../script-to-extract-fixed-cves/report.yaml | 1421 +++++++++++++++++ tasks/script-to-extract-fixed-cves/spec.md | 30 + 8 files changed, 3526 insertions(+) create mode 100644 tasks/script-to-extract-fixed-cves/component-mapping.md create mode 100644 tasks/script-to-extract-fixed-cves/config.yaml create mode 100644 tasks/script-to-extract-fixed-cves/execution.md create mode 100755 tasks/script-to-extract-fixed-cves/extract-fixed-cves.sh create mode 100644 tasks/script-to-extract-fixed-cves/plan.md create mode 100644 tasks/script-to-extract-fixed-cves/report.md create mode 100644 tasks/script-to-extract-fixed-cves/report.yaml create mode 100644 tasks/script-to-extract-fixed-cves/spec.md diff --git a/tasks/script-to-extract-fixed-cves/component-mapping.md b/tasks/script-to-extract-fixed-cves/component-mapping.md new file mode 100644 index 0000000..e817beb --- /dev/null +++ b/tasks/script-to-extract-fixed-cves/component-mapping.md @@ -0,0 +1,18 @@ +logging-view-plugin 6.0 release-6.0 release-coo-ocp-4.12 GA ui-logging-pf4 4.12, 4.13, 4.14 COO 1.5.0 logging-console-plugin-pf4 +logging-view-plugin 6.1 release-6.1 release-coo-ocp-4.15 GA ui-logging-pf5 4.15, 4.16, 4.17, 4,18, 4.19, 4.20, 4.21 COO +1.5.0 logging-console-plugin-pf5 logging-view-plugin 6.2 release-6.2 release-coo-ocp-4.22 GA ui-logging 4.22 COO 1.5.0 logging-console-plugin +console-dashboards-plugin 0.4 release-0.4 release-coo-ocp-4.12 DP ui-dashboards 4.12, 4.13, 4.14, 4.15, 4.16, 4.17, 4.18, 4.19, 4.20, 4.21, 4.22 COO +1.5.0 dashboards-console-plugin distributed-tracing-console-plugin 0.3 release-0.3 release-coo-ocp-4.12 TP ui-distributed-tracing-pf4 4.12, 4.13, +4.14 COO 1.5.0 distributed-tracing-console-plugin-pf4 +distributed-tracing-console-plugin 0.4 release-0.4 release-coo-ocp-4.15 TP ui-distributed-tracing-pf5 4.15, 4.16, 4.17, 4.18 COO +1.5.0 distributed-tracing-console-plugin-pf5 +distributed-tracing-console-plugin 1.0 release-1.0 release-coo-ocp-4.19 GA ui-distributed-tracing-pf6 4.19, 4.20, 4.21 COO +1.5.0 distributed-tracing-console-plugin-pf6 +distributed-tracing-console-plugin 1.1 release-1.1 release-coo-ocp-4.22 GA ui-distributed-tracing 4.22 COO 1.5.0 distributed-tracing-console-plugin +troubleshooting-panel-console-plugin 0.4 release-0.4 release-coo-ocp-4.19 GA ui-troubleshooting-panel-pf6 4.19, 4.20, 4.21 COO +1.5.0 troubleshooting-panel-console-plugin-pf6 +troubleshooting-panel-console-plugin 1.0 release-1.0 release-coo-ocp-4.22 GA ui-troubleshooting-panel 4.22 COO +1.5.0 troubleshooting-panel-console-plugin monitoring-console-plugin 0.4 release-coo-0.4 release-coo-ocp-4.15 DP ui-monitoring-pf5 4.15, 4.16, 4.17, +4.18 COO 1.5.0 monitoring-console-plugin-pf5 monitoring-console-plugin 0.5 release-coo-0.5 release-coo-ocp-4.19 GA ui-monitoring-pf6 4.19, 4.20, +4.21 COO 1.5.0 monitoring-console-plugin-pf6 monitoring-console-plugin 1.0 release-coo-1.0 release-coo-ocp-4.22 GA ui-monitoring 4.22 COO +1.5.0 monitoring-console-plugin diff --git a/tasks/script-to-extract-fixed-cves/config.yaml b/tasks/script-to-extract-fixed-cves/config.yaml new file mode 100644 index 0000000..332f316 --- /dev/null +++ b/tasks/script-to-extract-fixed-cves/config.yaml @@ -0,0 +1,67 @@ +default_rollback: 3 + +entries: + - project: perses-operator + branch: release-coo-1.5 + component: perses-operator-1-5 + rollback: 3 + + - project: monitoring-plugin + branch: release-coo-ocp-4.15 + component: monitoring-console-plugin-pf5-1-5 + rollback: 2 + + - project: monitoring-plugin + branch: release-coo-ocp-4.19 + component: monitoring-console-plugin-pf6-1-5 + rollback: 2 + + - project: monitoring-plugin + branch: release-coo-ocp-4.22 + component: monitoring-console-plugin-1-5 + rollback: 2 + + - project: logging-view-plugin + branch: release-coo-ocp-4.12 + component: logging-console-plugin-pf4-1-5 + rollback: 2 + + - project: logging-view-plugin + branch: release-coo-ocp-4.15 + component: logging-console-plugin-pf5-1-5 + rollback: 2 + + - project: logging-view-plugin + branch: release-coo-ocp-4.22 + component: logging-console-plugin-1-5 + rollback: 2 + + - project: distributed-tracing-plugin + branch: release-coo-ocp-4.12 + component: distributed-tracing-console-plugin-pf4-1-5 + rollback: 2 + + - project: distributed-tracing-plugin + branch: release-coo-ocp-4.15 + component: distributed-tracing-console-plugin-pf5-1-5 + rollback: 2 + + - project: distributed-tracing-plugin + branch: release-coo-ocp-4.19 + component: distributed-tracing-console-plugin-pf6-1-5 + rollback: 2 + + - project: distributed-tracing-plugin + branch: release-coo-ocp-4.22 + component: distributed-tracing-console-plugin-1-5 + rollback: 2 + + - project: troubleshooting-panel-plugin + branch: release-coo-ocp-4.19 + component: troubleshooting-panel-console-plugin-pf6-1-5 + rollback: 2 + + - project: troubleshooting-panel-plugin + branch: release-coo-ocp-4.22 + component: troubleshooting-panel-console-plugin-1-5 + rollback: 2 diff --git a/tasks/script-to-extract-fixed-cves/execution.md b/tasks/script-to-extract-fixed-cves/execution.md new file mode 100644 index 0000000..7b2b246 --- /dev/null +++ b/tasks/script-to-extract-fixed-cves/execution.md @@ -0,0 +1,169 @@ +# Execution: Script to Extract Fixed CVEs + +> Results are annotated inline: `-- **value**` for discovered values, `-- **passes/FAILED**` for verification. + +## Phase 1: Core Script — Git Operations and Project Loop +Depends on: nothing | Parallel with: none | Type: implementation | Projects: ai-sdlc (tasks/) + +- [x] Create `tasks/script-to-extract-fixed-cves/extract-fixed-cves.sh` with project/branch config array +- [x] Add repo-name → local-dir mapping function (`get_local_dir`) +- [x] Add remote resolution function (`resolve_remote`) +- [x] Add git checkout/restore logic with original ref tracking -- **uses temp file instead of associative array for bash 3.x compat** +- [x] Fix `.git` check to use `-e` instead of `-d` for submodule gitlinks +- [x] Add `git checkout -- .` before branch switches to reset npm install side effects + +### Phase 1 Verification +- [x] `bash -n tasks/script-to-extract-fixed-cves/extract-fixed-cves.sh` — **passes** + +## Phase 2: NPM Audit Diff Logic +Depends on: Phase 1 | Parallel with: none (same file as Phase 3) | Type: implementation | Projects: ai-sdlc (tasks/) + +- [x] Add frontend directory detection function (`detect_frontend_dir`) +- [x] Add npm audit at HEAD and HEAD~N +- [x] Add `extract_npm_urls` function using jq to extract advisory URLs +- [x] Add `extract_npm_detail` function for report enrichment +- [x] Add diff logic: extract URLs from HEAD and HEAD~N, use `comm -23` to find fixed + +### Phase 2 Verification +- [x] jq expression correctly parses npm audit JSON format -- **passes, tested against distributed-tracing and troubleshooting-panel** + +## Phase 3: govulncheck Diff Logic +Depends on: Phase 1 | Parallel with: none (same file as Phase 2) | Type: implementation | Projects: ai-sdlc (tasks/) + +- [x] Add govulncheck run at HEAD and HEAD~N +- [x] Add `extract_go_ids` function using jq to extract OSV IDs from finding entries +- [x] Add `extract_go_detail` function for report enrichment (aliases, module, summary) +- [x] Add diff logic mirroring NPM approach + +### Phase 3 Verification +- [x] jq expression correctly parses govulncheck JSON format -- **passes, found 20-32 Go vulns fixed per branch** + +## Phase 4: Markdown Report Generation +Depends on: Phase 2, Phase 3 | Parallel with: none | Type: implementation | Projects: ai-sdlc (tasks/) + +- [x] Add report header generation with date +- [x] Add per-project/branch section generation (NPM + Go tables) +- [x] Add summary table generation at end with status column + +### Phase 4 Verification +- [x] Generated report.md is valid markdown with proper tables -- **passes** + +## Phase 5: Error Handling and Cleanup +Depends on: Phase 4 | Parallel with: none | Type: implementation | Projects: ai-sdlc (tasks/) + +- [x] Add cleanup trap (restore git state with `checkout -- .` + ref restore, remove temp files) +- [x] Add temp directory creation (`mktemp -d`) +- [x] Add tool availability checks (jq required, govulncheck optional, npm required) +- [x] Add dirty-worktree guard (refuse to run with uncommitted changes) +- [x] Add progress logging to stderr +- [x] Add skip for too-few-commits, missing remote, fetch failure + +### Phase 5 Verification +- [x] Script handles missing `govulncheck` gracefully -- **confirmed: HAS_GOVULNCHECK flag skips with warning** +- [x] Cleanup restores working trees correctly after normal completion -- **verified: projects back on main, clean** + +## End-to-End Verification +- [x] Run against `troubleshooting-panel:release-coo-ocp-4.19` — **20 Go vulns fixed detected** +- [x] Run against `distributed-tracing:release-coo-ocp-4.{12,15,19,22}` — **32, 28, 28, 0 Go vulns fixed** +- [x] Report contains proper markdown tables with CVE IDs, modules, summaries +- [x] Frontend detected in `web/` for UI plugins +- [x] Projects restored to main branch with clean working tree after script completes +- [x] Full parallel run of all 13 entries — **5 min, 362% CPU, all entries ok** + +--- + +## Phase 6: Parallel Execution by Project +> Added post-initial implementation to reduce wall-clock time from ~25 min to ~5 min. + +- [x] Extract entry processing into `process_entry` function with index-based output files +- [x] Add `process_project` function to run entries for the same project sequentially +- [x] Replace main for-loop with parallel dispatch: one background job per unique project +- [x] Write report sections to `$WORK_DIR/sections/NNNN.md` and summaries to `$WORK_DIR/summaries/NNNN.txt` for ordered assembly +- [x] Move refs tracking to per-project files (`$WORK_DIR/refs/${local_dir}.txt`) to avoid concurrent writes +- [x] Update cleanup to iterate over `$WORK_DIR/refs/*.txt` +- [x] Prefix stderr with `[$local_dir]` for readable interleaved output +- [x] Fix `pids[@]` unbound variable when filter matches no projects + +### Phase 6 Verification +- [x] Single-project test (`troubleshooting-panel`) — **passes, sequential within project** +- [x] Full 13-entry run — **5 min, 362% CPU, all entries ok, correct report order** +- [x] All projects restored to main with clean working trees + +## Phase 7: Fix NPM Audit JSON Corruption +> `npm install` stdout (e.g., `up to date, audited 1700 packages`) was written into the same file as `npm audit --json`, corrupting the JSON. jq silently returned nothing, so all npm reports were empty. + +- [x] Root cause: `(cd "$dir" && npm install 2>/dev/null && npm audit --json)` piped both commands' stdout into one file +- [x] Fix: redirect npm install stdout to `/dev/null` (`npm install >/dev/null 2>&1`) +- [x] Verified: monitoring-plugin 4.22 now reports 23 npm fixes (was 0) + +### Phase 7 Verification +- [x] monitoring-plugin: 20, 20, 23 npm fixes on 4.15, 4.19, 4.22 — **passes** + +## Phase 8: Remove `npm install` from HEAD Audit +> `npm audit` reads `package-lock.json` directly — `npm install` is unnecessary and can modify the lockfile, contaminating the HEAD vs HEAD~N comparison. + +- [x] Remove `npm install` from the HEAD audit pipeline (keep only `npm audit --json`) +- [x] Remove `--ignore-scripts --legacy-peer-deps` flags (user request) +- [x] Verified: `npm audit --json` produces identical results with and without `node_modules` (11 URLs both ways) + +### Phase 8 Verification +- [x] monitoring-plugin results consistent with manual `npm audit` — **passes** + +## Phase 9: Map GHSA Advisories to CVE IDs +> NPM audit reports advisory URLs (GHSA-xxxx) but errata/release notes need CVE IDs. GitHub Advisory API returns the mapping via `gh api /advisories/GHSA-xxxx`. + +- [x] Add `gh` CLI availability check (`HAS_GH` flag, graceful fallback to "N/A") +- [x] Add `fetch_cve_for_ghsa` function using `gh api` + `jq .cve_id` +- [x] Add CVE column to NPM report table: `| Advisory | CVE | Package | Severity | Title |` +- [x] Rate limit confirmed: 5000 req/hr, worst case ~520 calls — **no concern** + +### Phase 9 Verification +- [x] troubleshooting-panel 4.19: 48 npm advisories, all CVE-mapped — **passes** +- [x] Advisories without CVE show "N/A" (e.g., GHSA-442j, GHSA-7rx3) — **passes** + +## Phase 10: Add Commit Messages to Report +> Each report section now includes the commits being analyzed (HEAD~N..HEAD), giving context on what changes introduced the fixes. + +- [x] Capture `git log --oneline $remote/$branch~$ROLLBACK..$remote/$branch` before checkout +- [x] Add "Commits analyzed" subsection at top of each project/branch report section + +### Phase 10 Verification +- [x] perses-operator report shows 5 commit messages — **passes** + +## Phase 11: YAML Output with Konflux Component Mapping +> Errata tooling needs a YAML file mapping each fixed CVE to its Konflux component name. Component names come from a hardcoded mapping derived from `component-mapping.md`. + +- [x] Add `--yaml` flag parsing (coexists with optional filter argument) +- [x] Add `get_component` function: hardcoded `(project, branch) → component-name` mapping for all 13 entries +- [x] Cache CVE lookups in `$etmp/cve_cache.txt` during report generation to avoid duplicate API calls +- [x] Write per-entry YAML fragments to `$WORK_DIR/yaml/NNNN.yaml` +- [x] Assemble `report.yaml` from fragments in entry order after all jobs complete +- [x] Go CVEs: extract CVE-* aliases from `extract_go_detail` output +- [x] NPM CVEs: read from cache file (populated during report table generation) + +### Phase 11 Verification +- [x] `--yaml perses-operator`: 20 Go CVEs mapped to `perses-operator-1-5` — **passes** +- [x] YAML format matches spec: `cves:\n - key: CVE-xxx\n component: xxx` — **passes** +- [x] Without `--yaml`: no report.yaml generated — **passes** + +--- + +## Summary + +**Status:** Complete (all 11 phases done) + +### Script capabilities +- Parallel processing of 5 projects (entries for same project run sequentially) +- Diffs npm audit + govulncheck at HEAD vs HEAD~N to find fixed CVEs +- Markdown report with commit messages, NPM table (with CVE via GitHub API), Go table, and summary +- Optional `--yaml` output mapping CVEs to Konflux component names +- Graceful handling of missing tools, dirty worktrees, missing branches +- Cleanup trap restores all project working trees on exit + +### Usage +```bash +./extract-fixed-cves.sh # all projects, markdown only +./extract-fixed-cves.sh monitoring-plugin # filter by project name +./extract-fixed-cves.sh --yaml # all projects, markdown + YAML +./extract-fixed-cves.sh --yaml troubleshooting # filter + YAML +``` diff --git a/tasks/script-to-extract-fixed-cves/extract-fixed-cves.sh b/tasks/script-to-extract-fixed-cves/extract-fixed-cves.sh new file mode 100755 index 0000000..0b39cb6 --- /dev/null +++ b/tasks/script-to-extract-fixed-cves/extract-fixed-cves.sh @@ -0,0 +1,482 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +CONFIG_FILE="$SCRIPT_DIR/config.yaml" +REPORT="$SCRIPT_DIR/report.md" +WORK_DIR=$(mktemp -d) + +YAML_OUTPUT=false +FILTER="" +for arg in "$@"; do + case "$arg" in + --yaml) YAML_OUTPUT=true ;; + *) FILTER="$arg" ;; + esac +done + +mkdir -p "$WORK_DIR/sections" "$WORK_DIR/summaries" "$WORK_DIR/refs" "$WORK_DIR/yaml" + +HAS_GOVULNCHECK=true +if ! command -v govulncheck &>/dev/null; then + echo "[WARN] govulncheck not found — Go vulnerability checks will be skipped" >&2 + HAS_GOVULNCHECK=false +fi + +if ! command -v jq &>/dev/null; then + echo "[ERROR] jq is required. Install: brew install jq" >&2 + exit 1 +fi + +if ! command -v npm &>/dev/null; then + echo "[ERROR] npm is required." >&2 + exit 1 +fi + +if ! command -v yq &>/dev/null; then + echo "[ERROR] yq is required to parse config.yaml. Install: brew install yq" >&2 + exit 1 +fi + +HAS_GH=true +if ! command -v gh &>/dev/null; then + echo "[WARN] gh CLI not found — CVE mapping will be skipped" >&2 + HAS_GH=false +fi + +# ---- Load config ---- + +if [ ! -f "$CONFIG_FILE" ]; then + echo "[ERROR] Config file not found: $CONFIG_FILE" >&2 + exit 1 +fi + +DEFAULT_ROLLBACK=$(yq -r '.default_rollback // 3' "$CONFIG_FILE") + +ENTRIES=() +ENTRY_COMPONENTS=() +ENTRY_ROLLBACKS=() + +entry_count=$(yq '.entries | length' "$CONFIG_FILE") +for (( i=0; i&2 + +# ---- Cleanup ---- + +cleanup() { + for refs_file in "$WORK_DIR"/refs/*.txt; do + [ -f "$refs_file" ] || continue + while IFS='|' read -r pdir ref; do + echo "[cleanup] Restoring $pdir" >&2 + git -C "$pdir" checkout -- . 2>/dev/null || true + git -C "$pdir" checkout "$ref" 2>/dev/null || true + done < "$refs_file" + done + rm -rf "$WORK_DIR" +} +trap cleanup EXIT + +# ---- Helper functions ---- + +resolve_remote() { + local pdir="$1" branch="$2" + git -C "$pdir" branch -r | grep -E "/${branch}$" | head -1 | sed 's/^[[:space:]]*//' | cut -d'/' -f1 +} + +detect_frontend_dir() { + local pdir="$1" + if [ -f "$pdir/web/package.json" ]; then + echo "$pdir/web" + elif [ -f "$pdir/package.json" ]; then + echo "$pdir" + else + echo "" + fi +} + +extract_npm_urls() { + local json_file="$1" + [ -s "$json_file" ] || return 0 + jq -r ' + .vulnerabilities // {} | to_entries[] | .value.via[]? | + select(type == "object") | .url // empty + ' "$json_file" 2>/dev/null | sort -u || true +} + +extract_npm_detail() { + local json_file="$1" url="$2" + [ -s "$json_file" ] || return 0 + jq -r --arg url "$url" ' + .vulnerabilities // {} | to_entries[] | .value.via[]? | + select(type == "object" and .url == $url) | + [.name // "N/A", .severity // "N/A", .title // "N/A"] | @tsv + ' "$json_file" 2>/dev/null | head -1 || true +} + +fetch_cve_for_ghsa() { + local url="$1" + if [ "$HAS_GH" = false ]; then echo "N/A"; return; fi + local ghsa_id="${url##*/}" + gh api "/advisories/$ghsa_id" 2>/dev/null | jq -r '.cve_id // "N/A"' 2>/dev/null || echo "N/A" +} + +extract_go_ids() { + local json_file="$1" + [ -s "$json_file" ] || return 0 + jq -r 'select(.finding != null) | .finding.osv' "$json_file" 2>/dev/null | sort -u || true +} + +extract_go_detail() { + local json_file="$1" vid="$2" + [ -s "$json_file" ] || return 0 + jq -r --arg id "$vid" ' + select(.osv != null and .osv.id == $id) | .osv | + [ + .id, + (.aliases // [] | join(", ")), + (if .affected then [.affected[].package.name] | unique | join(", ") else "N/A" end), + (.summary // "N/A" | gsub("\n"; " ")) + ] | @tsv + ' "$json_file" 2>/dev/null | head -1 || true +} + +# ---- Per-entry processing ---- + +process_entry() { + local entry_index="$1" project="$2" branch="$3" local_dir="$4" rollback="$5" component="$6" + local pdir="$REPO_ROOT/projects/$local_dir" + local padded_index + padded_index=$(printf "%04d" "$entry_index") + local label="[$local_dir]" + + local section_file="$WORK_DIR/sections/${padded_index}.md" + local summary_line="$WORK_DIR/summaries/${padded_index}.txt" + local refs_file="$WORK_DIR/refs/${local_dir}.txt" + + echo "$label Processing $branch..." >&2 + + if [ ! -e "$pdir/.git" ]; then + echo "$label [SKIP] Not found or not a git repo" >&2 + echo "$project|$branch|0|0|skipped" > "$summary_line" + touch "$section_file" + return + fi + + if ! git -C "$pdir" diff --quiet 2>/dev/null || ! git -C "$pdir" diff --cached --quiet 2>/dev/null; then + echo "$label [SKIP] Uncommitted changes — refusing to modify working tree" >&2 + echo "$project|$branch|0|0|dirty" > "$summary_line" + touch "$section_file" + return + fi + + # Save original ref (one file per project, sequential within project so no race) + if [ ! -s "$refs_file" ]; then + local ref + ref=$(git -C "$pdir" symbolic-ref --short HEAD 2>/dev/null || git -C "$pdir" rev-parse HEAD) + echo "${pdir}|${ref}" > "$refs_file" + fi + + local remote + remote=$(resolve_remote "$pdir" "$branch") + if [ -z "$remote" ]; then + echo "$label [SKIP] Cannot find remote for branch $branch" >&2 + echo "$project|$branch|0|0|no-remote" > "$summary_line" + touch "$section_file" + return + fi + + echo "$label Fetching $remote/$branch..." >&2 + if ! git -C "$pdir" fetch "$remote" "$branch" 2>/dev/null; then + echo "$label [SKIP] Failed to fetch $remote/$branch" >&2 + echo "$project|$branch|0|0|fetch-failed" > "$summary_line" + touch "$section_file" + return + fi + + if ! git -C "$pdir" rev-parse "$remote/$branch~$rollback" &>/dev/null; then + echo "$label [SKIP] Branch $branch has fewer than $rollback commits" >&2 + echo "$project|$branch|0|0|too-few-commits" > "$summary_line" + touch "$section_file" + return + fi + + local etmp="$WORK_DIR/${local_dir}_${branch}" + mkdir -p "$etmp" + + # --- Capture commit log for the range --- + local commits_file="$etmp/commits.txt" + git -C "$pdir" log --oneline "$remote/$branch~$rollback..$remote/$branch" > "$commits_file" 2>/dev/null || true + + # --- Checkout HEAD (tip of branch) --- + git -C "$pdir" checkout "$remote/$branch" --detach 2>/dev/null + local frontend_dir + frontend_dir=$(detect_frontend_dir "$pdir") + + local npm_head="$etmp/npm_head.json" + touch "$npm_head" + if [ -n "$frontend_dir" ]; then + echo "$label [npm] Auditing $branch at HEAD..." >&2 + (cd "$frontend_dir" && npm audit --json 2>/dev/null) > "$npm_head" || true + fi + + local go_head="$etmp/go_head.json" + touch "$go_head" + if [ -f "$pdir/go.mod" ] && [ "$HAS_GOVULNCHECK" = true ]; then + echo "$label [go] govulncheck $branch at HEAD..." >&2 + (cd "$pdir" && govulncheck -json ./... 2>/dev/null) > "$go_head" || true + fi + + # --- Checkout HEAD~N --- + git -C "$pdir" checkout -- . 2>/dev/null || true + git -C "$pdir" checkout "$remote/$branch~$rollback" --detach 2>/dev/null + local frontend_dir_old + frontend_dir_old=$(detect_frontend_dir "$pdir") + + local npm_old="$etmp/npm_old.json" + touch "$npm_old" + if [ -n "$frontend_dir_old" ]; then + echo "$label [npm] Auditing $branch at HEAD~$rollback..." >&2 + (cd "$frontend_dir_old" && npm install >/dev/null 2>&1 && npm audit --json 2>/dev/null) > "$npm_old" || true + fi + + local go_old="$etmp/go_old.json" + touch "$go_old" + if [ -f "$pdir/go.mod" ] && [ "$HAS_GOVULNCHECK" = true ]; then + echo "$label [go] govulncheck $branch at HEAD~$rollback..." >&2 + (cd "$pdir" && govulncheck -json ./... 2>/dev/null) > "$go_old" || true + fi + + # --- Restore --- + git -C "$pdir" checkout -- . 2>/dev/null || true + local original_ref + original_ref=$(grep "^${pdir}|" "$refs_file" 2>/dev/null | head -1 | cut -d'|' -f2) + git -C "$pdir" checkout "$original_ref" 2>/dev/null || true + + # --- Diff NPM --- + local urls_head="$etmp/urls_head.txt" urls_old="$etmp/urls_old.txt" fixed_npm="$etmp/fixed_npm.txt" + extract_npm_urls "$npm_head" > "$urls_head" + extract_npm_urls "$npm_old" > "$urls_old" + comm -23 "$urls_old" "$urls_head" > "$fixed_npm" 2>/dev/null || true + local npm_count + npm_count=$(wc -l < "$fixed_npm" | tr -d ' ') + + # --- Diff Go --- + local ids_head="$etmp/ids_head.txt" ids_old="$etmp/ids_old.txt" fixed_go="$etmp/fixed_go.txt" + extract_go_ids "$go_head" > "$ids_head" + extract_go_ids "$go_old" > "$ids_old" + comm -23 "$ids_old" "$ids_head" > "$fixed_go" 2>/dev/null || true + local go_count + go_count=$(wc -l < "$fixed_go" | tr -d ' ') + + echo "$label $branch: fixed $npm_count npm, $go_count go" >&2 + + # --- Write report section --- + { + echo "## $project ($branch)" + echo "" + + echo "### Commits analyzed" + echo "" + if [ -s "$commits_file" ]; then + while IFS= read -r commit_line; do + echo "- \`${commit_line}\`" + done < "$commits_file" + else + echo "No commits in range." + fi + echo "" + + echo "### NPM Vulnerabilities Fixed" + echo "" + if [ -z "$frontend_dir" ]; then + echo "N/A — no frontend in this project." + elif [ "$npm_count" -gt 0 ]; then + echo "| Advisory | CVE | Package | Severity | Title |" + echo "| -------- | --- | ------- | -------- | ----- |" + while IFS= read -r url; do + local detail pkg sev title cve + detail=$(extract_npm_detail "$npm_old" "$url") + pkg=$(echo "$detail" | cut -f1) + sev=$(echo "$detail" | cut -f2) + title=$(echo "$detail" | cut -f3) + cve=$(fetch_cve_for_ghsa "$url") + echo "${url}|${cve}" >> "$etmp/cve_cache.txt" + echo "| $url | $cve | $pkg | $sev | $title |" + done < "$fixed_npm" + else + echo "No NPM vulnerabilities were fixed." + fi + echo "" + + echo "### Go Vulnerabilities Fixed" + echo "" + if [ ! -f "$pdir/go.mod" ]; then + echo "N/A — no go.mod in this project." + elif [ "$HAS_GOVULNCHECK" = false ]; then + echo "Skipped — govulncheck not installed." + elif [ "$go_count" -gt 0 ]; then + echo "| ID | CVE/Aliases | Module | Summary |" + echo "| -- | ----------- | ------ | ------- |" + while IFS= read -r vid; do + local detail aliases module summary + detail=$(extract_go_detail "$go_old" "$vid") + aliases=$(echo "$detail" | cut -f2) + module=$(echo "$detail" | cut -f3) + summary=$(echo "$detail" | cut -f4) + echo "| $vid | $aliases | $module | $summary |" + done < "$fixed_go" + else + echo "No Go vulnerabilities were fixed." + fi + echo "" + } > "$section_file" + + echo "$project|$branch|$npm_count|$go_count|ok" > "$summary_line" + + # --- YAML fragment --- + if [ "$YAML_OUTPUT" = true ]; then + local yaml_file="$WORK_DIR/yaml/${padded_index}.yaml" + { + # NPM CVEs from cache + if [ -f "$etmp/cve_cache.txt" ]; then + while IFS='|' read -r _url cve; do + if [ "$cve" != "N/A" ] && [ -n "$cve" ]; then + echo " - key: $cve" + echo " component: $component" + fi + done < "$etmp/cve_cache.txt" + fi + # Go CVEs from aliases + if [ -s "$fixed_go" ]; then + while IFS= read -r vid; do + local detail aliases + detail=$(extract_go_detail "$go_old" "$vid") + aliases=$(echo "$detail" | cut -f2) + IFS=', ' read -ra cve_list <<< "$aliases" + for cve in "${cve_list[@]}"; do + cve=$(echo "$cve" | tr -d ' ') + if [[ "$cve" == CVE-* ]]; then + echo " - key: $cve" + echo " component: $component" + fi + done + done < "$fixed_go" + fi + } > "$yaml_file" + fi +} + +# ---- Per-project processing (sequential within project) ---- + +process_project() { + local target_project="$1" + for i in "${!ENTRIES[@]}"; do + local entry="${ENTRIES[$i]}" + local project="${entry%%:*}" + local branch="${entry##*:}" + + if [ "$project" != "$target_project" ]; then + continue + fi + + if [ -n "$FILTER" ] && [[ "$project" != *"$FILTER"* ]]; then + continue + fi + + process_entry "$i" "$project" "$branch" "$project" "${ENTRY_ROLLBACKS[$i]}" "${ENTRY_COMPONENTS[$i]}" + done +} + +# ---- Main: parallel dispatch ---- + +# Build unique project list (preserving order of first appearance) +unique_projects=() +seen_projects="" +for entry in "${ENTRIES[@]}"; do + project="${entry%%:*}" + if [[ "$seen_projects" != *"|${project}|"* ]]; then + unique_projects+=("$project") + seen_projects="${seen_projects}|${project}|" + fi +done + +echo "Starting parallel processing of ${#unique_projects[@]} projects..." >&2 + +pids=() +for project in "${unique_projects[@]}"; do + if [ -n "$FILTER" ] && [[ "$project" != *"$FILTER"* ]]; then + continue + fi + process_project "$project" & + pids+=($!) +done + +# Wait for all background jobs +failed=0 +for pid in "${pids[@]+"${pids[@]}"}"; do + if ! wait "$pid"; then + failed=$((failed + 1)) + fi +done + +if [ "$failed" -gt 0 ]; then + echo "[WARN] $failed project group(s) had errors" >&2 +fi + +# ---- Assemble report ---- + +{ + echo "# Fixed CVEs Report" + echo "" + echo "Generated: $(date +%Y-%m-%d)" + echo "" +} > "$REPORT" + +# Concatenate sections in original entry order +for section in "$WORK_DIR"/sections/*.md; do + [ -f "$section" ] || continue + [ -s "$section" ] || continue + cat "$section" >> "$REPORT" +done + +# Summary table +{ + echo "## Summary" + echo "" + echo "| Project | Branch | NPM Fixed | Go Fixed | Total | Status |" + echo "| ------- | ------ | --------- | -------- | ----- | ------ |" + for summary in "$WORK_DIR"/summaries/*.txt; do + [ -f "$summary" ] || continue + while IFS='|' read -r p b n g s; do + total=$((n + g)) + echo "| $p | $b | $n | $g | $total | $s |" + done < "$summary" + done + echo "" +} >> "$REPORT" + +# ---- Assemble YAML (if requested) ---- + +if [ "$YAML_OUTPUT" = true ]; then + YAML_REPORT="$SCRIPT_DIR/report.yaml" + echo "cves:" > "$YAML_REPORT" + for yf in "$WORK_DIR"/yaml/*.yaml; do + [ -f "$yf" ] || continue + [ -s "$yf" ] || continue + cat "$yf" >> "$YAML_REPORT" + done + echo "YAML saved to $YAML_REPORT" >&2 +fi + +echo "" >&2 +echo "Report saved to $REPORT" >&2 diff --git a/tasks/script-to-extract-fixed-cves/plan.md b/tasks/script-to-extract-fixed-cves/plan.md new file mode 100644 index 0000000..5980415 --- /dev/null +++ b/tasks/script-to-extract-fixed-cves/plan.md @@ -0,0 +1,358 @@ +# Plan: Script to Extract Fixed CVEs + +## Problem + +After dependency update commits are cherry-picked to release branches, we need a way to identify which CVEs were fixed by those updates. Currently +this requires manually running `npm audit` and `govulncheck` before and after the commits, then comparing the results — tedious and error-prone across +14 project/branch combinations. The script automates this: for each project+branch pair, it diffs the vulnerability state at HEAD vs HEAD~3 and +produces a markdown report listing the CVEs that were resolved. + +## Current State + +| Component | File / Location | Current Behavior | +| ---------------------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------- | +| perses-operator | `projects/perses-operator/` | Go-only project. Branch `release-coo-1.5` on `rhobs` remote. No frontend. | +| monitoring-plugin | `projects/monitoring-plugin/` | Go backend + frontend in `web/`. Release branches `release-coo-ocp-4.{15,19,22}` on `upstream` remote. | +| logging-view-plugin | `projects/logging-view-plugin/` | Go backend + frontend in `web/`. Release branches `release-coo-ocp-4.{12,15,22}` on `upstream` remote. | +| distributed-tracing-plugin | `projects/distributed-tracing-plugin/` | Go backend + frontend in `web/`. Release branches `release-coo-ocp-4.{12,15,19,22}` on `origin` remote. | +| troubleshooting-panel-plugin | `projects/troubleshooting-panel-plugin/` | Go backend + frontend in `web/`. Release branches `release-coo-ocp-4.{19,22}` on `origin` remote. | +| Script location | `tasks/script-to-extract-fixed-cves/` | Only `spec.md` exists. No script yet. | + +### Remote/Branch Mapping + +The script must resolve the correct remote for each branch since projects use different remote names: + +| Project | Remote | Branch pattern | +| ---------------------------- | ---------- | --------------------- | +| perses-operator | `rhobs` | `release-coo-1.5` | +| monitoring-plugin | `upstream` | `release-coo-ocp-4.*` | +| logging-view-plugin | `upstream` | `release-coo-ocp-4.*` | +| distributed-tracing-plugin | `origin` | `release-coo-ocp-4.*` | +| troubleshooting-panel-plugin | `origin` | `release-coo-ocp-4.*` | + +### Frontend Location + +All UI plugins have their `package.json` in `web/`, not the project root. The perses-operator has no frontend at all. The script must auto-detect the +frontend directory by checking for `web/package.json` first, then falling back to root `package.json`. + +## Changes + +### Phase 1: Core Script — Git Operations and Project Loop + +**Dependency:** None **Parallel with:** None + +#### Files Modified + +| File | Change | +| ---------------------------------------------------------- | --------------------------- | +| `tasks/script-to-extract-fixed-cves/extract-fixed-cves.sh` | New file — main bash script | + +#### Details + +Create a bash script that: + +1. **Defines the project/branch configuration as an array:** + +```bash +ENTRIES=( + "perses-operator:release-coo-1.5" + "monitoring-plugin:release-coo-ocp-4.15" + "monitoring-plugin:release-coo-ocp-4.19" + "monitoring-plugin:release-coo-ocp-4.22" + "logging-view-plugin:release-coo-ocp-4.12" + "logging-view-plugin:release-coo-ocp-4.15" + "logging-view-plugin:release-coo-ocp-4.22" + "distributed-tracing-console-plugin:release-coo-ocp-4.12" + "distributed-tracing-console-plugin:release-coo-ocp-4.15" + "distributed-tracing-console-plugin:release-coo-ocp-4.19" + "distributed-tracing-console-plugin:release-coo-ocp-4.22" + "troubleshooting-panel-console-plugin:release-coo-ocp-4.19" + "troubleshooting-panel-console-plugin:release-coo-ocp-4.22" +) +``` + +Note: The spec uses repository names (e.g., `distributed-tracing-console-plugin`) but the local submodule directories use shorter names (e.g., +`distributed-tracing-plugin`). The script must map repo names → local directory names: + +```bash +get_local_dir() { + case "$1" in + distributed-tracing-console-plugin) echo "distributed-tracing-plugin" ;; + troubleshooting-panel-console-plugin) echo "troubleshooting-panel-plugin" ;; + *) echo "$1" ;; + esac +} +``` + +2. **For each entry, resolves the remote that tracks the branch:** + +```bash +resolve_remote() { + local project_dir="$1" branch="$2" + git -C "$project_dir" branch -r | grep -E "/${branch}$" | head -1 | cut -d'/' -f1 | tr -d ' ' +} +``` + +3. **Fetches the remote, checks out the branch at detached HEAD, and records the original ref for restoration:** + +```bash +original_ref=$(git -C "$project_dir" rev-parse HEAD) +git -C "$project_dir" fetch "$remote" "$branch" +git -C "$project_dir" checkout "$remote/$branch" --detach +``` + +4. **After running audits (Phase 2), restores the original state:** + +```bash +git -C "$project_dir" checkout "$original_ref" --detach +git -C "$project_dir" checkout - # or restore to original branch +``` + +#### Phase 1 Verification + +- Run `bash -n tasks/script-to-extract-fixed-cves/extract-fixed-cves.sh` to verify syntax +- Verify the remote resolution logic works: `git -C ./projects/monitoring-plugin branch -r | grep -E "/release-coo-ocp-4.15$"` + +--- + +### Phase 2: NPM Audit Diff Logic + +**Dependency:** Phase 1 **Parallel with:** Phase 3 (different concern, same file) + +#### Files Modified + +| File | Change | +| ---------------------------------------------------------- | ---------------------------- | +| `tasks/script-to-extract-fixed-cves/extract-fixed-cves.sh` | Add npm audit diff functions | + +#### Details + +Add functions to detect the frontend directory, run `npm audit` at two points, and diff the results. + +##### Frontend Directory Detection + +```bash +detect_frontend_dir() { + local project_dir="$1" + if [ -f "$project_dir/web/package.json" ]; then + echo "$project_dir/web" + elif [ -f "$project_dir/package.json" ]; then + echo "$project_dir" + else + echo "" + fi +} +``` + +##### NPM Audit Diff + +```bash +run_npm_audit() { + local frontend_dir="$1" output_file="$2" + (cd "$frontend_dir" && npm install --ignore-scripts 2>/dev/null && npm audit --json 2>/dev/null) > "$output_file" +} + +extract_npm_cves() { + local audit_json="$1" + # npm audit --json outputs vulnerabilities keyed by package name + # Each vulnerability has a "via" array containing objects with "url" fields (advisory URLs containing CVE refs) + jq -r '.vulnerabilities | to_entries[] | .value.via[]? | + if type == "object" then .url // empty else empty end' "$audit_json" | + sort -u +} +``` + +The diff logic: + +1. At HEAD: run `npm audit --json` → `audit_head.json` +2. At HEAD~3: run `npm install --ignore-scripts` then `npm audit --json` → `audit_head3.json` +3. Extract advisory URLs from both +4. Use `comm -23` to find URLs in HEAD~3 but not in HEAD (= fixed) + +For each fixed advisory URL, also extract the CVE ID, severity, package name, and vulnerability title from the HEAD~3 audit JSON for the report. + +#### Phase 2 Verification + +- Test against monitoring-plugin on `release-coo-ocp-4.22` to verify npm audit output parsing +- Verify `jq` correctly extracts advisory URLs from npm audit JSON + +--- + +### Phase 3: govulncheck Diff Logic + +**Dependency:** Phase 1 **Parallel with:** Phase 2 (different concern, same file) + +#### Files Modified + +| File | Change | +| ---------------------------------------------------------- | ------------------------------ | +| `tasks/script-to-extract-fixed-cves/extract-fixed-cves.sh` | Add govulncheck diff functions | + +#### Details + +Add functions to run `govulncheck` at two points and diff the results. This applies to all projects since they all have `go.mod`. + +##### govulncheck Diff + +```bash +run_govulncheck() { + local project_dir="$1" output_file="$2" + (cd "$project_dir" && govulncheck -json ./... 2>/dev/null) > "$output_file" +} + +extract_go_vulns() { + local vuln_json="$1" + # govulncheck -json outputs one JSON object per line with "finding" entries + # Each finding has an "osv" field with the vulnerability ID (e.g., GO-2024-3321) + jq -r 'select(.finding != null) | .finding.osv' "$vuln_json" | sort -u +} +``` + +The diff logic mirrors NPM: + +1. At HEAD: run `govulncheck -json ./...` → `govulncheck_head.json` +2. At HEAD~3: run `govulncheck -json ./...` → `govulncheck_head3.json` +3. Extract vulnerability IDs from both +4. Use `comm -23` to find IDs in HEAD~3 but not in HEAD (= fixed) + +For each fixed vulnerability, also extract the aliases (CVE IDs), affected module, and summary from the HEAD~3 output for the report. The +`govulncheck -json` output includes `osv` entries with full details: + +```bash +extract_go_vuln_details() { + local vuln_json="$1" vuln_id="$2" + jq -r --arg id "$vuln_id" ' + select(.osv != null and .osv.id == $id) | + .osv | {id, aliases: (.aliases // []), summary, affected: [.affected[]?.package.name]} + ' "$vuln_json" +} +``` + +#### Phase 3 Verification + +- Test against perses-operator on `release-coo-1.5` to verify govulncheck output parsing +- Verify `jq` correctly extracts OSV IDs from govulncheck JSON + +--- + +### Phase 4: Markdown Report Generation + +**Dependency:** Phase 2, Phase 3 **Parallel with:** None + +#### Files Modified + +| File | Change | +| ---------------------------------------------------------- | ------------------------------ | +| `tasks/script-to-extract-fixed-cves/extract-fixed-cves.sh` | Add report generation function | + +#### Details + +After processing all entries, generate a markdown report at `tasks/script-to-extract-fixed-cves/report.md` with the following structure: + +```markdown +# Fixed CVEs Report + +Generated: 2026-06-29 + +## monitoring-plugin (release-coo-ocp-4.15) + +### NPM Vulnerabilities Fixed + +| Advisory | CVE | Package | Severity | Title | +| --------------------------------------- | ------------- | ------- | -------- | ------------------- | +| https://github.com/advisories/GHSA-xxxx | CVE-2024-xxxx | lodash | high | Prototype Pollution | + +### Go Vulnerabilities Fixed + +| ID | CVE | Module | Summary | +| ------------ | ------------- | ---------------- | ------------------ | +| GO-2024-3321 | CVE-2024-xxxx | golang.org/x/net | HTTP/2 rapid reset | + +## monitoring-plugin (release-coo-ocp-4.19) + +... + +## Summary + +| Project | Branch | NPM Fixed | Go Fixed | Total | +| ----------------- | -------------------- | --------- | -------- | ----- | +| monitoring-plugin | release-coo-ocp-4.15 | 3 | 1 | 4 | +| ... | ... | ... | ... | ... | +``` + +The script writes the report incrementally: each project/branch appends its section, then the summary table is generated at the end from the collected +counts. + +#### Phase 4 Verification + +- Run the full script and verify the generated `report.md` is valid markdown +- Check that every project/branch entry from the spec appears in the report (even if zero CVEs fixed) + +--- + +### Phase 5: Error Handling and Cleanup + +**Dependency:** Phase 4 **Parallel with:** None + +#### Files Modified + +| File | Change | +| ---------------------------------------------------------- | ----------------------------------- | +| `tasks/script-to-extract-fixed-cves/extract-fixed-cves.sh` | Add cleanup trap and error handling | + +#### Details + +1. **Trap for cleanup**: Ensure the working directory of each submodule is restored even if the script fails mid-execution. + +```bash +cleanup() { + for project_dir in "${TOUCHED_DIRS[@]}"; do + git -C "$project_dir" checkout - 2>/dev/null || true + git -C "$project_dir" clean -fd 2>/dev/null || true + done + rm -rf "$TMPDIR" +} +trap cleanup EXIT +``` + +2. **Temp directory for intermediate files**: Use `mktemp -d` for all audit JSON files. + +3. **Skip missing tools gracefully**: If `govulncheck` is not installed, log a warning and skip Go analysis (npm is assumed to be present). If `jq` is + missing, fail with a clear error since it's required. + +4. **Skip projects without Go/frontend**: If a project has no `go.mod`, skip govulncheck. If no `package.json` is found, skip npm audit. + +5. **Progress logging**: Print progress to stderr so stdout remains clean for piping. + +#### Phase 5 Verification + +- Verify cleanup works by killing the script mid-run and checking that submodule working trees are restored +- Verify the script handles missing `govulncheck` gracefully + +## PR Strategy + +| PR | Repository | Branch | Description | Dependencies | +| -- | ---------- | ------ | --------------------------------------------------------------------------- | ------------ | +| 1 | ai-sdlc | main | Add `extract-fixed-cves.sh` script in `tasks/script-to-extract-fixed-cves/` | None | + +Single PR since the script is self-contained in the task directory and doesn't modify any project submodule code. + +## Verification + +- **Rollback + npm audit**: Run the script against `monitoring-plugin:release-coo-ocp-4.22` and verify it detects CVEs that were fixed in the recent + `update-vulnerable-dependencies-26-06-2026` commits. +- **Rollback + govulncheck**: Run the script against `perses-operator:release-coo-1.5` and verify Go vulnerability diff works. +- **Multiple projects in a loop**: Run the full script with all 13 entries and verify the report contains a section for each. +- **Frontend in different directory**: Verify the script correctly finds `web/package.json` for the UI plugins and skips frontend analysis for + perses-operator. +- **Markdown report**: Verify the generated report is valid markdown with proper tables and summary. + +## Risks + +| Risk | Impact | Mitigation | +| ---------------------------------------------------------------------------------- | ----------------------------------------------- | ------------------------------------------------------------------------------------------ | +| `npm install` at HEAD~3 may fail if Node version is incompatible with old lockfile | Script aborts for that entry, no CVE data | Use `--ignore-scripts` and `--legacy-peer-deps` flags; catch errors and report as warnings | +| `govulncheck` requires Go to be installed and modules to build | Missing Go vulns for some entries | Check for `govulncheck` availability before running; skip with warning if missing | +| Checking out branches modifies submodule working trees | Uncommitted changes in submodules could be lost | Save and restore original ref; refuse to run if there are uncommitted changes | +| npm audit JSON format varies between npm versions | Parsing breaks | Use `npm audit --json` which has been stable since npm 7; document minimum npm version | +| Large number of entries (13) makes full run slow due to `npm install` at each | Script takes 10+ minutes | Add `--project` flag to run for a single project; show progress per entry | diff --git a/tasks/script-to-extract-fixed-cves/report.md b/tasks/script-to-extract-fixed-cves/report.md new file mode 100644 index 0000000..6bc9630 --- /dev/null +++ b/tasks/script-to-extract-fixed-cves/report.md @@ -0,0 +1,981 @@ +# Fixed CVEs Report + +Generated: 2026-06-29 + +## perses-operator (release-coo-1.5) + +### Commits analyzed + +- `f7d3b85 [FORK] downgrade to go 1.26.3 to match konflux` +- `7df0910 Merge pull request #8 from rhobs/optimize-watcher-mermory-usage` +- `9cbc2dc Merge pull request #10 from perses/fix-vulnerable-dependencies` +- `ffe645d [FORK] fix vulnerable dependencies` +- `31fee9b [ENHANCEMENT] use only metadata to watch for resources to optimize memory consumption` + +### NPM Vulnerabilities Fixed + +N/A — no frontend in this project. + +### Go Vulnerabilities Fixed + +| ID | CVE/Aliases | Module | Summary | +| ------------ | -------------- | ------------------------ | ----------------------------------------------------------------------------------------------------------------------- | +| GO-2026-4599 | CVE-2026-27137 | stdlib | Incorrect enforcement of email constraints in crypto/x509 | +| GO-2026-4600 | CVE-2026-27138 | stdlib | Panic in name constraint checking for malformed certificates in crypto/x509 | +| GO-2026-4601 | CVE-2026-25679 | stdlib | Incorrect parsing of IPv6 host literals in net/url | +| GO-2026-4602 | CVE-2026-27139 | stdlib | FileInfo can escape from a Root in os | +| GO-2026-4603 | CVE-2026-27142 | stdlib | URLs in meta content attribute actions are not escaped in html/template | +| GO-2026-4864 | CVE-2026-32282 | stdlib | TOCTOU permits root escape on Linux via Root.Chmod in os in internal/syscall/unix | +| GO-2026-4865 | CVE-2026-32289 | stdlib | JsBraceDepth Context Tracking Bugs (XSS) in html/template | +| GO-2026-4866 | CVE-2026-33810 | stdlib | Case-sensitive excludedSubtrees name constraints cause Auth Bypass in crypto/x509 | +| GO-2026-4869 | CVE-2026-32288 | stdlib | Unbounded allocation for old GNU sparse in archive/tar | +| GO-2026-4870 | CVE-2026-32283 | stdlib | Unauthenticated TLS 1.3 KeyUpdate record can cause persistent connection retention and DoS in crypto/tls | +| GO-2026-4918 | CVE-2026-33814 | golang.org/x/net, stdlib | Infinite loop in HTTP/2 transport when given bad SETTINGS_MAX_FRAME_SIZE in net/http/internal/http2 in golang.org/x/net | +| GO-2026-4946 | CVE-2026-32281 | stdlib | Inefficient policy validation in crypto/x509 | +| GO-2026-4947 | CVE-2026-32280 | stdlib | Unexpected work during chain building in crypto/x509 | +| GO-2026-4971 | CVE-2026-39836 | stdlib | Panic in Dial and LookupPort when handling NUL byte on Windows in net | +| GO-2026-4976 | CVE-2026-39825 | stdlib | ReverseProxy forwards queries with more than urlmaxqueryparams parameters in net/http/httputil | +| GO-2026-4977 | CVE-2026-42499 | stdlib | Quadratic string concatenation in consumePhrase in net/mail | +| GO-2026-4980 | CVE-2026-39826 | stdlib | Escaper bypass leads to XSS in html/template | +| GO-2026-4981 | CVE-2026-33811 | stdlib | Crash when handling long CNAME response in net | +| GO-2026-4982 | CVE-2026-39823 | stdlib | Bypass of meta content URL escaping causes XSS in html/template | +| GO-2026-4986 | CVE-2026-39820 | stdlib | Quadratic string concatentation in consumeComment in net/mail | + +## monitoring-plugin (release-coo-ocp-4.15) + +### Commits analyzed + +- `524eb72 Merge pull request #1018 from jgbernalp/update-vulnerable-dependencies-4.15-26-06-2026` +- `02e9fbd fix: update vulnerable dependencies` +- `a3ff6a4 Merge pull request #1011 from openshift-cherrypick-robot/cherry-pick-1009-to-release-coo-ocp-4.15` +- `f13fb8e fix(perses): fall back to metadata.name when dashboard display name is missing` + +### NPM Vulnerabilities Fixed + +| Advisory | CVE | Package | Severity | Title | +| ------------------------------------------------- | -------------- | --------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | +| https://github.com/advisories/GHSA-22p9-wv53-3rq4 | CVE-2026-48801 | linkify-it | high | LinkifyIt#match scan loop has quadratic algorithmic complexity | +| https://github.com/advisories/GHSA-2j2x-hqr9-3h42 | CVE-2026-40181 | react-router | moderate | React Router's same-origin redirect with path starting // causes open redirect via protocol-relative URL reinterpretation | +| https://github.com/advisories/GHSA-35p6-xmwp-9g52 | CVE-2026-6733 | undici | low | undici vulnerable to HTTP response queue poisoning via keep-alive socket reuse | +| https://github.com/advisories/GHSA-58qx-3vcg-4xpx | CVE-2026-45736 | ws | moderate | ws: Uninitialized memory disclosure | +| https://github.com/advisories/GHSA-64mm-vxmg-q3vj | CVE-2026-55602 | http-proxy-middleware | moderate | http-proxy-middleware `router` host+path substring matching allows Host-header-driven backend routing bypass | +| https://github.com/advisories/GHSA-79cf-xcqc-c78w | CVE-2026-6402 | webpack-dev-server | moderate | webpack-dev-server vulnerable to cross-origin source code exposure on non-HTTPS origins | +| https://github.com/advisories/GHSA-96hv-2xvq-fx4p | CVE-2026-48779 | ws | high | ws: Memory exhaustion DoS from tiny fragments and data chunks | +| https://github.com/advisories/GHSA-g8m3-5g58-fq7m | CVE-2026-11525 | undici | low | undici vulnerable to Set-Cookie SameSite attribute downgrade via permissive substring matching | +| https://github.com/advisories/GHSA-hm92-r4w5-c3mj | CVE-2026-6734 | undici | high | undici vulnerable to cross-origin request routing via SOCKS5 proxy pool reuse | +| https://github.com/advisories/GHSA-hmw2-7cc7-3qxx | CVE-2026-12143 | form-data | high | form-data: CRLF injection in form-data via unescaped multipart field names and filenames | +| https://github.com/advisories/GHSA-mx8g-39q3-5c79 | CVE-2026-9595 | webpack-dev-server | moderate | webpack-dev-server vulnerable to HMR WebSocket interception via permissive user proxies | +| https://github.com/advisories/GHSA-p88m-4jfj-68fv | CVE-2026-9679 | undici | moderate | undici vulnerable to HTTP header injection via Set-Cookie percent-decoding | +| https://github.com/advisories/GHSA-ph9p-34f9-6g65 | CVE-2026-44705 | tmp | high | tmp has Path Traversal via unsanitized prefix/postfix that enables directory escape | +| https://github.com/advisories/GHSA-pr7r-676h-xcf6 | CVE-2026-9678 | undici | moderate | undici vulnerable to cross-user information disclosure via shared cache whitespace bypass | +| https://github.com/advisories/GHSA-q89c-q3h5-w34g | CVE-2026-41691 | i18next-http-backend | moderate | i18next-http-backend has Path Traversal & URL Injection via Unsanitised lng/ns | +| https://github.com/advisories/GHSA-q8mj-m7cp-5q26 | CVE-2026-8723 | qs | moderate | qs has a remotely triggerable DoS: qs.stringify crashes with TypeError on null/undefined entries in comma-format arrays when encodeValuesOnly is set | +| https://github.com/advisories/GHSA-v6wh-96g9-6wx3 | CVE-2026-53632 | launch-editor | moderate | launch-editor: NTLMv2 hash disclosure via UNC path handling on Windows | +| https://github.com/advisories/GHSA-vmh5-mc38-953g | CVE-2026-9697 | undici | high | undici vulnerable to TLS certificate validation bypass via dropped requestTls in SOCKS5 ProxyAgent | +| https://github.com/advisories/GHSA-vxpw-j846-p89q | CVE-2026-12151 | undici | high | undici WebSocket client vulnerable to denial of service via fragment count bypass | +| https://github.com/advisories/GHSA-w7jw-789q-3m8p | CVE-2026-9277 | shell-quote | critical | shell-quote quote() does not escape newlines in object .op values | + +### Go Vulnerabilities Fixed + +| ID | CVE/Aliases | Module | Summary | +| ------------ | ----------------------------------- | ------------------------ | ----------------------------------------------------------------------------------------------------------------------- | +| GO-2025-3488 | CVE-2025-22868 | golang.org/x/oauth2 | Unexpected memory consumption during token parsing in golang.org/x/oauth2 | +| GO-2025-3503 | CVE-2025-22870, GHSA-qxp5-gwg8-xv66 | golang.org/x/net, stdlib | HTTP Proxy bypass using IPv6 Zone IDs in golang.org/x/net | +| GO-2025-3595 | CVE-2025-22872 | golang.org/x/net | Incorrect Neutralization of Input During Web Page Generation in x/net in golang.org/x/net | +| GO-2026-4337 | CVE-2025-68121 | stdlib | Unexpected session resumption in crypto/tls | +| GO-2026-4340 | CVE-2025-61730, CVE-2025-61730 | stdlib | Handshake messages may be processed at the incorrect encryption level in crypto/tls | +| GO-2026-4341 | CVE-2025-61726, CVE-2025-61726 | stdlib | Memory exhaustion in query parameter parsing in net/url | +| GO-2026-4342 | CVE-2025-61728, CVE-2025-61728 | stdlib | Excessive CPU consumption when building archive index in archive/zip | +| GO-2026-4440 | CVE-2025-47911 | golang.org/x/net | Quadratic parsing complexity in golang.org/x/net/html | +| GO-2026-4441 | CVE-2025-58190 | golang.org/x/net | Infinite parsing loop in golang.org/x/net | +| GO-2026-4601 | CVE-2026-25679 | stdlib | Incorrect parsing of IPv6 host literals in net/url | +| GO-2026-4602 | CVE-2026-27139 | stdlib | FileInfo can escape from a Root in os | +| GO-2026-4603 | CVE-2026-27142 | stdlib | URLs in meta content attribute actions are not escaped in html/template | +| GO-2026-4864 | CVE-2026-32282 | stdlib | TOCTOU permits root escape on Linux via Root.Chmod in os in internal/syscall/unix | +| GO-2026-4865 | CVE-2026-32289 | stdlib | JsBraceDepth Context Tracking Bugs (XSS) in html/template | +| GO-2026-4869 | CVE-2026-32288 | stdlib | Unbounded allocation for old GNU sparse in archive/tar | +| GO-2026-4870 | CVE-2026-32283 | stdlib | Unauthenticated TLS 1.3 KeyUpdate record can cause persistent connection retention and DoS in crypto/tls | +| GO-2026-4918 | CVE-2026-33814 | golang.org/x/net, stdlib | Infinite loop in HTTP/2 transport when given bad SETTINGS_MAX_FRAME_SIZE in net/http/internal/http2 in golang.org/x/net | +| GO-2026-4946 | CVE-2026-32281 | stdlib | Inefficient policy validation in crypto/x509 | +| GO-2026-4947 | CVE-2026-32280 | stdlib | Unexpected work during chain building in crypto/x509 | +| GO-2026-4971 | CVE-2026-39836 | stdlib | Panic in Dial and LookupPort when handling NUL byte on Windows in net | +| GO-2026-4976 | CVE-2026-39825 | stdlib | ReverseProxy forwards queries with more than urlmaxqueryparams parameters in net/http/httputil | +| GO-2026-4977 | CVE-2026-42499 | stdlib | Quadratic string concatenation in consumePhrase in net/mail | +| GO-2026-4980 | CVE-2026-39826 | stdlib | Escaper bypass leads to XSS in html/template | +| GO-2026-4981 | CVE-2026-33811 | stdlib | Crash when handling long CNAME response in net | +| GO-2026-4982 | CVE-2026-39823 | stdlib | Bypass of meta content URL escaping causes XSS in html/template | +| GO-2026-4986 | CVE-2026-39820 | stdlib | Quadratic string concatentation in consumeComment in net/mail | +| GO-2026-5024 | CVE-2026-39824 | golang.org/x/sys | Invoking integer overflow in NewNTUnicodeString in golang.org/x/sys/windows | +| GO-2026-5025 | CVE-2026-42506 | golang.org/x/net | Invoking incorrect handling of namespaced elements in foreign content in golang.org/x/net/html | +| GO-2026-5026 | CVE-2026-39821 | golang.org/x/net | Invoking failure to reject ASCII-only Punycode-encoded labels in golang.org/x/net/idna | +| GO-2026-5027 | CVE-2026-42502 | golang.org/x/net | Invoking incorrect handling of HTML elements in foreign content in golang.org/x/net/html | +| GO-2026-5028 | CVE-2026-25680 | golang.org/x/net | Invoking denial of service when parsing arbitrary HTML in golang.org/x/net/html | +| GO-2026-5029 | CVE-2026-25681 | golang.org/x/net | Invoking incorrect handling of character references in DOCTYPE nodes in golang.org/x/net/html | +| GO-2026-5030 | CVE-2026-27136 | golang.org/x/net | Invoking duplicate attributes can cause XSS in golang.org/x/net/html | + +## monitoring-plugin (release-coo-ocp-4.19) + +### Commits analyzed + +- `b4c81a8 Merge pull request #1016 from jgbernalp/update-vulnerable-dependencies-4.19-26-06-2026` +- `f119969 fix: update vulnerable dependencies` +- `f1f7895 Merge pull request #1009 from openshift-cherrypick-robot/cherry-pick-1005-to-release-coo-ocp-4.19` +- `c1eaf8e fix(perses): fall back to metadata.name when dashboard display name is missing` + +### NPM Vulnerabilities Fixed + +| Advisory | CVE | Package | Severity | Title | +| ------------------------------------------------- | -------------- | --------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| https://github.com/advisories/GHSA-22p9-wv53-3rq4 | CVE-2026-48801 | linkify-it | high | LinkifyIt#match scan loop has quadratic algorithmic complexity | +| https://github.com/advisories/GHSA-2j2x-hqr9-3h42 | CVE-2026-40181 | react-router | moderate | React Router's same-origin redirect with path starting // causes open redirect via protocol-relative URL reinterpretation | +| https://github.com/advisories/GHSA-35p6-xmwp-9g52 | CVE-2026-6733 | undici | low | undici vulnerable to HTTP response queue poisoning via keep-alive socket reuse | +| https://github.com/advisories/GHSA-39q2-94rc-95cp | N/A | dompurify | moderate | DOMPurify's ADD_TAGS function form bypasses FORBID_TAGS due to short-circuit evaluation | +| https://github.com/advisories/GHSA-4x5r-pxfx-6jf8 | CVE-2026-49356 | @babel/core | low | @babel/core: Arbitrary File Read via sourceMappingURL Comment | +| https://github.com/advisories/GHSA-58qx-3vcg-4xpx | CVE-2026-45736 | ws | moderate | ws: Uninitialized memory disclosure | +| https://github.com/advisories/GHSA-64mm-vxmg-q3vj | CVE-2026-55602 | http-proxy-middleware | moderate | http-proxy-middleware `router` host+path substring matching allows Host-header-driven backend routing bypass | +| https://github.com/advisories/GHSA-76mc-f452-cxcm | N/A | dompurify | moderate | DOMPurify: Hook mutation of `data.allowedTags` / `data.allowedAttributes` permanently pollutes `DEFAULT_ALLOWED_TAGS` / `DEFAULT_ALLOWED_ATTR` | +| https://github.com/advisories/GHSA-96hv-2xvq-fx4p | CVE-2026-48779 | ws | high | ws: Memory exhaustion DoS from tiny fragments and data chunks | +| https://github.com/advisories/GHSA-cj63-jhhr-wcxv | N/A | dompurify | moderate | DOMPurify USE_PROFILES prototype pollution allows event handlers | +| https://github.com/advisories/GHSA-cjmm-f4jc-qw8r | N/A | dompurify | moderate | DOMPurify ADD_ATTR predicate skips URI validation | +| https://github.com/advisories/GHSA-cmwh-pvxp-8882 | N/A | dompurify | moderate | DOMPurify: Permanent `ALLOWED_ATTR` pollution via `setConfig()` bypassing the hook clone-guard (incomplete fix of the 3.4.7 hook-pollution patch) | +| https://github.com/advisories/GHSA-crv5-9vww-q3g8 | CVE-2026-41239 | dompurify | moderate | DOMPurify has a SAFE_FOR_TEMPLATES bypass in RETURN_DOM mode | +| https://github.com/advisories/GHSA-g8m3-5g58-fq7m | CVE-2026-11525 | undici | low | undici vulnerable to Set-Cookie SameSite attribute downgrade via permissive substring matching | +| https://github.com/advisories/GHSA-gvmj-g25r-r7wr | N/A | dompurify | low | DOMPurify: SAFE_FOR_TEMPLATES bypass - template expressions survive sanitization inside