This is an automated email from the ASF dual-hosted git repository. lukaszlenart pushed a commit to branch docs/triage-notice-shape in repository https://gitbox.apache.org/repos/asf/struts.git
commit d2f6c3741a4f316cda518e9ff3ef66eeccc57f9d Author: Lukasz Lenart <[email protected]> AuthorDate: Mon Nov 9 12:39:36 2026 +0100 docs(skills): name list posts by ISO date and file the ticket before the notice Three gaps in the triaging-security-reports skill showed up while handling a batch of public Full Disclosure posts: - The notice subject recipe said "<public id, where one exists>"; for a mailing-list post agents either dropped the slot or used the seclists archive path (2026/Nov/2) as if it were a date. The subject now takes the venue plus the post's ISO publication date, or the advisory id for a scanner/CVE record; the header block gives the date and URL. - "What the project is doing" allowed a notice with no ticket, which came out as "tracked in the public issue tracker" and had to be rewritten once the ticket existed. A ticket that is owed now exists before the notice is drafted; without it the deliverable is one line naming the ticket needed. - A reply to the author of public posts who asked about CVEs and credit explained the project's policy, which decides the reserved question for the PMC. The reply section now says explaining the policy is answering, and gives the outcomes-only shape for a reporter whose claims are public. Baseline runs without the change failed all three; the same three scenarios pass with it. Co-Authored-By: Claude Mythos 5.1 <[email protected]> --- .claude/skills/triaging-security-reports/SKILL.md | 43 ++++++++++++++++++----- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/.claude/skills/triaging-security-reports/SKILL.md b/.claude/skills/triaging-security-reports/SKILL.md index 6ce2be63d..cc4688a37 100644 --- a/.claude/skills/triaging-security-reports/SKILL.md +++ b/.claude/skills/triaging-security-reports/SKILL.md @@ -128,6 +128,12 @@ The verdict does not change with the channel. The follow-up does: Until that notice exists, users and scanner vendors have only the reporter's version of the claim. The notice is the one statement they can point to, and it is written once. + When the disposition owes a ticket — a functional bug, or `VALID-HARDENING` with no fix merged — + the ticket exists before the notice is drafted, so the notice can name it. If the notice is + requested and no ticket exists, the deliverable is one line naming the ticket that is needed + (`WW ticket needed first: REST plugin reads chunked bodies under the bounded reader`), not a + notice that says "tracked in the issue tracker". + Posting the notice is the PMC's decision and drafting it is a task the user starts — the gate below applies to it exactly as to a reporter reply. The assessment's part is the two lines above. @@ -161,7 +167,15 @@ Once you *have* been asked: the bulletin, but that timing is the PMC's call (ASF actually permits allocating earlier and sharing the id with the reporter — see [`creating-security-bulletins`](../creating-security-bulletins/SKILL.md)), so a reply must not promise a CVE or its timing. When the reporter asks for one of these, say the - decision comes later and report the question to the user; do not answer it. + decision comes later and report the question to the user; do not answer it. Explaining the + project's CVE or credit policy to the reporter *is* answering it: "a hardening ticket gets no + CVE" or "a confirmed vulnerability gets a CVE with your name on it" decides the question for + the PMC. The reply carries the outcome; the policy question goes to the user. +- **A reporter whose claims are already public gets the outcomes, not a debate.** When the author + of public posts writes in, the reply states the verdict per post, names the tickets that exist, + says a `[NOT-VULNERABILITY]` notice follows where one is owed, and asks for further material + through `security@`, one report per mail. It goes in a thread about those outcomes, not in a + thread about a dispute. - Acknowledge anything the reporter got right (e.g. correct CVE-fix verification) — it builds the relationship and signals you actually read it. - Keep it private: no public issue, PR, Jira, or list thread before triage. Never open a PR that is itself the security fix (see [`CLAUDE.md`](../../../CLAUDE.md)). After triage, a public report closed as a non-vulnerability gets its `[NOT-VULNERABILITY]` notice @@ -173,11 +187,15 @@ Asked for the user-list notice on a public report, write it as these parts, in t the shape the Logging PMC posts on `log4j-user@` (search that list for `[NOT-VULNERABILITY]` for live examples): -1. **Subject** — `[NOT-VULNERABILITY] <public id, where one exists>: <what the report claims, as one - functional clause>`. Example: `[NOT-VULNERABILITY] sonatype-2026-006746: FilteredObjectInputStream - is no safer than ObjectInputStream on untrusted data`. -2. **Header block** — the report (link, publication date, publisher), the disposition from - [`THREAT_MODEL.md`](../../../THREAT_MODEL.md) §13, and `TLP:CLEAR`. +1. **Subject** — `[NOT-VULNERABILITY] <source>: <what the report claims, as one functional clause>`. + `<source>` is the advisory id when the report is a scanner or CVE record, and the venue plus the + post's own publication date in ISO form when it is a list post, blog or issue. An archive path + such as `2026/Nov/2` is an address, not a date, and never stands in for one. Examples: + `[NOT-VULNERABILITY] sonatype-2026-006746: FilteredObjectInputStream is no safer than + ObjectInputStream on untrusted data`; `[NOT-VULNERABILITY] Full Disclosure, 2026-10-29: the REST + plugin ignores a chunked request body instead of binding it`. +2. **Header block** — `Report: <venue>, <YYYY-MM-DD>, <URL>` with the post's own date, the + disposition from [`THREAT_MODEL.md`](../../../THREAT_MODEL.md) §13, and `TLP:CLEAR`. 3. **In short** — the verdict in two sentences: not a vulnerability in Apache Struts, and the one reason. 4. **What the report describes** — the claim and the mechanism, in words. No payload, no PoC string. 5. **What the framework does with that input** — the request-to-sink path from your table, hop by @@ -185,9 +203,10 @@ live examples): a scanner vendor will quote. 6. **Why that is not a vulnerability** — the ownership boundary, which hop the application or the operator owns, with the `THREAT_MODEL.md` / [`SECURITY.md`](../../../SECURITY.md) section that says so. -7. **What the project is doing** — the hardening ticket, where one exists, in functional terms - (`WW-XXXX adds java.beans to the default excluded packages`). Nothing more: no fix version, no - date, no statement about bulletins or CVEs. +7. **What the project is doing** — the ticket, in functional terms (`WW-XXXX adds java.beans to the + default excluded packages`); it exists before this part is written (see "A public report closes + in public"). When the verdict owes no change, say so in one sentence. Nothing more: no fix + version, no date, no statement about bulletins or CVEs. 8. **What users should do** — usually nothing. The setting to keep on; what to check in their own code. 9. **References** — the report, the ticket, `THREAT_MODEL.md`, `SECURITY.md`, the security guide page. 10. **Closing** — new evidence goes to `[email protected]`; questions go to the list. Signed @@ -216,6 +235,9 @@ exchange with them. - The report is public and your assessment ends with "a public statement is the PMC's call" — the `Disclosure:` and `Outcome notice:` lines are missing. - Writing "several users have asked" into a notice with no list thread to link. - A notice that names a release, a date, a bulletin decision, or the reporter. +- A notice subject with no venue and ISO date, or an archive path where the date belongs. +- About to write "tracked in the public issue tracker" with no `WW-` id — the ticket comes first. +- About to explain the CVE or credit policy in a reply — that is the reserved question, answered as background. ## Common Mistakes @@ -235,3 +257,6 @@ exchange with them. | "The reporter asked about a CVE, so I should answer it" | Report the question to the user. CVE timing is a PMC choice (not a fixed rule), so answering it commits the project to a process decision that isn't yours. | | "The verdict is the same whoever posted it, so where it was posted doesn't matter" | The verdict is the same. What the project owes afterwards is not: a public claim closed as a non-vulnerability stays "confirmed" in every scanner feed until the project's own notice exists. | | "Posting publicly is a communications call, not triage" | Posting is the PMC's. Saying that a notice is owed, and what its subject is, is the assessment's. | +| "No ticket exists yet, so the notice says 'tracked publicly'" | A notice without the id is rewritten the day the ticket appears. The ticket comes first; until then the deliverable is the one line naming it. | +| "The post has no id, so the subject carries only the claim" | A list post's source is its venue and ISO date. The archive path is an address and goes in the header as the URL. | +| "I'm not promising a CVE, I'm explaining our policy" | Explaining the policy to the reporter decides their question. State the outcome; report the question to the user. |
