* fix(2119): single SECURITY.md writer — auditor is return-only The gsd-security-auditor held Write/Edit and was instructed to write SECURITY.md (no <N>- prefix, no template frontmatter), while the orchestrator's Step 6 also wrote the correct padded <N>-SECURITY.md from templates/SECURITY.md. Two writers, two naming conventions, two shapes — the auditor's unprefixed file was invisible to the workflow's *-SECURITY.md glob detector and unparseable for the threats_open gate. Fix (option 1 from the issue): make the auditor return-only. - Remove Write/Edit from auditor's tools - Rewrite all 'Write SECURITY.md' instructions to 'Return structured verdict' with threats_open count - Add explicit constraint in workflow Step 5 spawn prompt - Update existing test (was asserting Write in tools — now asserts absence) - Add new regression test for single-writer contract - Update docs/AGENTS.md stale Tools/Produces rows - Regenerate golden fixtures + agent size baseline * docs(changeset): backfill PR number (#2154) * chore(#2119): regenerate pi/qwen golden fixtures after next merge The single-writer change edits gsd-core/workflows/secure-phase.md and agents/gsd-security-auditor.md; pi.json (added on next) and qwen.json (merge straggler) were the only runtime fixtures still holding pre-change hashes for those files. All other runtimes already reflect the change. Regenerated via the sanctioned gen-golden-install-parity script. * merge origin/next — regenerate goldens + baseline for merged state * fix slash-command syntax: /gsd-secure-phase → /gsd:secure-phase (#2154 CI fix)
This commit is contained in:
5
.changeset/2119-secure-phase-single-writer.md
Normal file
5
.changeset/2119-secure-phase-single-writer.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
type: Fixed
|
||||
pr: 2154
|
||||
---
|
||||
**`/gsd-secure-phase` now has a single SECURITY.md writer** — the `gsd-security-auditor` subagent previously held `Write`/`Edit` tools and was instructed to "write SECURITY.md" with no padded `<N>-` prefix and no template frontmatter, while the orchestrator's Step 6 also wrote the phase-scoped `<N>-SECURITY.md` from `templates/SECURITY.md`. The auditor is now return-only (drops `Write`/`Edit`, returns a structured verdict with `threats_open`); the orchestrator is the sole file writer. The workflow's Step 5 spawn constraints explicitly forbid the auditor from writing SECURITY.md. (#2119)
|
||||
@@ -1,10 +1,8 @@
|
||||
---
|
||||
name: gsd-security-auditor
|
||||
description: Verifies threat mitigations from PLAN.md threat model exist in implemented code. Produces SECURITY.md. Spawned by /gsd:secure-phase.
|
||||
description: Verifies threat mitigations from PLAN.md threat model exist in implemented code. Returns structured security verdict (SECURED / OPEN_THREATS / ESCALATE). Spawned by /gsd:secure-phase.
|
||||
tools:
|
||||
- Read
|
||||
- Write
|
||||
- Edit
|
||||
- Bash
|
||||
- Glob
|
||||
- Grep
|
||||
@@ -15,11 +13,11 @@ color: red
|
||||
<role>
|
||||
An implemented phase has been submitted for security audit. Verify that every declared threat mitigation is present in the code — do not accept documentation or intent as evidence.
|
||||
|
||||
Does NOT scan blindly for new vulnerabilities. Verifies each threat in `<threat_model>` by its declared disposition (mitigate / accept / transfer). Reports gaps. Writes SECURITY.md.
|
||||
Does NOT scan blindly for new vulnerabilities. Verifies each threat in `<threat_model>` by its declared disposition (mitigate / accept / transfer). Reports gaps. Returns a structured verdict — the orchestrator owns the SECURITY.md file write (#2119: single-writer contract).
|
||||
|
||||
**Mandatory Initial Read:** If prompt contains `<required_reading>`, load ALL listed files before any action.
|
||||
|
||||
**Implementation files are READ-ONLY.** Only create/modify: SECURITY.md. Implementation security gaps → OPEN_THREATS or ESCALATE. Never patch implementation.
|
||||
**Implementation files are READ-ONLY.** The auditor does NOT write any files — it returns a structured verdict (SECURED / OPEN_THREATS / ESCALATE). The orchestrator persists SECURITY.md. Implementation security gaps → OPEN_THREATS or ESCALATE. Never patch implementation.
|
||||
</role>
|
||||
|
||||
<adversarial_stance>
|
||||
@@ -79,20 +77,20 @@ Classify each threat before verification. Record classification for every threat
|
||||
- L3: deep trace — follow the data flow end-to-end, check edge cases and ordering, confirm no bypass path exists.
|
||||
</step>
|
||||
|
||||
<step name="verify_and_write">
|
||||
<step name="verify_and_return">
|
||||
For each `mitigate` threat: grep for declared mitigation pattern in cited files → found = `CLOSED`, not found = `OPEN`. Apply depth per `asvs_level` (see analyze_threats step).
|
||||
For `accept` threats: check SECURITY.md accepted risks log → entry present = `CLOSED`, absent = `OPEN`.
|
||||
For `accept` threats: check existing SECURITY.md accepted risks log → entry present = `CLOSED`, absent = `OPEN`.
|
||||
For `transfer` threats: check for transfer documentation → present = `CLOSED`, absent = `OPEN`.
|
||||
|
||||
For each `threat_flag` in SUMMARY.md `## Threat Flags`: if maps to existing threat ID → informational. If no mapping → log as `unregistered_flag` in SECURITY.md (not a blocker).
|
||||
For each `threat_flag` in SUMMARY.md `## Threat Flags`: if maps to existing threat ID → informational. If no mapping → log as `unregistered_flag` in the structured return (not a blocker).
|
||||
|
||||
**Severity-aware `threats_open` computation (severity order: critical > high > medium > low):**
|
||||
`threats_open` (the SECURITY.md frontmatter gate field) = the count of threats whose status is OPEN AND whose severity rank ≥ the `block_on` rank. `block_on: none` ⇒ 0 (nothing ever blocks). `block_on: low` ⇒ all open threats block. `block_on: high` (default) ⇒ only high and critical open threats block.
|
||||
Open threats BELOW the block threshold are recorded in SECURITY.md as **open — below {block_on} threshold (non-blocking)** and MUST NOT be counted in `threats_open`.
|
||||
Open threats BELOW the block threshold are recorded in the return as **open — below {block_on} threshold (non-blocking)** and MUST NOT be counted in `threats_open`.
|
||||
|
||||
**Fail-closed for missing severity:** if an OPEN threat has no severity or an unparseable severity (e.g. a legacy register predating the Severity column), treat it as `critical` for this computation — it COUNTS toward `threats_open` (blocking). Never silently drop an unranked open threat.
|
||||
|
||||
Write SECURITY.md. Set `threats_open` to the severity-filtered count. Return structured result.
|
||||
Return the structured result (SECURED / OPEN_THREATS / ESCALATE) with `threats_open` set to the severity-filtered count. The orchestrator writes SECURITY.md from this data — the auditor does NOT write any files (#2119).
|
||||
</step>
|
||||
|
||||
</execution_flow>
|
||||
@@ -116,7 +114,7 @@ Write SECURITY.md. Set `threats_open` to the severity-filtered count. Return str
|
||||
### Unregistered Flags
|
||||
{none / list from SUMMARY.md ## Threat Flags with no threat mapping}
|
||||
|
||||
SECURITY.md: {path}
|
||||
**threats_open:** {count}
|
||||
```
|
||||
|
||||
## OPEN_THREATS
|
||||
@@ -145,9 +143,9 @@ SECURITY.md: {path}
|
||||
|
||||
*Only blocking-open threats count toward `threats_open` in SECURITY.md frontmatter.*
|
||||
|
||||
Next: Implement mitigations or document as accepted in SECURITY.md accepted risks log, then re-run /gsd:secure-phase.
|
||||
Next: Implement mitigations or document as accepted risks, then re-run /gsd:secure-phase.
|
||||
|
||||
SECURITY.md: {path}
|
||||
**threats_open:** {count}
|
||||
```
|
||||
|
||||
## ESCALATE
|
||||
@@ -172,6 +170,6 @@ SECURITY.md: {path}
|
||||
- [ ] Each threat verified by disposition type (mitigate / accept / transfer)
|
||||
- [ ] Threat flags from SUMMARY.md `## Threat Flags` incorporated
|
||||
- [ ] Implementation files never modified
|
||||
- [ ] SECURITY.md written to correct path
|
||||
- [ ] Structured return: SECURED / OPEN_THREATS / ESCALATE
|
||||
- [ ] No files written — structured verdict returned only (orchestrator writes SECURITY.md)
|
||||
- [ ] Structured return: SECURED / OPEN_THREATS / ESCALATE with `threats_open` count
|
||||
</success_criteria>
|
||||
|
||||
@@ -479,10 +479,10 @@ Communication style, decision patterns, debugging approach, UX preferences, vend
|
||||
|----------|-------|
|
||||
| **Spawned by** | `/gsd-secure-phase` |
|
||||
| **Parallelism** | Single instance |
|
||||
| **Tools** | Read, Write, Edit, Bash, Glob, Grep |
|
||||
| **Tools** | Read, Bash, Glob, Grep |
|
||||
| **Model (balanced)** | Sonnet |
|
||||
| **Color** | Red |
|
||||
| **Produces** | `{phase}-SECURITY.md` |
|
||||
| **Produces** | Structured verdict (SECURED / OPEN_THREATS / ESCALATE) — orchestrator writes `{phase}-SECURITY.md` (#2119) |
|
||||
|
||||
**Key behaviors:**
|
||||
- Verifies each threat by its declared disposition (mitigate / accept / transfer)
|
||||
|
||||
@@ -108,7 +108,7 @@ Agent(
|
||||
"<files_to_read>{PLAN, SUMMARY, impl files, SECURITY.md}</files_to_read>" +
|
||||
"<threat_register>{threat register}</threat_register>" +
|
||||
"<config>asvs_level: {SECURITY_ASVS}, block_on: {SECURITY_BLOCK_ON}</config>" +
|
||||
"<constraints>Never modify implementation files. Verify mitigations exist — do not scan for new threats. Escalate implementation gaps.</constraints>" +
|
||||
"<constraints>Never modify implementation files. Verify mitigations exist — do not scan for new threats. Escalate implementation gaps. Return a structured verdict only — do NOT write SECURITY.md (the orchestrator owns the file write).</constraints>" +
|
||||
"${AGENT_SKILLS_AUDITOR}",
|
||||
subagent_type="gsd-security-auditor",
|
||||
model="{AUDITOR_MODEL}",
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
"gsd-project-researcher.md": 22242,
|
||||
"gsd-research-synthesizer.md": 13847,
|
||||
"gsd-roadmapper.md": 22273,
|
||||
"gsd-security-auditor.md": 8981,
|
||||
"gsd-security-auditor.md": 9431,
|
||||
"gsd-ui-auditor.md": 17249,
|
||||
"gsd-ui-checker.md": 14118,
|
||||
"gsd-ui-researcher.md": 19557,
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"agents/gsd-project-researcher.md": "85de7f562872ee9b",
|
||||
"agents/gsd-research-synthesizer.md": "18a2e1b30ff7ae3a",
|
||||
"agents/gsd-roadmapper.md": "7a8465ac6d4dd29e",
|
||||
"agents/gsd-security-auditor.md": "2ce13af25179dd10",
|
||||
"agents/gsd-security-auditor.md": "7730a026680cb821",
|
||||
"agents/gsd-ui-auditor.md": "f777f9c7bf62788c",
|
||||
"agents/gsd-ui-checker.md": "216af34b01e277aa",
|
||||
"agents/gsd-ui-researcher.md": "5f86de1decbd16d2",
|
||||
@@ -287,7 +287,7 @@
|
||||
"gsd-core/workflows/resume-project.md": "98e2cf8908e73a52",
|
||||
"gsd-core/workflows/review.md": "43c052bba1cbd4ac",
|
||||
"gsd-core/workflows/scan.md": "a7fecd67e5cd655f",
|
||||
"gsd-core/workflows/secure-phase.md": "96b199dfac00e60f",
|
||||
"gsd-core/workflows/secure-phase.md": "52ddc46233e8fa66",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
"gsd-core/workflows/settings-advanced.md": "6d25100a9de15b31",
|
||||
"gsd-core/workflows/settings-integrations.md": "4d3001ad2b5dad8f",
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"agents/gsd-project-researcher.md": "4531b7cc8f5e5f7d",
|
||||
"agents/gsd-research-synthesizer.md": "4a4f68e6c75b133a",
|
||||
"agents/gsd-roadmapper.md": "bb2f57695dbab32c",
|
||||
"agents/gsd-security-auditor.md": "f22374cc1db28dca",
|
||||
"agents/gsd-security-auditor.md": "4db2c41181ad1f97",
|
||||
"agents/gsd-ui-auditor.md": "dcd5712e6b160a53",
|
||||
"agents/gsd-ui-checker.md": "5c27ec0d88ef87c2",
|
||||
"agents/gsd-ui-researcher.md": "bcc591f2dfebdb60",
|
||||
@@ -358,7 +358,7 @@
|
||||
"gsd-core/workflows/resume-project.md": "f28da1200e4545f4",
|
||||
"gsd-core/workflows/review.md": "ad7c0f372ed986ae",
|
||||
"gsd-core/workflows/scan.md": "003883d71c37da7d",
|
||||
"gsd-core/workflows/secure-phase.md": "29fc6b62c5c5dc62",
|
||||
"gsd-core/workflows/secure-phase.md": "22ab3bb5da494ea7",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
"gsd-core/workflows/settings-advanced.md": "414db4dbea97ba44",
|
||||
"gsd-core/workflows/settings-integrations.md": "70515c5838fb9826",
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
"agents/gsd-project-researcher.md": "d7f355894519f9fe",
|
||||
"agents/gsd-research-synthesizer.md": "1c738df9932d325a",
|
||||
"agents/gsd-roadmapper.md": "453e9471ad27c7ea",
|
||||
"agents/gsd-security-auditor.md": "45bd98918cd3a004",
|
||||
"agents/gsd-security-auditor.md": "4551e5f621cf13ff",
|
||||
"agents/gsd-ui-auditor.md": "a0b09cc8e4645956",
|
||||
"agents/gsd-ui-checker.md": "33ffdc73d2105a24",
|
||||
"agents/gsd-ui-researcher.md": "4b36852c839f1134",
|
||||
@@ -357,7 +357,7 @@
|
||||
"gsd-core/workflows/resume-project.md": "af9761bcec0f6fe9",
|
||||
"gsd-core/workflows/review.md": "eec3a15bebb7fcf0",
|
||||
"gsd-core/workflows/scan.md": "75c670d08cee8680",
|
||||
"gsd-core/workflows/secure-phase.md": "64ec4d06ca85720a",
|
||||
"gsd-core/workflows/secure-phase.md": "8030d2b2a5bfdf07",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
"gsd-core/workflows/settings-advanced.md": "94d61da368e9f85b",
|
||||
"gsd-core/workflows/settings-integrations.md": "dfe3672c4fabf139",
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
"agents/gsd-project-researcher.md": "f468e96f8339d1e0",
|
||||
"agents/gsd-research-synthesizer.md": "7be02e47f4fd901b",
|
||||
"agents/gsd-roadmapper.md": "8a7f1f1256a6aed5",
|
||||
"agents/gsd-security-auditor.md": "4f9fc3f654af4944",
|
||||
"agents/gsd-security-auditor.md": "757f72894f79f11b",
|
||||
"agents/gsd-ui-auditor.md": "40c0dcc15bcfb9fb",
|
||||
"agents/gsd-ui-checker.md": "8126405043f99cb6",
|
||||
"agents/gsd-ui-researcher.md": "9e3ac030767167e0",
|
||||
@@ -286,7 +286,7 @@
|
||||
"gsd-core/workflows/resume-project.md": "7f8dc986f0f35d96",
|
||||
"gsd-core/workflows/review.md": "b0baf1dafebe3821",
|
||||
"gsd-core/workflows/scan.md": "47371c2073d6c0be",
|
||||
"gsd-core/workflows/secure-phase.md": "59d3c50aba8c9a6c",
|
||||
"gsd-core/workflows/secure-phase.md": "d6ac1f4db6a5da75",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
"gsd-core/workflows/settings-advanced.md": "339def28c34b0797",
|
||||
"gsd-core/workflows/settings-integrations.md": "53649313d20694ae",
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
"agents/gsd-project-researcher.md": "049f816c6caa4316",
|
||||
"agents/gsd-research-synthesizer.md": "2f7dcbff50371d4c",
|
||||
"agents/gsd-roadmapper.md": "bbb23d3097911516",
|
||||
"agents/gsd-security-auditor.md": "c35c8ec2f85b331f",
|
||||
"agents/gsd-security-auditor.md": "38935b2d0c532c29",
|
||||
"agents/gsd-ui-auditor.md": "9338efa31b9b7b99",
|
||||
"agents/gsd-ui-checker.md": "151d12b4e007cbbf",
|
||||
"agents/gsd-ui-researcher.md": "1bb303f3a3c4dfc9",
|
||||
@@ -290,7 +290,7 @@
|
||||
"gsd-core/workflows/resume-project.md": "e23981178fa37b3d",
|
||||
"gsd-core/workflows/review.md": "6c689f4ff8146d28",
|
||||
"gsd-core/workflows/scan.md": "dfd92717caea0ce7",
|
||||
"gsd-core/workflows/secure-phase.md": "cf78183f06a02582",
|
||||
"gsd-core/workflows/secure-phase.md": "00de56d6d993bb2c",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
"gsd-core/workflows/settings-advanced.md": "69f3a19bf2c61160",
|
||||
"gsd-core/workflows/settings-integrations.md": "76eee76d6eb57657",
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"agents/gsd-project-researcher.md": "e43c59f7f1f2f37a",
|
||||
"agents/gsd-research-synthesizer.md": "87955470c3c129b2",
|
||||
"agents/gsd-roadmapper.md": "20b69eff61a7a9fa",
|
||||
"agents/gsd-security-auditor.md": "d3b8f44034a76c9d",
|
||||
"agents/gsd-security-auditor.md": "bcd9c4859eb5b448",
|
||||
"agents/gsd-ui-auditor.md": "a26bbc733817959b",
|
||||
"agents/gsd-ui-checker.md": "25822359044cd708",
|
||||
"agents/gsd-ui-researcher.md": "e37d9f53ade25d1f",
|
||||
@@ -358,7 +358,7 @@
|
||||
"gsd-core/workflows/resume-project.md": "f28da1200e4545f4",
|
||||
"gsd-core/workflows/review.md": "ad7c0f372ed986ae",
|
||||
"gsd-core/workflows/scan.md": "003883d71c37da7d",
|
||||
"gsd-core/workflows/secure-phase.md": "29fc6b62c5c5dc62",
|
||||
"gsd-core/workflows/secure-phase.md": "22ab3bb5da494ea7",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
"gsd-core/workflows/settings-advanced.md": "414db4dbea97ba44",
|
||||
"gsd-core/workflows/settings-integrations.md": "70515c5838fb9826",
|
||||
|
||||
@@ -128,8 +128,8 @@
|
||||
"agents/gsd-research-synthesizer.toml": "053616d4941a1458",
|
||||
"agents/gsd-roadmapper.md": "e15d82d7781dbff3",
|
||||
"agents/gsd-roadmapper.toml": "0b17f618eafe9be8",
|
||||
"agents/gsd-security-auditor.md": "a4008a6b1d01833b",
|
||||
"agents/gsd-security-auditor.toml": "42f529475bd28c22",
|
||||
"agents/gsd-security-auditor.md": "d7b62f9a93cfbf78",
|
||||
"agents/gsd-security-auditor.toml": "e991aa6e7c70f813",
|
||||
"agents/gsd-ui-auditor.md": "abbf560bc8d5069c",
|
||||
"agents/gsd-ui-auditor.toml": "5e9dd62a12a16a1e",
|
||||
"agents/gsd-ui-checker.md": "b65d350a4e0564e9",
|
||||
@@ -140,7 +140,7 @@
|
||||
"agents/gsd-user-profiler.toml": "b9c244bb8fbf8140",
|
||||
"agents/gsd-verifier.md": "4ac4b860e2504374",
|
||||
"agents/gsd-verifier.toml": "8ed9fb961409e894",
|
||||
"config.toml": "fa48d84b92174890",
|
||||
"config.toml": "b5f627b42f060910",
|
||||
"gsd-core/VERSION": "ef0deccd81a6723c",
|
||||
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
|
||||
"gsd-core/bin/ensure-runtime-build.cjs": "51bc64467ab30f62",
|
||||
@@ -393,7 +393,7 @@
|
||||
"gsd-core/workflows/resume-project.md": "9965f87eb278f7f8",
|
||||
"gsd-core/workflows/review.md": "5faef3f4feb45c99",
|
||||
"gsd-core/workflows/scan.md": "1a3caa5d724d39e9",
|
||||
"gsd-core/workflows/secure-phase.md": "db91810d16964b1e",
|
||||
"gsd-core/workflows/secure-phase.md": "ab387a4bca381c18",
|
||||
"gsd-core/workflows/session-report.md": "dd8fa011c9394075",
|
||||
"gsd-core/workflows/settings-advanced.md": "2431433811616f76",
|
||||
"gsd-core/workflows/settings-integrations.md": "77730321d3d6d317",
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"agents/gsd-project-researcher.agent.md": "d73bdbe986ffa8a6",
|
||||
"agents/gsd-research-synthesizer.agent.md": "f03eed4aa89e47c5",
|
||||
"agents/gsd-roadmapper.agent.md": "322048cf8ddcb4e5",
|
||||
"agents/gsd-security-auditor.agent.md": "6f6a88b35dc2a24b",
|
||||
"agents/gsd-security-auditor.agent.md": "464cfbe9aafcd5af",
|
||||
"agents/gsd-ui-auditor.agent.md": "92f50549e84ef482",
|
||||
"agents/gsd-ui-checker.agent.md": "47d8cf3486009e11",
|
||||
"agents/gsd-ui-researcher.agent.md": "0746daeb54f83008",
|
||||
@@ -288,7 +288,7 @@
|
||||
"gsd-core/workflows/resume-project.md": "40db7f350f5866d8",
|
||||
"gsd-core/workflows/review.md": "4b649f31865a1785",
|
||||
"gsd-core/workflows/scan.md": "dcc2f76d0850e2fb",
|
||||
"gsd-core/workflows/secure-phase.md": "d87bd706f85bcad6",
|
||||
"gsd-core/workflows/secure-phase.md": "9bec6635ee1cbaed",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
"gsd-core/workflows/settings-advanced.md": "230a658de9c017a6",
|
||||
"gsd-core/workflows/settings-integrations.md": "a1d146d6bfd14db5",
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"agents/gsd-project-researcher.md": "beeac940d3a10e76",
|
||||
"agents/gsd-research-synthesizer.md": "6315f016d55176f4",
|
||||
"agents/gsd-roadmapper.md": "d28e7d4bac46dde2",
|
||||
"agents/gsd-security-auditor.md": "5ff44ee432387d93",
|
||||
"agents/gsd-security-auditor.md": "1e6a10833f556e4c",
|
||||
"agents/gsd-ui-auditor.md": "d824acc3b2a18c53",
|
||||
"agents/gsd-ui-checker.md": "c6c24e8066470830",
|
||||
"agents/gsd-ui-researcher.md": "0f5be5e55501f7e8",
|
||||
@@ -358,7 +358,7 @@
|
||||
"gsd-core/workflows/resume-project.md": "7f8dc986f0f35d96",
|
||||
"gsd-core/workflows/review.md": "3ba8bb85c8ede5b8",
|
||||
"gsd-core/workflows/scan.md": "47371c2073d6c0be",
|
||||
"gsd-core/workflows/secure-phase.md": "c55975672c4e1895",
|
||||
"gsd-core/workflows/secure-phase.md": "3063b0b6f7b56d46",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
"gsd-core/workflows/settings-advanced.md": "bf8ea69c8f7ae019",
|
||||
"gsd-core/workflows/settings-integrations.md": "166ca51b1f33c2a3",
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"agents/gsd-project-researcher.md": "425a7df7f37a5c06",
|
||||
"agents/gsd-research-synthesizer.md": "9d31c87fc2c87ffa",
|
||||
"agents/gsd-roadmapper.md": "64dce5d5f9fa5654",
|
||||
"agents/gsd-security-auditor.md": "e4d35ada4ea67d7f",
|
||||
"agents/gsd-security-auditor.md": "ed330ecbd9dbc377",
|
||||
"agents/gsd-ui-auditor.md": "86797e85f718dfac",
|
||||
"agents/gsd-ui-checker.md": "cfc8a3bac0a0ef5b",
|
||||
"agents/gsd-ui-researcher.md": "8799b6013e06ae49",
|
||||
@@ -287,7 +287,7 @@
|
||||
"gsd-core/workflows/resume-project.md": "a0443839f1f83c2d",
|
||||
"gsd-core/workflows/review.md": "761dd1ae5be40613",
|
||||
"gsd-core/workflows/scan.md": "b28f65d88c522767",
|
||||
"gsd-core/workflows/secure-phase.md": "f2957d4b88fb3746",
|
||||
"gsd-core/workflows/secure-phase.md": "a503dc469fd7a252",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
"gsd-core/workflows/settings-advanced.md": "49be159144d7f426",
|
||||
"gsd-core/workflows/settings-integrations.md": "1dce76db0aca08a5",
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"agents/gsd-project-researcher.md": "60573a38d3dfd9fe",
|
||||
"agents/gsd-research-synthesizer.md": "1f7cd286c5783c86",
|
||||
"agents/gsd-roadmapper.md": "277e0a3252553ab7",
|
||||
"agents/gsd-security-auditor.md": "0113435969e869c4",
|
||||
"agents/gsd-security-auditor.md": "30b1f87cdfc05de1",
|
||||
"agents/gsd-ui-auditor.md": "9b988b95d28e56ed",
|
||||
"agents/gsd-ui-checker.md": "e078ea5a07313976",
|
||||
"agents/gsd-ui-researcher.md": "cc9578c4f686d926",
|
||||
@@ -358,7 +358,7 @@
|
||||
"gsd-core/workflows/resume-project.md": "7f8dc986f0f35d96",
|
||||
"gsd-core/workflows/review.md": "f8109b9ec1f56962",
|
||||
"gsd-core/workflows/scan.md": "47371c2073d6c0be",
|
||||
"gsd-core/workflows/secure-phase.md": "e8855104c1e0417c",
|
||||
"gsd-core/workflows/secure-phase.md": "4977cf9e0462745b",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
"gsd-core/workflows/settings-advanced.md": "edd858cd6cfddaf1",
|
||||
"gsd-core/workflows/settings-integrations.md": "3ea8095d5fad891b",
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
".kimi/hooks/managed-hooks-registry.cjs": "08ec2585a3f8f132",
|
||||
".kimi/package.json": "dbf8353f77358bc1",
|
||||
"agents/gsd.md": "60fee7782ae4f2c6",
|
||||
"agents/gsd.yaml": "253a23ddda06c6c2",
|
||||
"agents/gsd.yaml": "b5f16c9fcf92cbff",
|
||||
"agents/subagents/gsd-advisor-researcher.md": "81bdc6cbd8fcde40",
|
||||
"agents/subagents/gsd-advisor-researcher.yaml": "662ced4837207406",
|
||||
"agents/subagents/gsd-ai-researcher.md": "f4cd2f17d2c3e35f",
|
||||
@@ -87,8 +87,8 @@
|
||||
"agents/subagents/gsd-research-synthesizer.yaml": "898104ab3bb0b81a",
|
||||
"agents/subagents/gsd-roadmapper.md": "62359d6876022b48",
|
||||
"agents/subagents/gsd-roadmapper.yaml": "679772cb14f3a015",
|
||||
"agents/subagents/gsd-security-auditor.md": "e621d1ee6b7aee6c",
|
||||
"agents/subagents/gsd-security-auditor.yaml": "fe8a4345cc13571d",
|
||||
"agents/subagents/gsd-security-auditor.md": "c6108af63b5f481d",
|
||||
"agents/subagents/gsd-security-auditor.yaml": "924783f1da6777b4",
|
||||
"agents/subagents/gsd-ui-auditor.md": "e4a319070959ebbc",
|
||||
"agents/subagents/gsd-ui-auditor.yaml": "3fc98c1d9e8f10fd",
|
||||
"agents/subagents/gsd-ui-checker.md": "07cd4e382ca55994",
|
||||
@@ -351,7 +351,7 @@
|
||||
"gsd-core/workflows/resume-project.md": "f28da1200e4545f4",
|
||||
"gsd-core/workflows/review.md": "ad7c0f372ed986ae",
|
||||
"gsd-core/workflows/scan.md": "003883d71c37da7d",
|
||||
"gsd-core/workflows/secure-phase.md": "29fc6b62c5c5dc62",
|
||||
"gsd-core/workflows/secure-phase.md": "22ab3bb5da494ea7",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
"gsd-core/workflows/settings-advanced.md": "414db4dbea97ba44",
|
||||
"gsd-core/workflows/settings-integrations.md": "70515c5838fb9826",
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"agents/gsd-project-researcher.md": "dae210ae0b3c6e2b",
|
||||
"agents/gsd-research-synthesizer.md": "e02c6ad5d1b74171",
|
||||
"agents/gsd-roadmapper.md": "1658a40b20d8b575",
|
||||
"agents/gsd-security-auditor.md": "e36e436c0d5c26d5",
|
||||
"agents/gsd-security-auditor.md": "64aa435793af52ec",
|
||||
"agents/gsd-ui-auditor.md": "e810012e685b2466",
|
||||
"agents/gsd-ui-checker.md": "4f88fd4c9d4c56a3",
|
||||
"agents/gsd-ui-researcher.md": "ecb617901cb7ad06",
|
||||
@@ -358,7 +358,7 @@
|
||||
"gsd-core/workflows/resume-project.md": "ad9f06a10bab8cc0",
|
||||
"gsd-core/workflows/review.md": "2d28a6683ff587de",
|
||||
"gsd-core/workflows/scan.md": "ad8ebcad4626d4a8",
|
||||
"gsd-core/workflows/secure-phase.md": "e9a488cec3b4efdc",
|
||||
"gsd-core/workflows/secure-phase.md": "71e6e689e80288ec",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
"gsd-core/workflows/settings-advanced.md": "252b0d3edc315339",
|
||||
"gsd-core/workflows/settings-integrations.md": "d1711a95f44fdbde",
|
||||
|
||||
2
tests/fixtures/golden-install-parity/pi.json
vendored
2
tests/fixtures/golden-install-parity/pi.json
vendored
@@ -254,7 +254,7 @@
|
||||
"gsd-core/workflows/resume-project.md": "f28da1200e4545f4",
|
||||
"gsd-core/workflows/review.md": "ad7c0f372ed986ae",
|
||||
"gsd-core/workflows/scan.md": "003883d71c37da7d",
|
||||
"gsd-core/workflows/secure-phase.md": "29fc6b62c5c5dc62",
|
||||
"gsd-core/workflows/secure-phase.md": "22ab3bb5da494ea7",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
"gsd-core/workflows/settings-advanced.md": "414db4dbea97ba44",
|
||||
"gsd-core/workflows/settings-integrations.md": "70515c5838fb9826",
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"agents/gsd-project-researcher.md": "b5baac64a15c85e2",
|
||||
"agents/gsd-research-synthesizer.md": "6cd9b501dc97bd50",
|
||||
"agents/gsd-roadmapper.md": "c357a77ab919e9e5",
|
||||
"agents/gsd-security-auditor.md": "d6d8f82501f10b92",
|
||||
"agents/gsd-security-auditor.md": "9e3bc9a62036352c",
|
||||
"agents/gsd-ui-auditor.md": "47937e784c9ae541",
|
||||
"agents/gsd-ui-checker.md": "7d708c53a106f748",
|
||||
"agents/gsd-ui-researcher.md": "e3768304c1c77753",
|
||||
@@ -287,7 +287,7 @@
|
||||
"gsd-core/workflows/resume-project.md": "7f20769f302e5427",
|
||||
"gsd-core/workflows/review.md": "bcbc20cb8df021cc",
|
||||
"gsd-core/workflows/scan.md": "949692db4834dd27",
|
||||
"gsd-core/workflows/secure-phase.md": "6758f1acf4113e9e",
|
||||
"gsd-core/workflows/secure-phase.md": "ef7b5ad194b687bf",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
"gsd-core/workflows/settings-advanced.md": "5e05212fb5cba531",
|
||||
"gsd-core/workflows/settings-integrations.md": "29c5de27fbbb18e9",
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"agents/gsd-project-researcher.md": "ddf7794e81300032",
|
||||
"agents/gsd-research-synthesizer.md": "a124b00271748d07",
|
||||
"agents/gsd-roadmapper.md": "493ef92b42b12cf4",
|
||||
"agents/gsd-security-auditor.md": "fbd3798eec23651b",
|
||||
"agents/gsd-security-auditor.md": "1e2eea5b2ab16d6e",
|
||||
"agents/gsd-ui-auditor.md": "771ae08260534d5c",
|
||||
"agents/gsd-ui-checker.md": "7ecd910efa6eb00e",
|
||||
"agents/gsd-ui-researcher.md": "9e1a84a55a4cac99",
|
||||
@@ -287,7 +287,7 @@
|
||||
"gsd-core/workflows/resume-project.md": "f242e4c8aba18ea2",
|
||||
"gsd-core/workflows/review.md": "835cf8c9594f17c1",
|
||||
"gsd-core/workflows/scan.md": "63631467651d9ca8",
|
||||
"gsd-core/workflows/secure-phase.md": "6cc236e53c2e7d56",
|
||||
"gsd-core/workflows/secure-phase.md": "4a647aec1e4d2dfe",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
"gsd-core/workflows/settings-advanced.md": "39e66386f6c48025",
|
||||
"gsd-core/workflows/settings-integrations.md": "f8f756709ec02363",
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"agents/gsd-project-researcher.md": "f6697b316b5995ba",
|
||||
"agents/gsd-research-synthesizer.md": "04036f38c1d373ea",
|
||||
"agents/gsd-roadmapper.md": "fb62e1e3de84b5f9",
|
||||
"agents/gsd-security-auditor.md": "13b660e2d336ed8e",
|
||||
"agents/gsd-security-auditor.md": "cacf711cb835300b",
|
||||
"agents/gsd-ui-auditor.md": "20cc99872e9b998b",
|
||||
"agents/gsd-ui-checker.md": "56698909c270b130",
|
||||
"agents/gsd-ui-researcher.md": "c348aa3ff8412ecb",
|
||||
@@ -287,7 +287,7 @@
|
||||
"gsd-core/workflows/resume-project.md": "82cfe1b8cb17c085",
|
||||
"gsd-core/workflows/review.md": "3e72508a5dccd45a",
|
||||
"gsd-core/workflows/scan.md": "12c11b2edc165df9",
|
||||
"gsd-core/workflows/secure-phase.md": "7bf923689bf58288",
|
||||
"gsd-core/workflows/secure-phase.md": "185a15d389951e6e",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
"gsd-core/workflows/settings-advanced.md": "2f86ec7b998f9485",
|
||||
"gsd-core/workflows/settings-integrations.md": "b082fc518b484c07",
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"agents/gsd-project-researcher.md": "f572892f138734ff",
|
||||
"agents/gsd-research-synthesizer.md": "29949bf3f049a8f1",
|
||||
"agents/gsd-roadmapper.md": "840ac933e3b094f9",
|
||||
"agents/gsd-security-auditor.md": "b7202c44366697dd",
|
||||
"agents/gsd-security-auditor.md": "4b86fa11ebda981e",
|
||||
"agents/gsd-ui-auditor.md": "76f446c50edf81f8",
|
||||
"agents/gsd-ui-checker.md": "15949ccab982b71c",
|
||||
"agents/gsd-ui-researcher.md": "0e8ec8509d476904",
|
||||
@@ -358,7 +358,7 @@
|
||||
"gsd-core/workflows/resume-project.md": "f28da1200e4545f4",
|
||||
"gsd-core/workflows/review.md": "ad7c0f372ed986ae",
|
||||
"gsd-core/workflows/scan.md": "003883d71c37da7d",
|
||||
"gsd-core/workflows/secure-phase.md": "29fc6b62c5c5dc62",
|
||||
"gsd-core/workflows/secure-phase.md": "22ab3bb5da494ea7",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
"gsd-core/workflows/settings-advanced.md": "414db4dbea97ba44",
|
||||
"gsd-core/workflows/settings-integrations.md": "70515c5838fb9826",
|
||||
|
||||
73
tests/secure-phase-single-writer.test.cjs
Normal file
73
tests/secure-phase-single-writer.test.cjs
Normal file
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* Regression test for #2119: /gsd-secure-phase dual SECURITY.md writers.
|
||||
*
|
||||
* The gsd-security-auditor agent must NOT have Write/Edit tools — the
|
||||
* orchestrator (secure-phase.md Step 6) is the sole SECURITY.md writer.
|
||||
* The auditor returns a structured verdict; it never writes files.
|
||||
*/
|
||||
|
||||
const { describe, test } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const AGENT_PATH = path.join(__dirname, '..', 'agents', 'gsd-security-auditor.md');
|
||||
|
||||
function parseYamlTools(content) {
|
||||
const lines = content.split(/\r?\n/);
|
||||
let inFrontmatter = false;
|
||||
let inTools = false;
|
||||
const tools = [];
|
||||
for (const line of lines) {
|
||||
const trimmed = line.trim();
|
||||
if (trimmed === '---') {
|
||||
inFrontmatter = !inFrontmatter;
|
||||
if (!inFrontmatter) break;
|
||||
continue;
|
||||
}
|
||||
if (!inFrontmatter) continue;
|
||||
if (trimmed.startsWith('tools:')) {
|
||||
inTools = true;
|
||||
continue;
|
||||
}
|
||||
if (inTools) {
|
||||
if (trimmed.startsWith('- ')) {
|
||||
tools.push(trimmed.slice(2).trim());
|
||||
} else if (trimmed && !trimmed.startsWith('#')) {
|
||||
inTools = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return tools;
|
||||
}
|
||||
|
||||
describe('#2119 — security auditor is return-only (no file writes)', () => {
|
||||
const content = fs.readFileSync(AGENT_PATH, 'utf-8');
|
||||
|
||||
test('auditor tools do not include Write or Edit', () => {
|
||||
const tools = parseYamlTools(content);
|
||||
assert.ok(tools.length > 0, 'tools list should be non-empty');
|
||||
assert.ok(
|
||||
!tools.includes('Write'),
|
||||
`Write must not be in auditor tools (got: ${tools.join(', ')}) — orchestrator is the sole SECURITY.md writer (#2119)`,
|
||||
);
|
||||
assert.ok(
|
||||
!tools.includes('Edit'),
|
||||
`Edit must not be in auditor tools (got: ${tools.join(', ')}) — orchestrator is the sole SECURITY.md writer (#2119)`,
|
||||
);
|
||||
});
|
||||
|
||||
test('auditor description does not claim to produce SECURITY.md', () => {
|
||||
const lines = content.split(/\r?\n/);
|
||||
const descLine = lines.find((l) => l.startsWith('description:'));
|
||||
assert.ok(descLine, 'description field must exist');
|
||||
assert.ok(
|
||||
!descLine.includes('Produces SECURITY.md'),
|
||||
'description must not claim to produce SECURITY.md — auditor returns a verdict, orchestrator writes (#2119)',
|
||||
);
|
||||
assert.ok(
|
||||
descLine.includes('Returns structured') || descLine.includes('returns'),
|
||||
'description should state the auditor returns a structured verdict',
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -57,15 +57,18 @@ describe('SECURE: gsd-security-auditor agent', () => {
|
||||
);
|
||||
});
|
||||
|
||||
test('tools include Read, Write, Bash, Glob, Grep', () => {
|
||||
test('tools include Read, Bash, Glob, Grep but NOT Write or Edit (#2119)', () => {
|
||||
const content = fs.readFileSync(agentPath, 'utf-8');
|
||||
const requiredTools = ['Read', 'Write', 'Bash', 'Glob', 'Grep'];
|
||||
const requiredTools = ['Read', 'Bash', 'Glob', 'Grep'];
|
||||
for (const tool of requiredTools) {
|
||||
assert.ok(
|
||||
content.includes(`- ${tool}`),
|
||||
`tools must include ${tool}`
|
||||
);
|
||||
}
|
||||
// #2119: auditor is return-only — orchestrator is the sole SECURITY.md writer
|
||||
assert.ok(!content.includes('- Write'), 'tools must NOT include Write (#2119)');
|
||||
assert.ok(!content.includes('- Edit'), 'tools must NOT include Edit (#2119)');
|
||||
});
|
||||
|
||||
test('has <role> section', () => {
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
"resume-project.md": 17270,
|
||||
"review.md": 47168,
|
||||
"scan.md": 7732,
|
||||
"secure-phase.md": 13520,
|
||||
"secure-phase.md": 13622,
|
||||
"session-report.md": 4044,
|
||||
"settings-advanced.md": 40019,
|
||||
"settings-integrations.md": 15892,
|
||||
|
||||
Reference in New Issue
Block a user