The PR #147 review forged origin: static as a JWT principal through
three tools the first version never touched (brain_edit, brain_ingest_raw,
ingest_raw + brain_promote), and through YAML spellings a key-name match
missed (quoted, escaped, tagged, flow-mapped, merge keys, lone CR).
- api.StampProvenance removes every spelling of origin/written_by from
the frontmatter and appends the real values. A line is dropped if
EITHER yaml.v3 (decoding quotes, escapes, tags, flow maps) or the
brain's line-oriented readers would read it as provenance; complex
keys, orphan values and merge keys are dropped outright. Lone CRs in
frontmatter become LF. Principal values that are not bare words are
quoted.
- Applied as the last step of writeHallNote, UpdateNote, PromoteNote
(re-attributed to the promoter), brain_edit (re-attributed to the
editor) and both ingests (every page written or merged). REST
/write and /promote carry no principal and stamp unknown.
- Ingest: newlines in scalar fields fold to spaces; a page type must be
a plain lower-case name (../raw wrote outside wiki/, pre-existing).
- Audit: outcome is ok|error, never the error text, which echoed
argument values.
Tests: every escape from the review is a table case asserting the
invariant on the file (parses as YAML, yaml.v3 and brain_get agree,
exactly the stamped lines). Plus REST /write, REST capture AuthPath,
body-line provenance in search, and quoted principals; two of the
review's surviving flips now go red. Three tests renamed so names claim
only what they assert.
Refs #145#144
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
brain_write was an ungated, durable prompt-injection path: a session that
just read an untrusted page could write an entry every later session
retrieves as trusted guidance (OWASP MCP06). Decision (#145): tag
provenance rather than stage writes.
Every write and update stamps origin (static | jwt | unknown) and
written_by (the authenticated principal) into frontmatter. Server-owned:
origin/written_by in caller content or an update fragment are discarded,
so a caller cannot claim a more trusted origin. No principal stamps
origin: unknown, never silence. brain_update re-stamps to the last
writer. capture (the claude.ai connector door) stamps too, via a new
server-derived CaptureContext.AuthPath set by both capture doors after
decode. REST POST /write carries no principal and stamps unknown.
brain_query results carry origin/written_by when the note has them;
older notes omit them rather than fabricate. brain_get already returns
frontmatter.
Refs #145
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
brain_query and brain_read left no record of who read what; only capture
and governance edits reached the audit sink (OWASP MCP08).
One slog record per call: tool, principal, origin (static/jwt/unknown),
wing/hall when given, outcome, duration. Content is reduced to
content_bytes so the audit line never copies brain content.
Refs #144
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>