Skip to content

T-01b: Agent pop-out/open-tab reopens the storage boundary (EXECUTED FAIL, no fix on this branch) - #122

Open
Natoshi-moto wants to merge 8 commits into
mainfrom
fable/t01-storage-boundary-001
Open

T-01b: Agent pop-out/open-tab reopens the storage boundary (EXECUTED FAIL, no fix on this branch)#122
Natoshi-moto wants to merge 8 commits into
mainfrom
fable/t01-storage-boundary-001

Conversation

@Natoshi-moto

Copy link
Copy Markdown
Owner

Status

No product code is proposed for merge here. This PR queues a security finding + evidence for review — the underlying question (remove the buttons? separate origin? disclose?) is an operator decision per the handoff brief.

One line

The T-01 iframe fix is real and verified. T-01 is still open through two other buttons in the Agent's own title bar — "Pop out" and "Open tab" — which load the same document top-level and unsandboxed, so it owns the host's storage outright.

What's in this branch

  • communications/publications/rounds/NOTED/2026-07-25_claude_t01b-popout-boundary-finding.md — round report (plain-language summary + what was verified)
  • operations/receipts/T01_FIX_20260725/CARD-04B-POPOUT.md — EXECUTED FAIL evidence (live probe: host localStorage read/write both SUCCEEDED from the popped-out window, IndexedDB opened)
  • communications/publications/epistemic/2026-07-25_claude_t01b-popout-boundary-epistemic.md — epistemic analysis
  • products/noted-host/scripts/ods-sec-003-t01b-popout-boundary.mjs (npm run ods:sec003) — permanent regression probe, checked in as a known red so the gap can't be forgotten
  • operations/handoffs/T01B_POPOUT_BOUNDARY_ADVERSARIAL_HANDOFF_001.md — tear-apart → fix → break → fix brief for whoever picks this up, with explicit kill criteria (any fix touching auth/crypto/deployment stops and returns to the operator)

Non-claims

  • status_authority: NONE
  • Not a security certificate; single-seat probe, not independently reproduced (same model family as the T-01 fix it's reviewing)
  • Not a claim the T-01 fix was wrong — it was correct and re-verified
  • dist/ build unprobed; T-02, T-03, CARD-11 untouched

🤖 Generated with Claude Code

Natoshi-moto and others added 7 commits July 25, 2026 08:52
Carries forward Grok's 2026-07-25 CARD-04 re-run (session 3) and the
Fable fix-draft handoff so the evidence this fix responds to travels
with the fix itself.

status_authority: NONE
The Agent iframe was served from a same-origin relative path AND
sandboxed with allow-same-origin, so the sandbox provided no real
storage boundary: script inside the iframe could read/write the
host's localStorage and open its IndexedDB directly, with no bridge
involved. EXECUTED-confirmed twice (BREAK_SESSION_20260722 CARD-04,
Anthropic; BREAK_SESSION_20260725 CARD-04, xAI).

Fix: remove allow-same-origin so the iframe gets an opaque origin.
Verified live that this closes the hole (ODS-SEC-001/002, new
permanent regression test at
scripts/ods-sec-001-t01-storage-boundary.mjs, npm run ods:sec001).

This makes the Agent's own storage calls throw too, so four call
sites in nexus-agent-v0.14-scrubbed.html that read/wrote
localStorage/sessionStorage without a try/catch are now guarded —
matching the storage layer's existing degrade-to-memory design
instead of crashing. The runAuthGate() sessionStorage recovery check
was the critical one: unguarded, it would have failed auth on every
boot, not just recovery attempts.

Synced products/nexus-blocks' canonical copy to match (README:
noted-host serves a byte-identical mirror); registry verify still
passes.

Verified: existing agent:prompt-smoke and bridge:smoke both pass with
zero console/page errors, typecheck clean, new ods:sec001 passes.
python3 -m unittest discover -s tests: 189/190 pass, the 1 failure is
pre-existing node_modules symlink/JSON tooling noise unrelated to
this change (same class already flagged in BREAK_SESSION_20260725).

Scope: T-01 storage boundary only. T-02 (unpinned CDN), T-03 (default
proxy), CARD-11 (crypto), and the production/dist second-origin
question (CARD-12 dist parity) are untouched and remain open.

status_authority: NONE — proposal/fix branch, not merged to main.
Documents the re-probe (ODS-SEC-001/002 now PASS on this branch),
what was verified, what was not (dist/ build, long-session behavior),
and the residual risks per the original handoff's required structure.

status_authority: NONE
Round-close publication + epistemic performance analysis per the
mandatory skill, documenting the T-01 fix, what was verified, what
was not, and the authorization-mapping inference made from the
operator's plain-language instruction.

status_authority: NONE
The T-01 fix (d59c15e) is correct and re-verified — `npm run ods:sec001`
passes. But it closes one of three routes into the same capability.

NexusAgentStudio.tsx opens the same Agent document top-level and
unsandboxed at line 46 ("Pop out", window.open) and line 89 ("Open tab",
<a target="_blank">) — ~40 lines above the line the fix patched. A
top-level same-origin document owns that origin's storage, so clicking
"Pop out" on the post-fix tree yields:

  host_ls_read     SUCCEEDED (synthetic canary retrieved verbatim)
  host_ls_write    SUCCEEDED (host page read the value back)
  host_keys_visible 13
  host_idb_open    SUCCEEDED (nexus-kernel)

noopener/noreferrer do not mitigate this — they sever window.opener, not
origin-scoped storage.

Filed:
- operations/receipts/T01_FIX_20260725/CARD-04B-POPOUT.md (EXECUTED)
- products/noted-host/scripts/ods-sec-003-t01b-popout-boundary.mjs
  (npm run ods:sec003) — checked in KNOWN RED, deliberately, not wired
  into any aggregate gate
- operations/handoffs/T01B_POPOUT_BOUNDARY_ADVERSARIAL_HANDOFF_001.md —
  tear apart / fix / break / fix brief for the next seat
- correction banner on T01_IFRAME_STORAGE_BOUNDARY_FABLE_RESPONSE.md,
  whose §1 asserted no such route exists
- round report + epistemic analysis + INDEX rows

No product code changed. Nothing merged; main untouched. Not independent
review — same model family as the seat that wrote the fix.

status_authority: NONE

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…E_ON_BRANCH)

Adds a proposed constitutional continuity loop: assess the prior task,
attack the current task, append state without erasing history, and never
mistake the resulting record for truth. Operator's verbatim message
preserved in both the constitutional document and a filed disclosure.
Wires the protocol into AGENTS.md, README_START_HERE.md, the mandatory
epistemic skill, and its template.

status_authority: NONE — no merge/push authorization claimed by the
originating round; pushed here on direct operator instruction.

Co-Authored-By: Codex <noreply@openai.com>
Pre-warning filed before the T-01b adversarial review (PR #122) lands:
the operator's credits for the reviewing Fable seat ran out partway
through, and they switched to Opus to continue. Filed per this repo's
disclosure discipline so the eventual review is read knowing it may
cross a model-family seam mid-task, not just what it concludes.

status_authority: NONE

Co-Authored-By: Claude Code <noreply@anthropic.com>
@Natoshi-moto

Copy link
Copy Markdown
Owner Author

Pre-warning for reviewers (Fable/Opus): the operator's credits for the Fable seat ran out partway through this review and they switched to Opus 5 to continue. Filed as a disclosure so the eventual review is read knowing it may cross a model-family seam mid-task — see user-disclosures/entries/2026-07-25_credits-exhausted-fable-to-opus-switch.md. Whichever seat picks this up should still state its own model family per the handoff brief's independence note.

Extends the T-01b arc. Documents only — no product code touched, no gate lifted.
G-01 stays RED.

Findings (evidence in EVIDENCE_ORIGIN_MODEL.md, probe checked in, re-runnable):
- Nexus_OS.html:1619/:1629 grants allow-same-origin to EVERY block it spawns,
  so the sandbox attribute is non-load-bearing for all 71 blocks. The Agent was
  never a special case (SOURCE_TRACED).
- The T-01 fix hardened 1 of 4 same-origin iframes. Router, Diagnostics and
  PromptStudioV3 still carry allow-same-origin; PromptStudioV3 holds the ps3
  credential blob (SOURCE_TRACED).
- Handoff Option C (blob:/data: delivery) is FALSIFIED: blob: inherits the
  creator's origin and read the host canary on both engines (EXECUTED).
- Two of three stated justifications for allow-same-origin are FALSIFIED:
  WebCrypto is present under an opaque origin, and cross-origin dynamic
  import() works. Only the localStorage objection survives (EXECUTED).
- Cross-origin tab (second port) is isolated, keeps its own storage, supports
  NATIVE fullscreen, and carries a verifiable event.origin (EXECUTED).
- noopener and a control channel are mutually exclusive; dropping noopener
  permits tabnabbing (EXECUTED, both engines).

Probe integrity: the tabnabbing assertion first reported HOLDS because an
escaped </script> meant the payload never ran. A probe that fails to execute
reports green. Recorded against my own interest; the corrected result stands.

Not executed / open: F-T01B-C (user-opened file -> privileged block) is an
inference from two verified links. WebKit untested. Real esm.sh graphs untested
(local CORS module only, per the no-third-party-probing rule).

Opens AUD-EXT-ORIGIN-MODEL-001: blind-first audit per constitution/AUDIT.md,
open to any provider family. All prior analysis in this arc is Anthropic-authored
and therefore SAME_FAMILY_DIFFERENTIAL, not independent corroboration.

Gates run clean with node_modules parked: doctor 0, verify 0, 190 tests OK.

status_authority: NONE

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant