gate the raw-HTML default instead of remembering it
Since Arc 1 the latent list has carried "raw HTML is omitted only because goldmark's default omits it". Shortcodes were the trigger, and they came and went without needing unsafe mode — a call renders a theme template (ADR-0036). So the right close is not a note saying we got away with it, but a gate: verify.sh now fails on WithUnsafe in any non-comment line under cmd/ or internal/. Proved both directions before keeping it. The first version also failed on the comment in render.go that explains the rule — a gate that fires on its own documentation is one nobody keeps, so it now skips comment lines. That is the seventh gate defect found by running against real code rather than reasoning about it. The latent row is gone rather than reworded, and the untrusted-boundary row says which half is now mechanism and which half still isn't.
This commit is contained in:
@@ -52,6 +52,12 @@ must do — not the theme. `verify.sh` fails if the embedded reference theme cha
|
||||
changing, because in practice those two drift together — and it fails on a `<script>` tag in that theme,
|
||||
because a reference theme that grows taste stops being a reference (ADR-0026).
|
||||
|
||||
**The injection boundary is a gate now, not a memory.** goldmark drops raw HTML from authored Markdown by
|
||||
default, and that default was the only thing standing between a Markdown file and script injection. A
|
||||
feature wanting to emit HTML renders a theme template instead (ADR-0036), so nothing here needs raw HTML
|
||||
enabled — and `verify.sh` fails on `WithUnsafe` appearing in any non-comment line under `cmd/` or
|
||||
`internal/`. Anything that genuinely needs it must argue with a failing gate first, which is the point.
|
||||
|
||||
## Coming back after a long absence
|
||||
|
||||
In order, cheapest first:
|
||||
|
||||
+2
-3
@@ -1,6 +1,6 @@
|
||||
# State
|
||||
|
||||
**Verified against:** `26cc829` on 2026-07-30 — update this line every change.
|
||||
**Verified against:** `820720d` on 2026-07-30 — update this line every change.
|
||||
If this file disagrees with the code, the code is right and this file is a bug.
|
||||
|
||||
## Inventory
|
||||
@@ -61,10 +61,9 @@ with a stated reason. A list nothing drains is a graveyard of known defects.
|
||||
| Item | Why it waits | Trigger to fix |
|
||||
|---|---|---|
|
||||
| No mechanical check that the counters are *correct* | Accepted at the Arc 1 boundary: the coupling gate makes forgetting them impossible, which is the real failure mode, and checking the values needs code to count | The first page-level transform (queue 15), now that the transform counter means something narrower |
|
||||
| No mechanical gate on the untrusted boundary (ADR-0003) | Scheduled to Arc 3: nothing untrusted is read yet | The comment path — a test that untrusted input reaches no shortcode or template evaluation |
|
||||
| No mechanical gate on the untrusted boundary (ADR-0003) | Scheduled to Arc 3: nothing untrusted is read yet. Half of it is now mechanical — `verify.sh` rejects `WithUnsafe`, so authored Markdown cannot become HTML — but there is still no check that a *future* untrusted source stays out of shortcode and template evaluation | The comment path — a test that untrusted input reaches no shortcode or template evaluation |
|
||||
| `date` stays in `Extra` after being lifted onto `Bundle.Date`, unlike `title`, `aliases`, `tags` and `order`, which are deleted | Spotted while adding `order`; the theme contract says `Extra` holds what the parser does not name, so one of the two is wrong. Harmless today — a template reading `.Extra.date` gets the raw YAML value | Whatever next reads `Extra` generically: feeds (queue 14) or `check` (17) |
|
||||
| Sequence resolution rescans the index on every bundle request — two passes over every key, each doing a `Lookup` | No cache exists anywhere yet, and a site of this size resolves in microseconds. Measuring first is the rule (queue 16) | The page cache (queue 16), which is the thing that makes the cost visible |
|
||||
| Raw HTML in Markdown is omitted only because goldmark's default omits it | Shortcodes landed without unsafe mode — a call renders through a template instead (ADR-0036), so the feared trigger came and went. What remains is that nothing *stops* a later change from enabling `html.WithUnsafe()`, which would silently turn authored Markdown into an injection path | Now: a gate rejecting `WithUnsafe` anywhere in the tree, so the rule is mechanism rather than memory |
|
||||
|
||||
## Open questions
|
||||
|
||||
|
||||
@@ -100,6 +100,20 @@ if [ -d .git ] && command -v git >/dev/null 2>&1; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# Invariant 2, as mechanism rather than memory. goldmark drops raw HTML by default, and that default is
|
||||
# the only thing standing between an authored Markdown file and script injection. A shortcode wanting to
|
||||
# emit HTML renders a theme template instead (ADR-0036), so nothing in this engine has a reason to turn
|
||||
# it off — and the day something seems to, it should have to argue with a failing gate first.
|
||||
# Comment lines are excluded: the rule is worth explaining next to the code it governs, and a gate that
|
||||
# fires on its own documentation is a gate nobody keeps.
|
||||
unsafe=$(grep -rn 'WithUnsafe' --include='*.go' cmd internal 2>/dev/null |
|
||||
grep -vE ':[0-9]+:[[:space:]]*//' | cut -d: -f1,2 || true)
|
||||
if [ -n "$unsafe" ]; then
|
||||
bad "html.WithUnsafe enables raw HTML from authored content (ADR-0003, ADR-0036): $(echo "$unsafe" | tr '\n' ' ')"
|
||||
else
|
||||
pass "raw HTML stays disabled"
|
||||
fi
|
||||
|
||||
# Dangling references. Every one of these found a real stale pointer when run by hand.
|
||||
refs=$(grep -rhoE '`(docs|scripts|ideas|reference|\.claude)/[A-Za-z0-9_./-]+`' \
|
||||
docs CLAUDE.md HARNESS.md ideas reference .claude scripts 2>/dev/null | tr -d '`' | sort -u)
|
||||
|
||||
Reference in New Issue
Block a user