You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(security): sanitize derived text at the MCP boundary (P2)
Executes P2 of docs/plans/2026-08-08-derived-artifact-hardening-execution-plan.md.
type_relations.target_text/to_symbol, symbol_effects.target_text, and
symbol_digests.rendered_text were the confirmed gap from this plan's §0.5
verification pass: derived facts surfaced by symbol_info/understand as
CALM's own analysis, but bypassing the injection_warning check that
source/understand's embedded-source-block/remember/recall/symbols_batch
already apply to the exact same class of untrusted content.
- fetch_architecture_digest (inspect.rs) now runs rendered_text through
sanitize_source_output (credential redaction, matching source's own
contract) then injection_warning, surfaced as a new
ArchitectureDigestOutput.content_warning field.
- New semantic_facts_content_warning checks type_relations.target_text/
to_symbol and symbol_effects.target_text via injection_warning only (no
credential redaction -- these are single AST identifier tokens, which
cannot syntactically contain the multi-character patterns that function
targets). Surfaced as a new SymbolInfoOutput.content_warning field,
shared by symbol_info and understand.
- fetch_semantic_facts itself is left byte-for-byte untouched: adding
content_warning as a third return value there tripped this repo's own
edit-safety tooling (a signature change on an existing function, even
with only 2 real callers, escalates to a human-review-required gate).
Worked around identically to P1: added the warning computation as a new
sibling function instead, wired in at the two call sites' bodies.
- New end-to-end test locks in both warnings firing via a real understand()
call against injection-shaped type_relations/symbol_digests rows.
locate.snap/symbol_info.snap/understand.snap toolsnaps regenerated for the
3 tools whose output schema gained the new field.
Verified: full `cargo test --workspace --features embeddings` green (1054
calm-core + 366 calm-server + all other packages, 0 failures), clippy -D
warnings clean, rustfmt clean.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: crates/calm-server/src/__toolsnaps__/locate.snap
+7Lines changed: 7 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -201,6 +201,13 @@
201
201
"items": {
202
202
"$ref": "#/$defs/EffectOutput"
203
203
}
204
+
},
205
+
"content_warning": {
206
+
"description": "P2 (docs/plans/2026-08-08-derived-artifact-hardening-execution-plan.md):\nset when any `type_relations`/`effects` `target_text`/`to_symbol`\nabove looks injection-shaped -- same trust boundary `source`'s\n`content_warning` covers, applied to syntax-derived facts instead of\na raw file body. Text itself is never mutated.",
Copy file name to clipboardExpand all lines: crates/calm-server/src/__toolsnaps__/symbol_info.snap
+7Lines changed: 7 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -371,6 +371,13 @@
371
371
"$ref": "#/$defs/EffectOutput"
372
372
}
373
373
},
374
+
"content_warning": {
375
+
"description": "P2 (docs/plans/2026-08-08-derived-artifact-hardening-execution-plan.md):\nset when any `type_relations`/`effects` `target_text`/`to_symbol`\nabove looks injection-shaped -- same trust boundary `source`'s\n`content_warning` covers, applied to syntax-derived facts instead of\na raw file body. Text itself is never mutated.",
376
+
"type": [
377
+
"string",
378
+
"null"
379
+
]
380
+
},
374
381
"caveat": {
375
382
"description": "Advisory hint on an empty/not-found result. Never set alongside a\npopulated `success` unless a tool opts in via `with_caveat` (e.g.\n`callers` on zero direct callers).",
Copy file name to clipboardExpand all lines: crates/calm-server/src/__toolsnaps__/understand.snap
+14Lines changed: 14 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -131,6 +131,13 @@
131
131
"items": {
132
132
"$ref": "#/$defs/EffectOutput"
133
133
}
134
+
},
135
+
"content_warning": {
136
+
"description": "P2 (docs/plans/2026-08-08-derived-artifact-hardening-execution-plan.md):\nset when any `type_relations`/`effects` `target_text`/`to_symbol`\nabove looks injection-shaped -- same trust boundary `source`'s\n`content_warning` covers, applied to syntax-derived facts instead of\na raw file body. Text itself is never mutated.",
137
+
"type": [
138
+
"string",
139
+
"null"
140
+
]
134
141
}
135
142
},
136
143
"required": [
@@ -446,6 +453,13 @@
446
453
"truncated": {
447
454
"description": "`true` when the underlying facts (callees/effects) were capped —\n`rendered_text` is a real subset, not the full picture, for a very\nhigh-fan-out symbol.",
448
455
"type": "boolean"
456
+
},
457
+
"content_warning": {
458
+
"description": "P2 (docs/plans/2026-08-08-derived-artifact-hardening-execution-plan.md):\nset when `rendered_text` (after credential redaction) looks\ninjection-shaped -- same trust boundary `source`'s `content_warning`\ncovers for a raw file body, applied here since `rendered_text`\naggregates callee/type/effect identifiers from across the graph and\nis presented as CALM's own analysis, not obviously untrusted.",
0 commit comments