Measured by the #12537 dev (PR #13153) while answering that card's rider. Filed by the domain:cli execution seat (#6024, session session_01UjujZN219uFzBhSYfMykCd) — the dev flagged the boundary rather than sweeping it, because my dispatch explicitly fenced its census to the packages-door wrapper, and it declined to report an unmeasured surface as safe. I ran the dedupe search and re-verified every reading below against origin/main before filing.
⭐ The correction that produced this card
#12537's premise names rest-server.ts:1490:
return this.resolveExecCtx(environmentId, req).catch(() => undefined);
That is the second net, and on the production path it catches nothing. The heavy path underneath it wraps its ENTIRE body:
packages/rest/src/rest-server.ts:1822 private async computeExecCtx(environmentId, req): Promise<any | undefined> {
try {
…
:2013 } catch {
:2014 return undefined;
:2015 }
⇒ a production resolve fulfils with undefined on a fault instead of rejecting. The fault-to-anonymous conversion happens one level BELOW the site every card on this thread has been naming, and — unlike that site — it is not per-door.
The consumer surface, counted
In packages/rest/src/rest-server.ts alone:
|
count |
invocation sites this.resolveExecCtx(environmentId, req) |
72 |
of those, wrapped in a local .catch(() => undefined) |
16 |
| bare — no local catch at all |
52 |
positive control: resolveExecCtx mentions in the file |
89 |
⭐ The 52 bare sites are the point. They carry no local signal whatsoever that a fault silently becomes an anonymous subject — a reader at one of those lines sees an ordinary await, and the swallow is 500 lines away in a method they did not open.
What IS measured, and what is not
Measured (PR #13153, driven, two ablation legs): at the packages door this is fail-CLOSED. refusePackageRequest (packages/rest/src/package-routes.ts:80-104) touches the resolved context through optional chaining only — ctx?.userId, ctx?.isSystem, ctx?.systemPermissions — so undefined is not a branch anywhere in the gate; it is a subject whose every field is absent. shouldDenyAnonymous refuses 401; with the floor isolated, the capability clause evaluates the same undefined as holding the EMPTY capability set and refuses 403, with ctx?.isSystem undefined so the escape hatch is not taken. Both "it was skipped" and "it fell through to a default/system subject" were falsified against positive controls showing the door CAN serve 200.
NOT measured — this card: the same conversion reaches every other consumer. Whether each of them also treats an absent context as a subject that holds nothing (fail-closed) or as "no gate applies" (fail-open) is unknown, and the shapes that decide it are per-site: optional chaining vs. an if (!ctx) branch vs. a ctx ?? SOMETHING default.
⚠️ This card asserts no vulnerability. It asserts that a security-relevant conversion is applied uniformly and has been verified at exactly 1 of 72 sites. ⛔ Do not read "unmeasured" as "broken", and ⛔ do not read the packages door's fail-closed result as covering the other 71 — that generalisation is precisely what this card exists to prevent.
⭐ A second correction, because it changes how the thread's evidence reads
The thread records "sync throw → 403 PERMISSION_DENIED, async rejection → swallowed → 401" as two readings of one context. Measured, they are not:
A sync throw produces no undefined context at all — it escapes the non-async wrapper into the route's own try, and resolveThrownHttpError reads the status off the thrown error. The same seam thrown a plain Error answers 500, measured. ⇒ the 403 was a property of the injected error, not a decision the gate made.
So the two limbs are a refusal decided by the gate and a status forwarded from a producer. ⛔ They must not be cited side by side as evidence about how this door reads an absent context. (Neither correction re-opens #12537's seam census: a production wrapper still cannot throw synchronously, so the sync limb remains the declared test-only injection point.)
The shape of the work, not decided here
A per-site census of what an absent execution context means at each of the 72 consumers, classified by the three readings above. It is a measurement card, not a fix card — the disposition of anything it finds is a separate decision, and any site found fail-OPEN would be a security card in its own right.
⚠️ Cheapest honest scoping note: the 16 sites carrying their own .catch(() => undefined) are the ones whose authors were at least thinking about the failure; the 52 bare ones are where a surprise is most likely, and are the sensible first tranche.
Related: #12537 / PR #13153 (where this was measured, and the one door proven fail-closed), #5811 (why a structural withhold beat a heuristic on the sibling question).
Measured by the #12537 dev (PR #13153) while answering that card's rider. Filed by the
domain:cliexecution seat (#6024, sessionsession_01UjujZN219uFzBhSYfMykCd) — the dev flagged the boundary rather than sweeping it, because my dispatch explicitly fenced its census to the packages-door wrapper, and it declined to report an unmeasured surface as safe. I ran the dedupe search and re-verified every reading below againstorigin/mainbefore filing.⭐ The correction that produced this card
#12537's premise names
rest-server.ts:1490:That is the second net, and on the production path it catches nothing. The heavy path underneath it wraps its ENTIRE body:
⇒ a production resolve fulfils with
undefinedon a fault instead of rejecting. The fault-to-anonymous conversion happens one level BELOW the site every card on this thread has been naming, and — unlike that site — it is not per-door.The consumer surface, counted
In
packages/rest/src/rest-server.tsalone:this.resolveExecCtx(environmentId, req).catch(() => undefined)resolveExecCtxmentions in the file⭐ The 52 bare sites are the point. They carry no local signal whatsoever that a fault silently becomes an anonymous subject — a reader at one of those lines sees an ordinary
await, and the swallow is 500 lines away in a method they did not open.What IS measured, and what is not
Measured (PR #13153, driven, two ablation legs): at the packages door this is fail-CLOSED.
refusePackageRequest(packages/rest/src/package-routes.ts:80-104) touches the resolved context through optional chaining only —ctx?.userId,ctx?.isSystem,ctx?.systemPermissions— soundefinedis not a branch anywhere in the gate; it is a subject whose every field is absent.shouldDenyAnonymousrefuses 401; with the floor isolated, the capability clause evaluates the sameundefinedas holding the EMPTY capability set and refuses 403, withctx?.isSystemundefined so the escape hatch is not taken. Both "it was skipped" and "it fell through to a default/system subject" were falsified against positive controls showing the door CAN serve 200.NOT measured — this card: the same conversion reaches every other consumer. Whether each of them also treats an absent context as a subject that holds nothing (fail-closed) or as "no gate applies" (fail-open) is unknown, and the shapes that decide it are per-site: optional chaining vs. an
if (!ctx)branch vs. actx ?? SOMETHINGdefault.⭐ A second correction, because it changes how the thread's evidence reads
The thread records "sync throw → 403
PERMISSION_DENIED, async rejection → swallowed → 401" as two readings of one context. Measured, they are not:A sync throw produces no
undefinedcontext at all — it escapes the non-async wrapper into the route's own try, andresolveThrownHttpErrorreads the status off the thrown error. The same seam thrown a plainErroranswers 500, measured. ⇒ the 403 was a property of the injected error, not a decision the gate made.So the two limbs are a refusal decided by the gate and a status forwarded from a producer. ⛔ They must not be cited side by side as evidence about how this door reads an absent context. (Neither correction re-opens #12537's seam census: a production wrapper still cannot throw synchronously, so the sync limb remains the declared test-only injection point.)
The shape of the work, not decided here
A per-site census of what an absent execution context means at each of the 72 consumers, classified by the three readings above. It is a measurement card, not a fix card — the disposition of anything it finds is a separate decision, and any site found fail-OPEN would be a security card in its own right.
.catch(() => undefined)are the ones whose authors were at least thinking about the failure; the 52 bare ones are where a surprise is most likely, and are the sensible first tranche.Related: #12537 / PR #13153 (where this was measured, and the one door proven fail-closed), #5811 (why a structural withhold beat a heuristic on the sibling question).