Skip to content

Commit e4a929e

Browse files
author
merge-queue-bot
committed
Merge PR #533: Security review outputs — full repo audit @ 646f04a
2 parents 658b499 + 2a2e547 commit e4a929e

22 files changed

Lines changed: 709 additions & 67 deletions

File tree

.claude/skills/mdsmith-security-review/SKILL.md

Lines changed: 62 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -81,29 +81,73 @@ This skill runs in two modes. Decide first; if unclear, ask.
8181
for, per surface. **Read this file before forming conclusions**
8282
it is the core of the skill.
8383

84-
4. **Record findings in the structured model.** Capture each finding
85-
as a JSON object per the schema in `references/output-formats.md`.
86-
One finding = one defect. Assign severity using the rubric below
87-
and record `confidence` honestly (`confirmed` only if you traced
88-
the code path or built a repro; otherwise `likely` / `tentative`).
89-
90-
5. **Emit the three outputs.** From the single `findings.json`,
91-
render the report, the SARIF, and the inline annotations. Use the
92-
script — do not hand-write SARIF:
84+
4. **Record findings in the structured model.** Pick the review's
85+
directory first: `docs/security/<YYYY-MM-DD-slug>/` (today's date
86+
plus a short scope slug, e.g.
87+
`docs/security/2026-06-09-full-repo-audit/`). Author the findings
88+
as `<dir>/findings.json` per the schema in
89+
`references/output-formats.md`. One finding = one defect. Assign
90+
severity using the rubric below and record `confidence` honestly
91+
(`confirmed` only if you traced the code path or built a repro;
92+
otherwise `likely` / `tentative`).
93+
94+
5. **Emit the three outputs.** From the single findings file,
95+
render the report, the SARIF, and the inline annotations into
96+
the same directory. Use the script — do not hand-write SARIF:
9397

9498
```bash
95-
go run ./cmd/mdsmith-secreview render findings.json --out-dir <dir>
99+
go run ./cmd/mdsmith-secreview render \
100+
docs/security/<stem>/findings.json \
101+
--out-dir docs/security/<stem>/
96102
```
97103

98-
It writes `security-review.md`, `findings.sarif`, and
99-
`inline-annotations.json`; see `references/output-formats.md` for
100-
the schema and what each output is for.
101-
102-
6. **Summarize honestly.** Lead with the highest-severity confirmed
104+
It writes `report.md`, `findings.sarif`, and
105+
`inline-annotations.json` beside the `findings.json` input. The
106+
schema and the purpose of each output live in
107+
`references/output-formats.md`. The per-audit directory keeps
108+
every review's files apart, so a later review never overwrites an
109+
earlier one. `SECURITY.md`'s catalog indexes the new `report.md`
110+
on the next fix pass.
111+
112+
Then make the report pass the linter: add the front matter
113+
`docs/security/proto.md` requires (`date`, `scope`, `method`,
114+
`title`, `summary`), keep the H1 directly after the closing
115+
`---`, and run `mdsmith fix` on it. Regenerate `SECURITY.md`'s
116+
catalog with `mdsmith fix SECURITY.md`.
117+
118+
6. **Schedule the fixes as plans.** A review that finds defects
119+
but leaves no track record of how they get fixed is half a
120+
review. Turn the actionable findings into `plan/` files so the
121+
fix work is queued like any other task. Group by the fix, not by
122+
the finding: one plan covers one coherent change even when it
123+
closes several findings (the
124+
[`83_security-hardening-batch`](../../../plan/83_security-hardening-batch.md)
125+
plan is the canonical multi-finding batch).
126+
127+
- File one plan per Critical/High/Medium finding (or per shared
128+
fix). Batch the Low/informational/hardening items into a single
129+
"security hardening batch — `<date>`" plan rather than one file
130+
each.
131+
- Use the next free numeric prefix in `plan/` and follow
132+
[`plan/proto.md`](../../../plan/proto.md): front-matter `id`,
133+
`title`, `status: "🔲"`, a `summary`, and a `model` suggestion;
134+
body sections Goal, Tasks (red/green TDD steps), and Acceptance
135+
Criteria.
136+
- In the Goal, name each finding it closes by id and link the
137+
review's `report.md`, so the plan and the audit cross-reference.
138+
- Run `mdsmith fix PLAN.md` to refresh the index, then
139+
`mdsmith check plan/` so the new files pass.
140+
- In **PR-review mode**, prefer fixing a finding in the PR itself;
141+
file a plan only for follow-up work that is out of the PR's
142+
scope. In **audit mode**, file plans for every actionable
143+
finding.
144+
145+
7. **Summarize honestly.** Lead with the highest-severity confirmed
103146
findings. Separate confirmed defects from hardening suggestions.
104-
If you could not reach a conclusion on an in-scope area (e.g. you
105-
couldn't find the recipe-execution code), say so explicitly
106-
rather than implying it's clean.
147+
List the plan files you filed. If you could not reach a
148+
conclusion on an in-scope area (e.g. you couldn't find the
149+
recipe-execution code), say so explicitly rather than implying
150+
it's clean.
107151

108152
## Severity rubric
109153

.claude/skills/mdsmith-security-review/evals/README.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,18 +65,22 @@ Run these from the repo root.
6565
3. Start a fresh session with the skill available and give it the
6666
case `prompt`.
6767
4. Follow the skill: read `SKILL.md`, then
68-
`references/threat-model.md`, trace the code, record
69-
`findings.json`, and render it:
68+
`references/threat-model.md`. Trace the code. Pick a review
69+
directory under `docs/security/`. Record the findings in its
70+
`findings.json` and render them:
7071

7172
```bash
72-
go run ./cmd/mdsmith-secreview render findings.json --out-dir out
73+
go run ./cmd/mdsmith-secreview render \
74+
docs/security/<stem>/findings.json \
75+
--out-dir docs/security/<stem>/
7376
```
7477

7578
5. Score the mechanical bar with the grader, then judge the prose
7679
`must` / `must_not` yourself:
7780

7881
```bash
79-
go run ./cmd/mdsmith-secreview grade --findings findings.json \
82+
go run ./cmd/mdsmith-secreview grade \
83+
--findings docs/security/<stem>/findings.json \
8084
--cases .claude/skills/mdsmith-security-review/evals/cases.yaml \
8185
--case <case-id>
8286
```

.claude/skills/mdsmith-security-review/evals/cases.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ cases:
1717
- "Reads references/threat-model.md before concluding."
1818
- "Confirms the §0 baseline defenses against the actual code (recipes not executed,
1919
symlink default-deny, VS Code untrustedWorkspaces, no npm postinstall)."
20-
- "Produces all three outputs via the script (findings.sarif, security-review.md,
21-
inline-annotations.json)."
20+
- "Produces all three outputs via the script (findings.sarif, report.md,
21+
inline-annotations.json) under docs/security/<stem>/."
2222
- "Names residual un-traced areas in the coverage note rather than omitting them."
2323
must_not:
2424
- "Reports a Critical/High recipe-execution RCE without a traced exec call path."

.claude/skills/mdsmith-security-review/references/output-formats.md

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,31 @@
11
# Output Formats
22

3-
All three outputs are rendered from one **`findings.json`** by the
3+
All three outputs are rendered from one **findings file** by the
44
`mdsmith-secreview render` command. Author that one file; never
55
hand-write SARIF.
66

7+
## File layout
8+
9+
Each review owns a directory `docs/security/<YYYY-MM-DD-slug>/`
10+
holding four fixed-name files:
11+
12+
```text
13+
docs/security/<stem>/findings.json # authored input
14+
docs/security/<stem>/report.md # rendered report
15+
docs/security/<stem>/findings.sarif # rendered SARIF
16+
docs/security/<stem>/inline-annotations.json # rendered annotations
17+
```
18+
19+
Render with `--out-dir docs/security/<stem>/`. The directory — not a
20+
filename stem — namespaces each review, so the basenames are fixed
21+
and a later review never overwrites an earlier one. `SECURITY.md`'s
22+
`<?catalog?>` over `docs/security/*/report.md` indexes the report;
23+
run `mdsmith fix SECURITY.md` after rendering. The `security-note`
24+
kind validates `report.md` against `docs/security/proto.md`.
25+
726
## The finding object
827

9-
`findings.json` is `{"target": {...}, "findings": [ <finding>, ... ]}`.
28+
The findings file is `{"target": {...}, "findings": [ <finding>, ... ]}`.
1029

1130
```json
1231
{
@@ -73,7 +92,7 @@ title, the CWE (as a `properties.tags` entry), and the
7392
`security-severity`. `confidence` and `severity` are recorded in
7493
result `properties`.
7594

76-
## Output 2 (human report): `security-review.md`
95+
## Output 2 (human report): `report.md`
7796

7897
Layout the renderer produces:
7998

.mdsmith.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -232,8 +232,8 @@ overrides:
232232
paragraph-structure:
233233
max-sentences: 12
234234
max-words-per-sentence: 120
235-
- glob: ["docs/security/*.md", "!docs/security/proto.md",
236-
"website/content/docs/security/*.md"]
235+
- glob: ["docs/security/*/report.md",
236+
"website/content/docs/security/*/report.md"]
237237
rules:
238238
line-length:
239239
max: 600
@@ -378,7 +378,7 @@ kinds:
378378
required-structure:
379379
schema: .claude/skills/proto.md
380380
security-note:
381-
path-pattern: "docs/security/{proto.md,[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]-*.md}"
381+
path-pattern: "docs/security/{proto.md,*/report.md}"
382382
rules:
383383
required-structure:
384384
schema: docs/security/proto.md
@@ -601,8 +601,8 @@ kind-assignment:
601601
# against a synced file resolves to the same kind as the
602602
# source. The recursive walk still skips these because the
603603
# output directory is gitignored.
604-
- glob: ["docs/security/*.md",
605-
"website/content/docs/security/*.md"]
604+
- glob: ["docs/security/proto.md", "docs/security/*/report.md",
605+
"website/content/docs/security/*/report.md"]
606606
kinds: [security-note]
607607
- glob: ["docs/development/secret-rotations/*.md",
608608
"website/content/docs/development/secret-rotations/*.md"]

PLAN.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,4 +169,6 @@ footer: |
169169
| 239 | 🔲 | opus | [cuelite phase 3 — surface C (row-expr evaluator)](plan/239_cuelite-surface-c.md) |
170170
| 240 | 🔲 | sonnet | [cuelite phase 4 — drop cuelang.org and enable tinygo](plan/240_cuelite-drop-cue.md) |
171171
| 241 | 🔲 | opus | [Schema-per-file config under `.mdsmith/schemas/`](plan/241_schema-files.md) |
172+
| 242 | 🔲 | sonnet | [Recover from panics in the LSP lint pipeline](plan/242_lsp-panic-recovery.md) |
173+
| 243 | 🔲 | sonnet | [Security hardening batch — 2026-06-09 audit](plan/243_secreview-2026-06-09-hardening.md) |
172174
<?/catalog?>

SECURITY.md

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,23 +43,25 @@ repository.
4343
## Security Audit Log
4444

4545
Point-in-time security reviews live in
46-
[`docs/security/`](docs/security/) and follow the
47-
filename pattern `YYYY-MM-DD-<slug>.md`. Each note
48-
records the scope, the review method, the findings,
49-
and the fix or follow-up.
46+
[`docs/security/`](docs/security/). Each review is a
47+
directory named `YYYY-MM-DD-<slug>/`. It holds a
48+
`report.md` next to its machine-readable companions:
49+
`findings.json`, `findings.sarif`, and
50+
`inline-annotations.json`. The report records the
51+
scope, the method, the findings, and the follow-up.
5052

5153
<?catalog
5254
glob:
53-
- "docs/security/*.md"
54-
- "!docs/security/proto.md"
55+
- "docs/security/*/report.md"
5556
sort: -date
5657
header: |
5758
| Date | Review | Scope |
5859
|------|--------|-------|
5960
row: "| {date} | [{title}]({filename}) | {scope} |"
6061
?>
61-
| Date | Review | Scope |
62-
| ---------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
63-
| 2026-05-12 | [Supply-Chain Hardening — mini-shai-hulud / TanStack Class](docs/security/2026-05-12-supply-chain-hardening.md) | npm, PyPI, VS Code Marketplace, and Open VSX publishing surface; GitHub Actions CI/CD; lockfile and lifecycle-script handling. |
64-
| 2026-04-05 | [Adversarial Markdown Input](docs/security/2026-04-05-adversarial-markdown.md) | Adversarial markdown input causing unintended side effects on the host machine |
62+
| Date | Review | Scope |
63+
| ---------- | ---------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
64+
| 2026-06-09 | [mdsmith security audit — 2026-06-09](docs/security/2026-06-09-full-repo-audit/report.md) | full repo — all surfaces |
65+
| 2026-05-12 | [Supply-Chain Hardening — mini-shai-hulud / TanStack Class](docs/security/2026-05-12-supply-chain-hardening/report.md) | npm, PyPI, VS Code Marketplace, and Open VSX publishing surface; GitHub Actions CI/CD; lockfile and lifecycle-script handling. |
66+
| 2026-04-05 | [Adversarial Markdown Input](docs/security/2026-04-05-adversarial-markdown/report.md) | Adversarial markdown input causing unintended side effects on the host machine |
6567
<?/catalog?>

cmd/mdsmith-secreview/main.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,10 @@ const usageText = `Usage: mdsmith-secreview <command> [args]
3333
3434
Commands:
3535
render <findings.json> [--out-dir DIR]
36-
Render findings.sarif, security-review.md, and
36+
Render findings.sarif, report.md, and
3737
inline-annotations.json into DIR (default ".").
38+
Point DIR at the per-audit directory
39+
docs/security/<YYYY-MM-DD-slug>/.
3840
grade --findings F (--cases C --case ID | --forbid-severity S...
3941
--require-min-severity S --require-location-file F)
4042
Grade a findings.json against a case rubric (via
@@ -71,7 +73,7 @@ func run(args []string, stdout, stderr io.Writer) int {
7173
func runRender(args []string, stdout, stderr io.Writer) int {
7274
fs := flag.NewFlagSet("render", flag.ContinueOnError)
7375
fs.SetOutput(stderr)
74-
outDir := fs.String("out-dir", ".", "directory to write the three outputs into")
76+
outDir := fs.String("out-dir", ".", "per-audit directory to write the three outputs into")
7577
fs.Usage = func() {
7678
_, _ = fmt.Fprint(stderr, "Usage: mdsmith-secreview render <findings.json> [--out-dir DIR]\n")
7779
}

cmd/mdsmith-secreview/main_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ func TestRenderHappy(t *testing.T) {
6666
require.Equalf(t, 0, code, "stderr: %s", errOut)
6767
assert.Contains(t, out, "rendered 1 finding(s) ->")
6868
assert.Contains(t, out, "findings.sarif")
69-
for _, name := range []string{"findings.sarif", "security-review.md", "inline-annotations.json"} {
69+
for _, name := range []string{"findings.sarif", "report.md", "inline-annotations.json"} {
7070
_, err := os.Stat(filepath.Join(outDir, name))
7171
require.NoErrorf(t, err, "expected %s", name)
7272
}

docs/background/markdown-linters.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -989,7 +989,7 @@ you need a stable rule set while these land.
989989
[mado]: https://github.com/akiomik/mado
990990
[panache]: https://panache.bz/
991991
<!-- mdsmith plan + security + reference links -->
992-
[mdsmith-sec]: ../security/2026-04-05-adversarial-markdown.md
992+
[mdsmith-sec]: ../security/2026-04-05-adversarial-markdown/report.md
993993
[conventions]: ../reference/conventions.md
994994
[bench]: ../research/benchmarks/README.md
995995
[mdcov]: ../research/markdownlint-coverage/README.md

0 commit comments

Comments
 (0)