feat(security-issue-sync): five pre-push hygiene gates for CVE-record quality#372
Merged
potiuk merged 4 commits intoMay 29, 2026
Merged
Conversation
…ort public summary on every push The *"Short public summary for publish"* body field powers the published CVE description that end users read in the advisory. A summary that names the vulnerability accurately but lacks the concrete upgrade-target version (e.g. *"upgrade to the Airflow version that contains the fix"* without naming `3.3.0`) is a defect — it forces the reader to open another tab to figure out which version to pin, exactly the friction the advisory is supposed to remove. The skill already enforces this as a gate at the `pr merged → fix released` transition. Widen the rule so it fires on **every** sync pass that proposes a body-field update or a JSON regen, not only at one boundary. Two changes: 1. Step 2b paragraph: explicitly call out the "every sync pass" trigger plus the per-scope target shapes (`apache-airflow X.Y.Z or later`, `apache-airflow-providers-<name> X.Y.Z or later`, `apache-airflow-helm-chart X.Y.Z or later`). 2. Step 1d signal-table row: detector for "summary populated but does not name a concrete upgrade-target version". Surfaces the rewrite proposal on every qualifying pass so the tightening lands lock-step with the next JSON regen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…her-CVE summary rules Two complementary additions to security-issue-sync, both validated on every sync pass (not gated to one transition): 1. Issue-title hygiene. The GitHub issue title is read verbatim by generate-cve-json into containers.cna.title, so it ships to cve.org and the published advisory. Re-use the same title-strip cascade security-cve-allocate applies at allocation time — project-name tokens, internal split-markers, report-form classifiers, external-tracker IDs, version-noise suffixes. Titles drift between allocation and the final regen, so the cascade has to re-run on every sync. Preserve stripped context as audit trail in body / rollup; never silently drop it. 2. Incomplete-fix-to-another-CVE summary expansion. When a tracker is a follow-up to a previously-PUBLISHED CVE — typically because the original root cause spanned multiple products (provider + core, chart + core) and the team split per product/package — the Short public summary for publish must do more than describe this tracker's vulnerability in isolation. It must (1) name the prior CVE explicitly, (2) state the prior fix did not cover the current product, (3) tell users who already applied the prior fix to also apply this one. Without this cross-CVE + cross-product framing, downstream readers see two CVEs about the same root-cause class and assume one fix covered both — missing that two upgrades are needed. Both rules are detector + propose-rewrite. The user confirms each summary/title edit before regen + push to Vulnogram, same as the existing summary-quality rule. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…n) in Short public summary
Extend the Step 2b summary-quality rule and the matching Step 1d
detector with a fourth gate: the summary must state the triggering
conditions, not only the bug mechanism.
The reader scans the published advisory asking "does this affect
us?" — and the answer comes from trigger context, not from a
mechanical description of the bug. Concretely the summary must
make these three things unambiguous in one sentence each (in any
order):
WHO — attacker role / required capability (an authenticated
UI user with Op permissions, a Dag author, a partner
with write access to the bucket, a worker holding a
valid Execution-API JWT, etc.).
WHEN — deployment shape / config / feature that has to be
active (when [opensearch] host embeds credentials,
when the Kubernetes executor is configured, affects
deployments where the webserver is reachable by
untrusted users, etc.).
ACTION — the step the attacker takes against which surface
(follows a crafted next= redirect URL, uploads an
object containing .. path segments, PATCHes the
deferred-state endpoint with crafted next_kwargs,
etc.).
A summary without all three forces the reader to open the issue
PR / patch to figure out the trigger — exactly the work the
advisory should remove.
Step 2b paragraph: enumerate the three required elements with
worked example wordings (positive + negative).
Step 1d signal-table row: detector heuristic looks for phrases
that mark each of the three elements (the regex set is in the
row); fires when fewer than two of three are unambiguously
present.
Apply this gate in parallel with the upgrade-target rule (same
sync pass) so a rewrite covers both gates at once.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…hygiene-gate scan
Two more pre-push hygiene gates on top of the four existing ones
(upgrade-target version, trigger conditions, title cleanup,
incomplete-fix cross-CVE clause):
1. CWE field must carry the long-form `CWE-NNN: <Canonical
Title>` shape, not just a bare `CWE-NNN` token. Examples:
`CWE-22: Improper Limitation of a Pathname to a Restricted
Directory ('Path Traversal')`, `CWE-502: Deserialization of
Untrusted Data`, `CWE-601: URL Redirection to Untrusted Site
('Open Redirect')`. The value lands verbatim in the CVE
record's `problemTypes[].descriptions[].description`; readers
don't memorise MITRE numbering. Prefer a CWE from the
project's *advised CWEs* list when one is declared.
2. Step 5b pre-push hygiene-gate scan. Before any
`vulnogram-api-record-update` push, re-scan the JSON about to
be pushed for all five pre-push gates: title strip cascade,
upgrade-target version, trigger conditions, incomplete-fix
cross-CVE clause, CWE long form. When any gate fails the
regenerated JSON, the recovery is to fix the underlying body
field (or title), re-regen, then re-scan — not to push a
degraded record. The gates protect against the case where
body fields drift between the Step 2b proposal cycle and the
actual push (e.g. a user applied only a subset of the proposed
updates).
The pre-push scan is the safety net that catches the "body
proposal landed but the push went out on a stale or partial
edit" failure mode; the individual Step 1d detector rows surface
the rewrite proposals on every sync pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Five pre-push hygiene gates added to
security-issue-sync. All five are validated on every sync pass (not gated to a single transition); each one is a Step 1d detector + Step 2b propose-rewrite that surfaces in the proposal and applies after user confirmation. A unified Step 5b pre-push hygiene-gate scan re-runs all five gates against the regenerated JSON immediately before anyvulnogram-api-record-updatepush, refusing the push when any gate fails the regenerated content (catches the case where body fields drift between the Step 2b proposal cycle and the actual push — e.g. user applied only a subset of proposed updates).The five gates
<package> <X.Y.Z>pattern[GHSA-...]/(ZDRES-...)/(HUNTR-...)/(GHSL-...)external IDs,(split from #NNN)internal markers,[Security Report]classifiers, version-noise suffixessecurity-cve-allocateStep 2 title-strip cascade; preserve stripped context as audit trail in body / rollupCWE-NNNtoken without: <Canonical Title>CWE-NNN: <Canonical Title>; prefer the project's advised CWEs list when declaredStep 5b pre-push hygiene-gate scan
A new sub-step in Step 5b (between the skip-condition gate and the session probe) re-runs all five gates on the JSON about to be pushed. When any gate fails the regenerated JSON, the recovery is to fix the underlying body field (or title), re-regen, then re-scan — not to push a degraded record. This protects against the "body proposal landed but the push went out on a stale or partial edit" failure mode.
Real-world examples (caught during ad-hoc sync, motivating these rules)
airflow-s/airflow-s#259(CVE-2026-40961) — generic "upgrade to the Airflow version that contains the fix" survived multiple sync passes."Open Redirect Bypass Vulnerability in Apache Airflow", within Apache Airflowtrailing the title and shipping into the CVE record'stitlefield.airflow-s/airflow-s#233(CVE-2026-49298) — the airflow-core follow-up toCVE-2026-27173(K8s provider, already PUBLIC); without the cross-CVE clause, K8s deployers who applied the provider fix would think they're done.airflow-s/airflow-s#244(CVE-2026-33264) — original summary described theimport_string()bug mechanism but didn't enumerate the attacker role / config that exposes deployments.Changes
1b(pre-push hygiene-gate scan)<project>/<package>/<sibling-package>/<current-package>placeholders per the framework's placeholder-convention rules; thecheck-placeholderspre-commit hook is green.Test plan
skill-and-tool-validatepre-commit hook greencheck-placeholdersgreenWas generative AI tooling used to co-author this PR?