PR: #462: feat(agents): lock the safe agent-output renderer [Story-1828]
by @ankitmaheshwari-com
View PR →
Story-1828 · app lane · RFD-SEC-001 D5 · security hardening
Lock the safe agent-output renderer
The agent brain is a trust boundary. An agent turn can carry an
<img> tracking pixel, an external link, raw HTML, or a
<script>. The renderer is safe today —
renderAtlasMarkdown is a dependency-free whitelist that builds
React nodes and never touches a raw-HTML sink. This story locks
that invariant so the planned "rich-markdown" swap cannot silently
re-open <img>/link exfiltration. A gate, not a ban.
What changed
- Render regression test —
src/components/add-matter/step5-review/render-atlas-markdown.test.tsxfeeds hostile content (<img>, external anchor,javascript:markdown link,<script>,<iframe>) through the canonical egress and asserts it renders as inert text: no live element, nosrc/href, no auto-fetch — while the allowed**bold**whitelist still renders a real<strong>. - CI gate —
pnpm check:render-safety(scripts/check-render-safety.mjs) scans the agent-output render path and fails the build on a raw-HTML sink (dangerouslySetInnerHTML,rehype-raw, or a full markdown library with no allowlist sanitizer). Wired into the CIqualityjob. - Sanitizer contract —
docs/build-doctrine/agent-output-rendering.mddocuments the locked invariant and the rules for any future rich formatting (tables/lists/blockquotes OK; no raw HTML; no auto-fetch img; links neutralized; one canonical egress). - Pointer comment added to
render-atlas-markdown.tsxtying the renderer to its test, gate, and contract doc.
The sanitizer contract
| Allowed (whitelist) | Forbidden (raw-HTML sink) |
|---|---|
| Bold, italic, paragraphs, line breaks | dangerouslySetInnerHTML |
| Lists, tables, blockquotes, code | rehype-raw |
| Links neutralized to inert text/chip | Markdown lib with no allowlist sanitizer |
| — | Auto-fetch <img>, <script>, <iframe> |
Verification
pnpm test— render lock regression suite green.pnpm check:render-safety— render path clean; gate exits 0.pnpm lint,pnpm typecheck,pnpm buildgreen.- No new dependencies; the renderer stays dependency-free.
Files changed
6 file(s), +231 / -0 lines
| Path | Role | + | - |
|---|---|---|---|
| .github/workflows/ci.yml | Workflow | +7 | -0 |
| docs/build-doctrine/agent-output-rendering.md | Documentation | +59 | -0 |
| package.json | Config | +1 | -0 |
| scripts/check-render-safety.mjs | Script | +101 | -0 |
| src/components/add-matter/step5-review/render-atlas-markdown.test.tsx | Component | +58 | -0 |
| src/components/add-matter/step5-review/render-atlas-markdown.tsx | Component | +5 | -0 |