You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@magpie.apache.org by po...@apache.org on 2026/10/17 00:21:46 UTC

(magpie) 14/27: feat(security-issue-sync): propose missing or ambiguous CVE fields with the session's own model

This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch perf/security-1-import
in repository https://gitbox.apache.org/repos/asf/magpie.git

commit ac14f9bd5b8aa76caf50dda96be51538f97027be
Author: Jarek Potiuk <po...@apache.org>
AuthorDate: Sat Oct 17 00:07:43 2026 +0200

    feat(security-issue-sync): propose missing or ambiguous CVE fields with the session's own model
    
    generate-cve-json never calls a model, so a tracker whose summary,
    affected versions, CWE or credit is empty, or whose body repeats a field
    with different values, needed a human to notice the gap. Every skill
    step that regenerates the record (security-issue-sync, security-cve-
    allocate, security-issue-deduplicate) now passes --report-json and,
    when the report lists missing_required or needs_decision, follows one
    shared procedure in issue-sync/outcomes/cve-json-fallback.md.
    
    The model already running the skill reads the tracker body and proposes
    values, labelled AI-proposed and applied only on explicit confirmation
    through the normal field write, with the confirmation recorded in the
    rollup. No new LLM hop is added. Severity is never proposed (the team
    scores it independently; a reporter's CVSS stays informational) and the
    CVE link comes only from allocation. For a field repeated with
    different values the user sees each candidate and picks one; the
    normalised body is then offered as a whole-body rewrite, applied only on
    confirmation.
    
    Part of #1672
    
    Generated-by: Claude Opus 5
---
 .../magpie-security/skills/cve-allocate/SKILL.md   |  6 +--
 .../skills/cve-allocate/tracker-updates.md         |  3 +-
 .../skills/issue-deduplicate/SKILL.md              |  7 +--
 .../skills/issue-sync/apply-and-push.md            |  3 +-
 .../issue-sync/outcomes/cve-json-fallback.md       | 58 ++++++++++++++++++++++
 .../cve-tool-vulnogram/generate-cve-json/SKILL.md  |  2 +-
 6 files changed, 70 insertions(+), 9 deletions(-)

diff --git a/plugins/magpie-security/skills/cve-allocate/SKILL.md b/plugins/magpie-security/skills/cve-allocate/SKILL.md
index 02cc2acf..27e5ee49 100644
--- a/plugins/magpie-security/skills/cve-allocate/SKILL.md
+++ b/plugins/magpie-security/skills/cve-allocate/SKILL.md
@@ -21,7 +21,7 @@ argument-hint: "[issue-number] [CVE-YYYY-NNNNN]"
 capability: capability:resolve
 surface_hash: sha256:25c2286c4e159c66
 license: Apache-2.0
-measured_tokens: 7366
+measured_tokens: 7407
 ---
 
 <!-- Placeholder convention (see AGENTS.md#placeholder-convention-used-in-skill-files):
@@ -411,8 +411,8 @@ partial failures stay legible:
    (`uv run --project ~/.claude/magpie/vetted-ops vetted-op-tracker --caller security-cve-allocate rollup-append <N> "CVE allocated (<CVE-YYYY-NNNNN>)" <scratch>/rollup-entry-<N>.md`)
    — appends the entry to the tracker's rollup, creating the rollup if none exists yet.
    `<scratch>` is the session scratch directory as an absolute path (fall back to `$TMPDIR`); the entry point runs outside the sandbox, where `$TMPDIR` differs, so pass it absolute paths.
-4. `uv run --project <framework>/<cve-tool>/generate-cve-json generate-cve-json <N> --attach`
-   — embeds the CVE JSON in the body.
+4. `uv run --project <framework>/<cve-tool>/generate-cve-json generate-cve-json <N> --attach --report-json <scratch>/cve-report-<N>.json`
+   — embeds the CVE JSON in the body; a report with missing or ambiguous fields goes through the [CVE JSON fallback](../issue-sync/outcomes/cve-json-fallback.md).
 5. Create draft on the original thread (reporter notification, if
    applicable) via the project's configured drafting backend — see
    [`tools/gmail/draft-backends.md`](../../../../tools/gmail/draft-backends.md).
diff --git a/plugins/magpie-security/skills/cve-allocate/tracker-updates.md b/plugins/magpie-security/skills/cve-allocate/tracker-updates.md
index ba539769..44253a11 100644
--- a/plugins/magpie-security/skills/cve-allocate/tracker-updates.md
+++ b/plugins/magpie-security/skills/cve-allocate/tracker-updates.md
@@ -43,9 +43,10 @@ user to confirm. Numbered items:
 4. **Regenerate the CVE JSON attachment** in the tracker body by
    running the adapter's `generate-cve-json` sub-tool:
    ```bash
-   uv run --project <framework>/<cve-tool>/generate-cve-json generate-cve-json <N> --attach
+   uv run --project <framework>/<cve-tool>/generate-cve-json generate-cve-json <N> --attach --report-json <scratch>/cve-report-<N>.json
    ```
    This is how the CVE record first gets seeded with the allocated ID.
+   A non-empty `missing_required` or `needs_decision` in the report goes through [`security-issue-sync`'s CVE JSON fallback](../issue-sync/outcomes/cve-json-fallback.md).
    The remediation-developer credit (if any) comes from the *Remediation developer* body field,
    which `security-issue-sync` fills from the linked PR's author the first time *PR with the fix* is set
    (editable by hand thereafter); no CLI flag needed.
diff --git a/plugins/magpie-security/skills/issue-deduplicate/SKILL.md b/plugins/magpie-security/skills/issue-deduplicate/SKILL.md
index 72308457..5fcb1ee1 100644
--- a/plugins/magpie-security/skills/issue-deduplicate/SKILL.md
+++ b/plugins/magpie-security/skills/issue-deduplicate/SKILL.md
@@ -25,7 +25,7 @@ argument-hint: "[kept-issue] [duplicate-issue]"
 capability: capability:resolve
 surface_hash: sha256:3c9f502b802d2037
 license: Apache-2.0
-measured_tokens: 5989
+measured_tokens: 6040
 ---
 
 <!-- Placeholder convention (see AGENTS.md#placeholder-convention-used-in-skill-files):
@@ -365,8 +365,9 @@ After confirmation, apply **sequentially** (never in parallel):
    (GitHub's `duplicate` close-reason is not exposed by `gh` on
    all versions; `not planned` combined with the `duplicate` label
    carries the same signal)
-6. `uv run --project <framework>/tools/<cve-tool>/generate-cve-json generate-cve-json <keep> --attach`
-   — remediation-developer credits come from the *Remediation developer* body field
+6. `uv run --project <framework>/tools/<cve-tool>/generate-cve-json generate-cve-json <keep> --attach --report-json <scratch>/cve-report-<keep>.json`
+   — a report with missing or ambiguous fields (a merge can leave a field twice) goes through the [CVE JSON fallback](../issue-sync/outcomes/cve-json-fallback.md);
+   remediation-developer credits come from the *Remediation developer* body field
    (populated by `security-issue-sync` from the linked PR's author); no CLI flag needed.
    The regen output is the kept tracker's canonical JSON record.
    When the kept tracker already carries an allocated CVE ID, feed the record into
diff --git a/plugins/magpie-security/skills/issue-sync/apply-and-push.md b/plugins/magpie-security/skills/issue-sync/apply-and-push.md
index a7939c6c..8d80d48b 100644
--- a/plugins/magpie-security/skills/issue-sync/apply-and-push.md
+++ b/plugins/magpie-security/skills/issue-sync/apply-and-push.md
@@ -265,10 +265,11 @@ In every other case — including already-published CVEs — regenerate.
 The minimum command, from the `<tracker>` clone root:
 
 ```bash
-uv run --project <framework>/tools/cve-tool-vulnogram/generate-cve-json generate-cve-json <N> --attach
+uv run --project <framework>/tools/cve-tool-vulnogram/generate-cve-json generate-cve-json <N> --attach --report-json <scratch>/cve-report-<N>.json
 ```
 
 That alone is enough: the script reads every template field from the issue body, emits the full CVE 5.x record, and patches (or appends to) the tracking issue body in place.
+When the report lists `missing_required` or `needs_decision` (exit 2), follow [`outcomes/cve-json-fallback.md`](outcomes/cve-json-fallback.md).
 
 ### Remediation-developer credit comes from the body field
 
diff --git a/plugins/magpie-security/skills/issue-sync/outcomes/cve-json-fallback.md b/plugins/magpie-security/skills/issue-sync/outcomes/cve-json-fallback.md
new file mode 100644
index 00000000..18dbd2b4
--- /dev/null
+++ b/plugins/magpie-security/skills/issue-sync/outcomes/cve-json-fallback.md
@@ -0,0 +1,58 @@
+<!-- SPDX-License-Identifier: Apache-2.0
+     https://www.apache.org/licenses/LICENSE-2.0 -->
+
+# CVE JSON fallback: missing or ambiguous fields
+
+Every skill step that runs `generate-cve-json` passes `--report-json <scratch>/cve-report-<N>.json`
+(`<scratch>` is the session scratch directory as an absolute path).
+The tool is deterministic and never calls a model:
+it normalises the body in memory, reads the fields, and writes a report of field names, line numbers and kinds — never body text.
+Read the report after every run.
+When `missing_required` and `needs_decision` are both empty, there is nothing to do here.
+Otherwise this procedure runs, and it is the **only** place a value may come from anything other than the team or the existing body.
+
+**Which model.** The proposals come from the model already running this skill, in this session.
+No new LLM hop is added: the session's own harness model is default-approved in [`tools/privacy-llm/models.md`](../../../../../tools/privacy-llm/models.md), and the tracker body it reads is the same content the skill already handles.
+If the active stack is not approved for `<security-list>` content, skip the proposals and list the fields for the user to fill by hand.
+
+## Missing required fields
+
+For each `missing_required` entry:
+
+- **`agent_may_propose: false` — never propose.**
+  *Severity* stays empty (or `Unknown`) until the team scores it independently:
+  a reporter's CVSS, vector or label is informational only and is never copied, paraphrased or "confirmed" ([`AGENTS.md`](../../../../../AGENTS.md#reporter-supplied-cvss-scores-are-informational-only--never-propagate-them)).
+  *CVE tool link* comes only from [`security-cve-allocate`](../../cve-allocate/SKILL.md).
+  Name the field to the user as waiting on the team, and move on.
+- **`agent_may_propose: true` — propose, never apply.**
+  Read the tracker body (`vetted-op-read --caller <skill> body-field-get <N> "<field>"` for the description, plus what the skill already gathered from the thread and comments) and draft one value per field:
+  - *Short public summary for publish* — public-safe wording for the CVE description; no CVE ID, no tracker content beyond the summary itself, and no other project's vulnerability.
+  - *Affected versions* — only a range the team or the thread states, in the generator's syntax (`>= X, < Y`, `< NEXT VERSION`); never a guess.
+  - *CWE* — from the team's own assessment or your reading of the described weakness; never a CWE the reporter assigned to their own report.
+  - *Reporter credited as* — the reporter's chosen credit, per [`credit-field.md`](../../issue-import/outcomes/credit-field.md).
+
+Show every proposal to the user, labelled **AI-proposed**, with where it came from.
+Write a field only after the user explicitly confirms that field, through the normal field write
+(`vetted-op-tracker --caller <skill> body-field-set <N> "<field>" <scratch>/<file>.md`),
+and record it in the run's rollup entry as *"`<field>`: AI-proposed, confirmed by @<user>"* — the field name, never the value's source text.
+A declined proposal leaves the field empty; regenerate and carry on with the record as `DRAFT`.
+
+## Fields that need a decision
+
+`needs_decision` makes the tool exit 2 with no record. Never work around it with a CLI override or a guessed value.
+
+- **`duplicate`** — the field appears at the listed heading lines with different values.
+  Produce the normalised body without pushing it:
+  `uv run --directory <framework>/tools/github-body-field body-field --repo <tracker> normalise <N> --fields-json <scratch>/cve-fields.json --output <scratch>/body-<N>.md`,
+  where `cve-fields.json` holds the template field names generate-cve-json reads
+  (`Short public summary for publish`, `Affected versions`, `Security mailing list thread`, `Public advisory URL`, `Reporter credited as`, `PR with the fix`, `Remediation developer`, `CWE`, `Severity`, `CVE tool link`).
+  Show the user each candidate (the value under each listed line) and ask which one to keep; do not recommend one by recency or length.
+  Re-run the same command with `--keep "<field>=<line>"` for the chosen copy, confirm its `needs_decision` is empty, and show the user its `changes`.
+- **`container-heading`** — markdown readers disagree on whether the listed heading is a field (it sits in or next to a list, quote or fence).
+  Show the user the lines and ask whether it is the field; the fix is a hand edit that moves the heading out of the container, or escapes it, before regenerating.
+
+The normalised body is a **proposed whole-body rewrite**:
+apply it only on explicit confirmation, with `gh issue edit <N> --repo <tracker> --body-file <scratch>/body-<N>.md`,
+record *"body normalised (`<kinds>`), confirmed by @<user>"* in the rollup, and re-run `generate-cve-json`.
+When the report shows only `normalisation_changes` (no decision, nothing missing), the record is already right;
+offer the same rewrite once, as a tidy-up, and skip it unless the user says yes.
diff --git a/tools/cve-tool-vulnogram/generate-cve-json/SKILL.md b/tools/cve-tool-vulnogram/generate-cve-json/SKILL.md
index 3a117387..b63696c4 100644
--- a/tools/cve-tool-vulnogram/generate-cve-json/SKILL.md
+++ b/tools/cve-tool-vulnogram/generate-cve-json/SKILL.md
@@ -461,7 +461,7 @@ Additional flags, all optional:
   `needs_decision` (fields repeated with different values),
   and `missing_required` (empty required fields, each with `agent_may_propose`; `false` for *Severity* and *CVE tool link*).
   It is written before the record is built, so it exists on exit 2 too.
-  The calling skill acts on it; the tool itself never calls a model.
+  The calling skill acts on it per [the CVE JSON fallback](../../../plugins/magpie-security/skills/issue-sync/outcomes/cve-json-fallback.md); the tool itself never calls a model.
 
 Before reading any field the script normalises the body **in memory** with
 [`github-body-field`](../../github-body-field/README.md)'s `normalise`: