feat patch companion feat/companion-app #10 / 15
aaronrene · 5 days ago · Jun 5, 2026 · Diff

feat(companion): Phase 2 loopback endpoint security core (pure request-guard)

Add lib/companion-loopback-guard.mjs — a pure, I/O-free request-decision core (verifyLoopbackRequest) enforcing gate §4 loopback controls 1,2,3,5,6,8 at the request-decision level, plus rate-state helpers and a constant-time token comparator. No socket is bound: the gate's "DOES NOT approve" list still forbids opening a local HTTP listener, so the bind is deferred to Phase 5 behind an explicit gate (mirrors how Phase 1 shipped pure, tested decision logic).

Security properties (all fail-closed): strict Host allowlist + loopback-only check (DNS-rebinding defense), cross-site Origin/Sec-Fetch-Site rejection with no wildcard CORS / no Origin reflection, constant-time bearer-token compare, sliding-window rate limit ordered to bound brute-force without enabling a budget-exhaustion DoS, no ambient authority (verdict is the only output), note body never read (prompt-injection cannot alter the decision), and no token/JWT/ note body in any reason, return value, or thrown error.

7-tier test suite (test/companion-loopback-guard-*.test.mjs, 102 cases, green): unit, integration, e2e, stress, data-integrity, performance, and the security centerpiece (missing/wrong token, DNS-rebinding, cross-origin, rate-limit, no ambient authority, note-body-as-data, no-secret-in-output, global fail-closed).

docs/COMPANION-APP-PHASE-2-LOOPBACK-SECURITY.md records the accepted design, the adversarial threat model -> control mapping, the guard contract, and what Phase 5 must do to bind the socket safely.

Refs: gate §4, §10, §13; Phase 1 adapter seam.

sha256:edea42a90bef97963a66d35a47cb95a157c8b8469f11c4be01cedcfb9e0701ad sha
+44 symbols
sha256:32fd81bac998eaa72036c6a82ed65dbce2362fe810a2c81a5086a35c49f38c4d snapshot
+44
symbols added
0
dead code introduced
Semantic Changes 44 symbols
+ Companion App — Phase 2: Loopback Endpoint Security Core section Companion App — Phase 2: Loopback Endpoint Security Core L1–345
+ Adversarial threat model section 1. Adversarial threat model L63–141
+ Attacker A — malicious web page in the user's browser (cross-origin) section Attacker A — malicious web page in the user's browser (cross-origin) L69–89
+ Attacker B — DNS-rebinding (make a remote origin appear to target loopback) section Attacker B — DNS-rebinding (make a remote origin appear to target loopback) L89–106
+ Attacker C — a local non-browser process section Attacker C — a local non-browser process L106–124
+ Attacker D — prompt-injection payload inside a note body section Attacker D — prompt-injection payload inside a note body L124–141
+ mjs section 2. Guard contract — lib/companion-loopback-guard.mjs L141–216
+ 1 verifyLoopbackRequest(params) → LoopbackVerdict section 2.1 verifyLoopbackRequest(params) → LoopbackVerdict L143–186
+ code[js] variable variable code[js] L147–151
+ table@L152 section table@L152 L152–161
+ table@L165 section table@L165 L165–176
+ 2 Rate-limit helpers section 2.2 Rate-limit helpers L186–197
+ 3 Why the Origin allowlist is the loopback origin only section 2.3 Why the Origin allowlist is the loopback origin only L197–216
+ Evaluation order (and why) section 3. Evaluation order (and why) L216–241
+ code variable variable code L220–229
+ The rate-limit recording contract section 4. The rate-limit recording contract L241–266
+ code[js] variable variable code[js] L246–252
+ Mapping: gate §4 controls → Phase 2 enforcement section 5. Mapping: gate §4 controls → Phase 2 enforcement L266–286
+ table section table L268–278
+ What Phase 5 must do to bind the socket safely section 6. What Phase 5 must do to bind the socket safely L286–321
+ Test obligations satisfied (gate §10, 7 tiers) section 7. Test obligations satisfied (gate §10, 7 tiers) L321–337
+ table section table L325–334
+ Deferred (explicitly not Phase 2) section 8. Deferred (explicitly not Phase 2) L337–345
+ Simple summary section Simple summary L16–37
+ Technical summary section Technical summary L37–63
~ lib/companion-loopback-guard.mjs .mjs 10 symbols added
+ constantTimeStringEqual function function constantTimeStringEqual L146–152
+ createLoopbackRateState function function createLoopbackRateState L120–128
+ deny function function deny L321–323
+ evaluateRateLimit function function evaluateRateLimit L226–249
+ getHeader function function getHeader L164–174
+ isLoopbackHost function function isLoopbackHost L210–214
+ parseHostHeader function function parseHostHeader L183–203
+ recordLoopbackRequest function function recordLoopbackRequest L267–283
+ shouldCountTowardRateLimit function function shouldCountTowardRateLimit L315–318
+ verifyLoopbackRequest function function verifyLoopbackRequest L366–451
+ base function function base L25–36
+ listenerDecide function function listenerDecide L26–30
+ req function function req L26–37
+ step function function step L40–47
+ base function function base L24–35
+ base function function base L37–48
+ toString method method toString L289–289
+ base function function base L25–36
+ goodRequest function function goodRequest L32–47

0 comments

No comments yet. Be the first to start the discussion.

To add a comment, use the Muse CLI: muse hub commit comment sha256:edea42a90bef97963a66d35a47cb95a157c8b8469f11c4be01cedcfb9e0701ad --body "your comment"