| name | fix-cve-pr |
| description | Two-phase workflow for processing Jira CVE tickets across any project. Driven by a configurable JSON mapping file (component → repo → branch → local path). Phase 1: analyze each ticket and write cve_summaries/<release>/*.md resolution files plus a combined _phase1_summary.md. Phase 2: after user review, create or update PRs (new, grouped, or stacked on existing). Detects open PRs touching the same go.mod to avoid merge conflicts. Use when the user says "fix this CVE", "process these CVE tickets", "analyze CVE issues", or provides a Jira CSV export or list of ACM/OADP/other Jira keys with CVE titles. |
Fix CVE PR — Two-Phase Workflow
Configuration — mapping file
All project-specific mappings live in a single JSON file. The skill reads this file at the start
of every run. You must tell the agent which mapping file to use (or it will default to the
Global Hub one below).
Default mapping file (Global Hub)
workflows/cve-service/config/repo_mapping.json
How to create a mapping file for another project
Copy the sample below and adapt the four sections:
| Field | What to set |
|---|
project | Short human-readable name |
jira_project_key | Jira project key (e.g. ACM, OADP) |
component_to_repo | Map each pscomponent slug that appears in Jira ticket titles to its GitHub org/repo |
repo_version_to_branch | For each repo, map product version strings (from [project-X.Y] in titles) to Git branch names |
ga_baseline_versions | Map minor version → GA patch shipped to customers (e.g. 1.4 → 1.4.6). Required for closed GA lines. |
ga_baseline_publish_dates | Map GA patch → ISO publish date (e.g. 1.4.6 → 2026-06-03). Fixes merged after this date are not in the GA build — still active for customers. Required for closed GA lines. |
local_repo_paths | Absolute path to each repo clone on your machine (used to read go.mod) |
Sample — multicluster-global-hub (workflows/cve-service/config/repo_mapping.json):
{
"project": "multicluster-global-hub",
"jira_project_key": "ACM",
"component_to_repo": {
"multicluster-globalhub-agent-rhel9": "stolostron/multicluster-global-hub",
"multicluster-globalhub-grafana-rhel9": "stolostron/glo-grafana",
"multicluster-globalhub-postgres-exporter-rhel9": "stolostron/postgres_exporter",
"multicluster-globalhub-operator-rhel9": "stolostron/multicluster-global-hub",
"multicluster-globalhub-manager-rhel9": "stolostron/multicluster-global-hub",
"redhat-user-workloads/glo-grafana-globalhub-1-4":"stolostron/glo-grafana"
},
"repo_version_to_branch": {
"stolostron/multicluster-global-hub": {
"1.4": "release-2.13",
"1.5": "release-2.14"
},
"stolostron/glo-grafana": {
"1.4": "release-1.4",
"1.5": "release-1.5"
}
},
"local_repo_paths": {
"stolostron/multicluster-global-hub": "/home/user/repos/multicluster-global-hub",
"stolostron/glo-grafana": "/home/user/repos/glo-grafana"
}
}
Example for a different project (OADP / ACM Backup):
{
"project": "oadp",
"jira_project_key": "OADP",
"component_to_repo": {
"oadp-operator-container": "openshift/oadp-operator",
"oadp-velero-container": "openshift/velero"
},
"repo_version_to_branch": {
"openshift/oadp-operator": {
"1.4": "release-1.4",
"1.3": "release-1.3"
},
"openshift/velero": {
"1.4": "release-1.4",
"1.3": "release-1.3"
}
},
"local_repo_paths": {
"openshift/oadp-operator": "/home/user/repos/oadp-operator",
"openshift/velero": "/home/user/repos/velero"
}
}
If the user provides a mapping file path, use it. If they don't, use the default Global Hub one.
If no mapping file exists yet, stop and tell the user:
"No mapping file found. Please create one at <path> using the template above."
Phase 1 — Analyze & write resolution summaries
For each Jira issue provided, produce a markdown summary file and wait for user review before touching any repo.
Input — accepted formats
Option A: Jira CSV export (preferred for bulk)
Export directly from the Jira issue list:
Issues list → "..." menu → Export → "Export CSV (current fields)"
If the user says "use a CSV" or "I exported from Jira" but hasn't given a path,
ask: "What is the path to the CSV file?" before proceeding.
The agent reads Issue Key and Summary columns (all other columns are ignored).
Option B: Paste key + title pairs
ACM-31131 "CVE-2026-25679 multicluster-globalhub/multicluster-globalhub-manager-rhel9: Go net/url [multicluster-globalhub-1.4]"
ACM-31781 "CVE-2026-25679 multicluster-globalhub/multicluster-globalhub-manager-rhel9: Go net/url [multicluster-globalhub-1.3]"
Option C: Key-only list (requires JIRA_TOKEN env var for auto-fetch)
ACM-31131, ACM-31781, ACM-31939
Step 1.0 — Pre-filter: skip tickets that are already handled
Apply two independent skip rules before doing any CVE analysis. Either rule alone is
sufficient to skip a ticket. List all skipped tickets in the summary table.
Rule A — Jira Status already progressed
The Jira CSV export includes a Status column. If the status is Backlog or
In Progress, the ticket has already been triaged/processed. Skip it.
SKIP_STATUSES = {"Backlog", "In Progress"}
skip = row["Status"].strip() in SKIP_STATUSES
Why: Moving a ticket to Backlog or In Progress means the CVE has been acknowledged
and a fix is already being tracked. Re-processing it would duplicate work.
Rule B — Open PR already linked
The Jira CSV export includes a Development column. When a ticket has a linked open
GitHub PR, this column contains "open":true.
skip = skip or ('"open":true' in row["Development"])
Why: The Development field is the canonical place Jira stores linked PR state.
If a PR is open, the fix is already in flight.
Both rules together in the pre-filter:
SKIP_STATUSES = {"Backlog", "In Progress"}
def should_skip(row):
if row.get("Status", "").strip() in SKIP_STATUSES:
return "status=" + row["Status"].strip()
if '"open":true' in row.get("Development", ""):
return "PR already open"
return None
Step 1.0b — Cross-reference already-processed CVEs for the same release
Before doing any OSV/NVD lookups or go.mod fetches, check whether the same release version
already has processed MD files in the cve_summaries folder:
workflows/cve-service/src/cve_summaries/<release>/
where <release> is the version string extracted from the ticket (e.g. 1.4, 1.7).
How to use this data:
- Glob the folder for
*_latest.md files. If the folder is empty or missing, skip this step.
- For each new ticket, check if the same CVE ID already appears in an existing MD file in
that same
<release>/ folder (grep for the CVE ID string).
- If found:
- Read the existing MD to extract its status (
ALREADY_FIXED, NEEDS_FIX, FALSE_POSITIVE, etc.),
the fix PR (if any), and the go.mod version that was recorded.
- Use this as a starting point for the new ticket — it likely affects the same repo/branch,
so the same investigation findings apply (same module version, same fix).
- Still verify the current
go.mod on the branch to confirm the status hasn't changed
since the prior file was written (e.g., a PR may have merged in the meantime).
- Check
_phase1_summary.md in the same folder — it contains a consolidated view of all
prior tickets including ALREADY_FIXED entries and PR numbers. Use this to:
- Detect if a pending "PR C / PR D" from a prior batch would also fix the new tickets.
- Detect if Go toolchain bumps or transitive dependency pulls already resolved the new CVE.
Why this matters: ProdSec often files separate tickets per component (operator / manager / agent)
for the same CVE and module. If the first ticket in a batch was analyzed and found ALREADY_FIXED
(e.g., Go toolchain bump pulled the module transitively), the subsequent tickets are the same fix.
Checking existing MDs avoids redundant OSV lookups and catches cases where the fix was already
applied by a previous PR in that release cycle.
Important: Only use the same <release>/ folder. Do NOT use data from other release
folders (e.g., 1.3 data for 1.4 tickets) as the go.mod versions and branch states differ.
Step 1.1 — Parse each title
Extract per ticket:
| Field | Source | Example |
|---|
| Jira key | arg | ACM-31131 |
| CVE ID(s) | regex in title | CVE-2026-25679 |
| Component slug | substring match | multicluster-globalhub-manager-rhel9 |
| GH version | [multicluster-globalhub-X.X] | 1.4 |
| Repo | component → repo map | stolostron/multicluster-global-hub |
| Branch | version → branch map | release-2.13 |
Component → repo + branch mapping:
Read from the mapping file (component_to_repo and repo_version_to_branch fields).
Do not hardcode mappings; always load them from the JSON file specified by the user (or the default).
Repos in the default Global Hub mapping: stolostron/multicluster-global-hub,
stolostron/glo-grafana, stolostron/postgres_exporter. Jira titles use the same
[multicluster-globalhub-X.Y] version tag regardless of which component/repo is affected
(e.g. multicluster-globalhub-postgres-exporter-rhel9 → stolostron/postgres_exporter on
release-2.13 for GH 1.4).
Step 1.1b — Closed GA branches: validate against shipped baseline (CRITICAL)
Read ga_baseline_versions and ga_baseline_publish_dates from the mapping file. For Global
Hub, closed GA patches include: 1.3.4, 1.4.6, 1.5.5, 1.6.3, 1.7.1, 1.8.0.
Post-GA security work (rpm lockfile refreshes, Konflux Mintmaker deps PRs, dependency bumps
merged after GA publish) updates git on the release branch but does not change what customers
received in the GA build unless a z-stream is cut and released.
GA publish date cutoff (CRITICAL):
For ticket [multicluster-globalhub-X.Y], resolve the GA patch from ga_baseline_versions
(e.g. 1.4 → 1.4.6) and its publish date from ga_baseline_publish_dates
(e.g. 1.4.6 → 2026-06-03 — GLO 1.4.6 / RHSA prod ship).
| Fix timing | Customer impact | Status to use |
|---|
| CVE not applicable to GA build (wrong binding, dep absent, etc.) | None | FALSE_POSITIVE / NA |
| CVE applicable to GA; fix merged after GA publish date | Active CVE for GA customers | FIXED_IN_GIT_NOT_IN_GA (or NEEDS_FIX if no fix in git yet) |
| CVE applicable to GA; fix merged on or before GA publish date | May be in GA snapshot | Confirm from Konflux prod release / snapshot, not branch HEAD today |
| Fix shipped in a released z-stream after GA | Resolved for customers on that z-stream | ALREADY_FIXED |
Never mark ALREADY_FIXED because branch HEAD has a patched go.mod if the fix merged
after the GA publish date. Example: GLO 1.4.6 published 2026-06-03 — any security
fix merged after that date is not in the GA build; treat as an active CVE until a z-stream
ships.
When triaging a CVE filed against [multicluster-globalhub-X.Y] for any mapped component
(agent, manager, operator, grafana, postgres-exporter):
- Establish the GA baseline first — GA patch version + publish date from the mapping file;
Konflux prod release snapshot or errata (e.g.
release-summary.json, RHSA) for what was
actually built/shipped. Do not use current branch HEAD as the customer-facing version.
- Check applicability against the GA baseline — is the vulnerable module/version present
in the GA build?
- Compare fix PR merge date to GA publish date — if a fix exists in git, fetch
mergedAt:
gh pr view <PR> --repo <org/repo> --json mergedAt,title
mergedAt after ga_baseline_publish_dates[<ga_patch>] → FIXED_IN_GIT_NOT_IN_GA
mergedAt on or before publish date → fix may be in GA; confirm from snapshot
- Then check branch HEAD separately — post-GA fixes queued but not yet released?
- Check open Mintmaker PRs on the target branch —
stolostron/postgres_exporter often has
dozens of open red-hat-konflux bot PRs bumping the same modules (see PR review doc).
If ga_baseline_publish_dates[<ga_patch>] is null, ask the user or check prod release
records before using ALREADY_FIXED.
Resolution outcomes for closed GA lines:
| Outcome | When | Jira disposition |
|---|
| FALSE_POSITIVE / NA | CVE does not affect the GA build (wrong component, dep not in image, wrong binding, version not in vulnerable range, etc.) | Close N/A with VEX vulnerable_code_not_present or equivalent |
| FIXED_IN_GIT_NOT_IN_GA | CVE is applicable to the GA build; fix merged after GA publish date (or not yet in a released z-stream) | Document: active for GA X.Y.Z customers; fix in git (PR #NNN, merged YYYY-MM-DD) but not in shipped GA. Do not mark ALREADY_FIXED. Future z-stream TBD. |
| SKIP (PR already open) | Mintmaker/Renovate PR already open on the branch for the same module | Link the open PR; do not open a duplicate |
| NEEDS_FIX | CVE applicable to GA build and fix is not in GA build and not yet on branch HEAD | Open PR or escalate; note whether a z-stream is possible for this closed line |
| ALREADY_FIXED | Fix was in the GA Konflux snapshot at publish or in a released z-stream already available to customers | Close with VEX / Won't Do as appropriate |
Common mistake: Seeing a patched version on release-2.14 HEAD (postgres_exporter GH 1.5)
and closing as ALREADY_FIXED when GA 1.5.5 images were built before Mintmaker PR #131
merged. Always compare: GA shipped (version + publish date + snapshot) vs git HEAD
vs fix PR mergedAt.
postgres_exporter example:
curl -s "https://raw.githubusercontent.com/stolostron/postgres_exporter/release-2.14/go.mod" \
| grep 'golang.org/x/crypto'
gh pr list --repo stolostron/postgres_exporter --state open --base release-2.14 \
--json number,title | grep -i crypto
Step 1.2 — Look up CVE technical details
Query OSV API (Go ecosystem):
curl -s "https://api.osv.dev/v1/vulns/<CVE-ID>"
Extract: Go module path, vulnerable range, exact fixed version.
Also query NVD for description:
curl -s "https://services.nvd.nist.gov/rest/json/cves/2.0?cveId=<CVE-ID>"
If OSV returns no Go ecosystem entry, the CVE may not be a Go module issue. Before marking
MANUAL_REVIEW, check whether the module appears in go.sum on the remote branch:
curl -s "https://raw.githubusercontent.com/<org>/<repo>/<branch>/go.sum" | grep "<module>"
Interpret the results:
- No entries at all → module is not in the dependency graph → FALSE_POSITIVE
- Only
/go.mod h1: entries → module go.mod was fetched for graph resolution but code was never downloaded/compiled → FALSE_POSITIVE
- Plain
h1: entries (without /go.mod) → module code is compiled → investigate further
go.work.sum vs go.sum: If a module appears in go.work.sum but NOT in go.sum, it is
pulled in only by a workspace sub-module (e.g. ./pkg/apiserver) used for local development or
tooling. Container images are built from the root module only — workspace sub-modules are
excluded from the container build. This is still a FALSE_POSITIVE for the shipping binary.
Always check go.sum (not go.work.sum) as the authoritative source for what is in the image.
Step 1.3 — Read current version from go.mod
Always fetch go.mod from the remote GitHub branch, never from the local clone.
The local checkout may be on a feature branch with different versions.
curl -s "https://raw.githubusercontent.com/<org>/<repo>/<branch>/go.mod" | grep '<module>'
Examples:
curl -s "https://raw.githubusercontent.com/stolostron/multicluster-global-hub/release-2.13/go.mod" \
| grep 'golang.org/x/net'
curl -s "https://raw.githubusercontent.com/stolostron/postgres_exporter/release-2.14/go.mod" \
| grep 'golang.org/x/crypto'
After confirming NEEDS_FIX, also check for open PRs that may already fix the issue:
gh pr list --repo <org/repo> --state open --base <branch> \
--json number,title,headRefName | grep -i "<module-or-ACM-key>"
If an open PR already touches the relevant module or mentions a related ACM key → mark as
SKIP (PR already open) rather than NEEDS_FIX, and link the PR in the summary.
Determine status:
FIXED_IN_GIT_NOT_IN_GA — CVE applies to the GA baseline build, but branch HEAD now has
the fix (post-GA merged PR or open Mintmaker PR queued). Customers on the GA release do not
have it yet. Use for closed GA lines (see Step 1.1b). Do not conflate with ALREADY_FIXED.
FIXED_BY_PR — remote go.mod version ≥ patched version due to a merged PR, but the z-stream has not yet been released. Use this instead of ALREADY_FIXED whenever the fix is not yet shipped to customers. In the recommended action, say "Fixed in PR #NNN … will be delivered with GH X.Y.Z z-stream release."
ALREADY_FIXED — remote go.mod version ≥ patched version AND the fix was already shipped in a previous z-stream release (i.e., the release containing the fix is already generally available). Use this only when the version has shipped. For closed GA lines, confirm the fix was in the GA baseline build, not just on branch HEAD.
NEEDS_FIX — remote go.mod version < patched version AND no open PR found
SKIP (PR already open) — open PR exists that bumps the same module or fixes the same CVE (common on postgres_exporter Mintmaker backlog)
MANUAL_REVIEW — CVE not in OSV Go index (OS/RPM-level, not a Go module)
Step 1.4 — Write resolution summary
Extract the release version from the ticket titles (e.g. [multicluster-globalhub-1.4] → 1.4).
Write each summary to a release-scoped subfolder:
workflows/cve-service/src/cve_summaries/<release>/<ACM-KEY>_latest.md
Example: cve_summaries/1.4/ACM-31131_latest.md
Create the folder if it doesn't exist:
mkdir -p workflows/cve-service/src/cve_summaries/<release>
Template:
# CVE Summary: <ACM-KEY>
**Status:** <NEEDS_FIX | ALREADY_FIXED | MANUAL_REVIEW>
| Field | Value |
|-------|-------|
| Jira Key | [<ACM-KEY>](https://redhat.atlassian.net/browse/<ACM-KEY>) |
| CVE | <CVE-ID> |
| CVSS | <score> (<severity>) |
| Summary | <one-line title> |
| Component | <pscomponent slug> |
| Repo | <org/repo> |
| Branch | <release-branch> |
| GA Baseline | Global Hub <X.Y.Z> |
| GA Publish Date | <YYYY-MM-DD from ga_baseline_publish_dates, or TBD> |
| Fix Version | Global Hub <X.Y.Z> |
| Jira Status | <New / In Progress> |
## Description
<2-3 sentences from Jira/NVD explaining the flaw>
## Investigation
<Explain what was checked: go.mod version, go mod graph, OSV lookup, whether the vulnerable
code path is reachable. Be specific — version numbers, file names, module paths.>
## Resolution / Recommended Jira Close Comment
<Choose the appropriate block below and fill it in:>
---
### If NEEDS_FIX — action required (paste into Jira before opening PR):
> Bump `<module>` from `<current>` to `<patched-version>` — PR incoming.
---
### If ALREADY_FIXED — paste into Jira as close comment:
> **Investigation complete — fix already applied**
>
> <CVE-ID> describes <brief flaw>. The fix requires <library/Go> ≥ <version>.
>
> The `<org/repo>` repository (<branch>) already specifies `<go directive / module version>`
> in `go.mod`. The patched code is already in use — the vulnerable code path no longer exists
> in the build.
>
> Closing as **Won't Do / Already Fixed**. VEX justification: `vulnerable_code_not_present` —
> the fix is present in the toolchain/dependency version currently used by this component.
---
### If MANUAL_REVIEW / not applicable — paste into Jira as close comment:
**Option A — dependency not present:**
> **False Positive — Vulnerable code not in execution path**
>
> Investigation confirmed that `<module>` is not a dependency of `<org/repo>`. It does not appear
> in `go.mod` or any application package manifests. The scanner flagged this because <reason>.
> The vulnerable code is never executed in the context of the `<component>` container.
>
> Closing as **Won't Do / Not Applicable**. VEX justification: `vulnerable_code_not_present`.
**Option B — wrong version / wrong Go line:**
> **Investigation complete — not applicable to this component/version**
>
> <CVE-ID> describes <flaw>. This CVE only affects <library/Go> <specific version range>.
> It was <introduced / fixed> in <version> and <does not exist / was already fixed> in <other version>.
>
> The `<org/repo>` repository (<branch>) uses <current version> — the vulnerable code path
> does not exist in this version line.
>
> Closing as **Won't Do / Not Applicable**. VEX justification: `vulnerable_code_not_present` —
> the vulnerable code was <introduced in / fixed before> <version> and is not present here.
Step 1.5 — Write and present summary table
After writing all individual MD files, also write a combined summary table to:
workflows/cve-service/src/cve_summaries/<release>/_phase1_summary.md
Template for _phase1_summary.md:
# Phase 1 Summary — <release> (processed <date>)
## SKIP — Already Processed or PR Open
| Key | CVE | Reason |
|-----|-----|--------|
| [ACM-XXXXX](./ACM-XXXXX_latest.md) | CVE-XXXX-XXXXX | status=Backlog / status=In Progress / [repo#NNN](url) |
## ALREADY_FIXED
| Key | CVE | Reason |
|-----|-----|--------|
| [ACM-XXXXX](./ACM-XXXXX_latest.md) | CVE-XXXX-XXXXX | go.mod already at Go X.Y.Z |
## NEEDS_FIX — Actionable
| Key | CVE | Fix | Repo/Branch |
|-----|-----|-----|-------------|
| [ACM-XXXXX](./ACM-XXXXX_latest.md) | CVE-XXXX-XXXXX | bump module vA → vB | repo/branch |
## MANUAL_REVIEW
| Key | CVE | Why Manual |
|-----|-----|-----------|
| [ACM-XXXXX](./ACM-XXXXX_latest.md) | CVE-XXXX-XXXXX | reason |
Then print the same table to the user in chat.
Stop here. Wait for user review and instructions.
Phase 2 — Create PRs (after user review)
User will instruct one of:
- "Create a new PR for ACM-XXXXX"
- "Add ACM-XXXXX to PR #NNN"
- "Create one PR for all 1.4 tickets"
- "Create one PR per release"
Step 2.1 — Group tickets (if creating grouped PRs)
Group NEEDS_FIX tickets by (repo, branch). Each group = one PR.
Step 2.2 — Check for conflicting open PRs
For each target (repo, branch):
gh pr list --repo <org/repo> --state open --base <branch> \
--json number,title,headRefName,files
Then check if any open PR already modifies go.mod:
gh pr diff <PR-number> --repo <org/repo> | grep "^+++ b/go.mod"
If a conflicting PR is found, present options to the user:
- Stack on top of PR #NNN — checkout that branch, apply fix on top, push (PR auto-updates)
- Wait for PR #NNN to merge first — do nothing now, revisit after merge
- Create independent PR — may require rebase later (warn the user)
Wait for user choice before proceeding.
Step 2.3 — Apply fix and commit
Stack on existing branch (Option A):
cd <repo>
git fetch origin && git checkout <existing-branch> && git pull origin <existing-branch>
go get <module>@<patched-version>
make vendor
git add go.mod go.sum vendor/
git commit --signoff -m "Security: Fix <KEYS> - <CVE> [multicluster-globalhub-X.X]
- Bump <module> from <old> to <new> to fix <CVE-ID>"
git push origin <existing-branch>
New branch (Option B):
cd <repo>
git fetch origin && git checkout <release-branch> && git pull
git checkout -b fix/<acm-key-slugs>
go get <module>@<patched-version>
make vendor
git add go.mod go.sum vendor/
git commit --signoff -m "Security: Fix <KEYS> - <CVE> [multicluster-globalhub-X.X]
- Bump <module> from <old> to <new> to fix <CVE-ID>"
git push origin fix/<acm-key-slugs>
Step 2.4 — Create or update PR
PR body must include Jira ticket title and description for every Jira key fixed by the PR.
Use this template for the --body:
## Summary
Fixes: <ACM-KEY1>, <ACM-KEY2>, ...
### <ACM-KEY1> — <Jira ticket title>
<Jira ticket description / CVE description, 2-3 sentences>
### <ACM-KEY2> — <Jira ticket title>
<Jira ticket description / CVE description, 2-3 sentences>
## Changes
- Bump `<module>` from `<old>` to `<new>` to fix <CVE-ID>
New PR:
gh pr create \
--repo <org/repo> \
--title "Security: Fix <KEYS> - <CVE-ID> <pkg>: <description> [multicluster-globalhub-X.X]" \
--base <release-branch> \
--label "dco-signoff: yes" \
--body "$(cat <<'EOF'
## Summary
Fixes: ACM-XXXXX
### ACM-XXXXX — <Jira ticket title>
<CVE description>
## Changes
- Bump `<module>` from `<old>` to `<new>` to fix <CVE-ID>
EOF
)"
Stacked on existing PR: just push; optionally add a comment:
gh pr comment <PR-number> --repo <org/repo> \
--body "Added fix for <KEYS> (<CVE-ID>): bumped \`<module>\` to \`<version>\`."
PR title format (canonical)
Security: Fix <KEY1> / <KEY2> - <CVE-ID> <pkg short name>: <description> [multicluster-globalhub-X.X]
Examples:
Security: Fix ACM-31131 - CVE-2026-25679 Go net/url: Incorrect parsing of IPv6 host literals [multicluster-globalhub-1.4]
Security: Fix ACM-31791 / ACM-31790 - CVE-2026-33186 gRPC-Go Auth bypass [multicluster-globalhub-1.6]
Known Patterns and Gotchas
Stacking on multiple open PRs for the same release branch
When a release branch already has multiple independent open PRs (not a single linear stack),
build the new branch on top of all of them to avoid future merge conflicts:
gh pr list --repo <org/repo> --base <branch> --state open --json number,headRefName,headRefOid
git log --oneline <upstream>/<branch>..<fork>/<pr-branch>
git checkout origin/<most-advanced-pr-branch> -b fix/<new-acm-key>
git cherry-pick <sha-from-independent-pr>
After creating the PR, add a do-not-merge/hold label so it cannot merge before its base PRs land:
gh pr edit <PR-number> --repo <org/repo> --add-label "do-not-merge/hold"
Document the stack in the PR body:
### Stack
This branch is built on top of all open <branch> PRs to avoid merge conflicts:
- #NNN (description)
- #MMM (description) — cherry-picked
When base PRs have been merged, rebase the stacked branch onto the updated release branch,
then remove the hold:
gh pr edit <PR-number> --repo <org/repo> --remove-label "do-not-merge/hold"
pr/external label
The pr-external-labelling.yml GitHub Actions workflow automatically adds pr/external to PRs
from non-MEMBER contributors. It fires only on pull_request_target: opened.
DCO sign-off
Always use git commit -s (or --signoff). If you forgot on the last commit:
git commit --amend -s --no-edit
git push <remote> <branch> --force-with-lease
The DCO check (dco status check on GitHub) will fail if any commit in the PR lacks
Signed-off-by: Name <email>.
go.sum — intermediate entries for Cachi2 hermetic builds
When bumping a Go module (e.g. apache/thrift v0.18.1 → v0.23.0), the Konflux Cachi2
prefetch step requires /go.mod h1: entries for all intermediate versions in go.sum,
not just the final version. Without them, go mod verify fails in the hermetic build.
Pattern: fetch the entries from a branch where the bump was already done and verified:
git show <verified-branch>:go.sum | grep "<module>"
Then manually insert the missing intermediate entries in sorted version order.
go.work must match go.mod
When bumping the Go toolchain version in go.mod, also update go.work to the same version.
If they diverge, the Konflux prefetch-dependencies step fails with:
go: module . listed in go.work file requires go >= X.Y.Z, but go.work lists go A.B.C
Fix:
sed -i 's/^go A.B.C/go X.Y.Z/' go.work
gRPC v1.79.3+ interface compatibility (glo-grafana)
google.golang.org/grpc v1.79.3 added a List() method to the grpc_health_v1.HealthServer
interface. Any struct implementing HealthServer will fail to compile unless it embeds
grpc_health_v1.UnimplementedHealthServer:
type healthServer struct {
grpc_health_v1.UnimplementedHealthServer
entityServer EntityStoreServer
}
Apply this fix whenever upgrading grpc in stolostron/glo-grafana.
glo-grafana Containerfile — no minor-version pin
Containerfile.konflux uses rhel_9_1.25 (no minor version tag):
FROM brew.registry.redhat.io/rh-osbs/openshift-golang-builder:rhel_9_1.25 AS builder
This means no Containerfile change is needed when bumping Go within the 1.25.x line
(e.g. 1.25.7 → 1.25.8 → 1.25.9). Only update the go directive in go.mod and go.work.
Consolidating multiple CVE fixes into one PR
When the user has separate PRs planned for the same (repo, branch), consider consolidating
them into a single PR to reduce review overhead. This is especially useful when:
- All fixes touch only
go.mod / go.sum / go.work (no code changes)
- The PR is already on hold waiting for base PRs to merge
Update the PR title and body to list all Jira keys when consolidating.
Quick checklist
Phase 1:
- [ ] Pre-filter applied: skip Status=Backlog or Status=In Progress → mark SKIP (already processed)
- [ ] Pre-filter applied: skip Development contains "open":true → mark SKIP (PR already open)
- [ ] Existing cve_summaries/<release>/*_latest.md files checked — same CVE already recorded? → reuse status/fix PR, verify go.mod still matches
- [ ] _phase1_summary.md for same release checked — pending PRs that would also fix new tickets?
- [ ] All titles parsed: CVE ID, component, version, repo, branch
- [ ] OSV queried for each CVE → module + patched version
- [ ] go.mod fetched from **remote GitHub branch** (not local clone) → current version per repo/branch
- [ ] Open PRs checked — any PR already fixing the module? → mark SKIP if found (check Mintmaker backlog on postgres_exporter)
- [ ] For closed GA minors (1.3–1.8): validated against **GA baseline version + publish date** from `ga_baseline_publish_dates`, not branch HEAD alone
- [ ] Fix PR `mergedAt` compared to GA publish date — post-publish merges → FIXED_IN_GIT_NOT_IN_GA (active CVE for GA), never ALREADY_FIXED
- [ ] Status chosen correctly: FIXED_IN_GIT_NOT_IN_GA (post-GA publish, not in shipped build) vs FIXED_BY_PR (merged PR, z-stream not yet shipped) vs ALREADY_FIXED (fix in GA snapshot or released z-stream)
- [ ] MD summary written to cve_summaries/<release>/<KEY>_latest.md
- [ ] Combined table written to cve_summaries/<release>/_phase1_summary.md
- [ ] Summary table printed — WAITING FOR USER REVIEW
Phase 2:
- [ ] User specified PR strategy (new / grouped / stacked)
- [ ] Open PRs on same branch checked for go.mod conflicts
- [ ] User chose: stack / wait / independent (if conflict found)
- [ ] If stacking on multiple open PRs: cherry-picked all relevant commits; do-not-merge/hold label added
- [ ] go.mod shows updated version
- [ ] go.work updated to match go.mod Go version (if Go toolchain was bumped)
- [ ] go.sum intermediate /go.mod h1: entries added for all versions between old and new (if module bumped)
- [ ] git commit --signoff (-s) created — DCO sign-off present
- [ ] Branch pushed (force-with-lease if amended)
- [ ] PR created/updated with correct title + body listing all Jira keys
- [ ] PR body includes Jira ticket title + CVE description for each key fixed
- [ ] pr/external label added manually if workflow failed (or removed if not needed)