Security audit finding, commit abf4e87.
File: src/client/runtime.ts:223 → reconcileStream (runtime.ts:160-183)
Attack / Failure scenario
On cold load, reconcileStream(document) scans the entire document and trusts control templates as instructions, without distinguishing framework-emitted templates from app body content:
<template data-brandy-stream-redirect="URL"> → location.replace(URL)
<template data-brandy-stream-target="id"> → replaceChildren on the element with that id
If an app reflects unescaped input anywhere in the body, brandy upgrades an otherwise inert, CSP-contained HTML injection into:
- a script-less forced redirect (works even under a strict
script-src), or
- arbitrary DOM replacement by element id.
The soft-nav path already scopes reconciliation to the swapped fragment; only the cold-load path over-scans.
Suggested fix
Scope the control-template protocol so app content cannot forge it:
- Have the cold-load path only sweep past the
STREAM_BOUNDARY marker (mirroring how soft-nav scopes to the swapped fragment), and/or
- Gate control-template processing on the server-set
x-brandy-stream disposition.
Security audit finding, commit
abf4e87.File:
src/client/runtime.ts:223→reconcileStream(runtime.ts:160-183)Attack / Failure scenario
On cold load,
reconcileStream(document)scans the entire document and trusts control templates as instructions, without distinguishing framework-emitted templates from app body content:<template data-brandy-stream-redirect="URL">→location.replace(URL)<template data-brandy-stream-target="id">→replaceChildrenon the element with that idIf an app reflects unescaped input anywhere in the body, brandy upgrades an otherwise inert, CSP-contained HTML injection into:
script-src), orThe soft-nav path already scopes reconciliation to the swapped fragment; only the cold-load path over-scans.
Suggested fix
Scope the control-template protocol so app content cannot forge it:
STREAM_BOUNDARYmarker (mirroring how soft-nav scopes to the swapped fragment), and/orx-brandy-streamdisposition.