Skip to content

docs(lessons): record the ADR-numbering collision and the RolesGuard fail-open trap - #2116

Merged
piotrswierzy merged 1 commit into
mainfrom
2082-lessons-adr-numbering-rolesguard
Aug 14, 2026
Merged

docs(lessons): record the ADR-numbering collision and the RolesGuard fail-open trap#2116
piotrswierzy merged 1 commit into
mainfrom
2082-lessons-adr-numbering-rolesguard

Conversation

@piotrswierzy

Copy link
Copy Markdown
Collaborator

Docs-only. Two entries in docs/lessons.md, both empirical gotchas from the #1032 OMS review (#2066).

1. Claim an ADR number from the "Reserved numbers" note, not the last row of the index

#2066 numbered three ADRs by reading the index table and taking "last merged row + 1". All three were taken, because the table lists only merged ADRs while several are normally in flight:

Compounding it, that branch had also deleted main's ADR-041 row and the "Reserved numbers" note itself — removing the warning against the exact mistake, then making it. Per the review, the third numbering collision in two days.

The entry also records the renumber hazard, which is the more dangerous half: a blanket find-replace of ADR-0NN across docs/ corrupts other plans' legitimate references to the real ADR at that number. The fix is to scope the replace to files your PR authored and verify the rest byte-identical to main.

Points at #2082 for mechanical enforcement rather than duplicating it.

2. A new authenticated principal must never land on req.user

RolesGuard.canActivate returns true when a route carries no @Roles() decorator, and it is registered as a global APP_GUARD. So any principal satisfying JwtAuthGuard that reaches req.user is authorized on every undecorated route — including the customers controller (buyer PII), products, inventory, webhooks and cursors.

#1032's planned pack station proposed exactly that: a device principal on req.user with an endpoint allowlist the design called "the security boundary". It would have been decorative.

This is latent, not exploitable today — every principal currently in the system is an OL user with a role, and MCP correctly avoids the trap via @Public() + its own verifier (the split mcp-tokens.controller.ts documents). The entry records that separation as the rule, and notes that copying only the storage half of the mcp_tokens pattern is not enough.

Points at #2079 for the guard hardening.

Why lessons.md rather than a canonical doc

Per that file's own scope note, this is a regression ledger for empirical gotchas — not architectural rules. Neither entry states new policy: (1) is a process gotcha about a file's structure, and (2) restates an existing pattern that a design nearly violated. Both carry Applies to scopes and cite their tracking issue, so they graduate cleanly if either hardens into a rule.

Refs #1032

…fail-open trap

Two empirical gotchas from the #1032 OMS review, written forward.

1. ADR numbers. #2066 numbered three ADRs from the last row of the
   README index table, which lists only MERGED ADRs. All three were
   taken: 041 already merged, 039 claimed by #2014 and already
   referenced by name six times from a plan on main, 040 claimed by
   #2050. The branch had also deleted main's 041 row and the "Reserved
   numbers" note that named the collision - removing the warning, then
   making the mistake. Third collision in two days. Also records the
   renumber hazard: a blanket find-replace of ADR-0NN across docs/
   corrupts other plans' legitimate references to the real ADR at that
   number.

2. RolesGuard. canActivate returns true when a route has no @roles()
   decorator, and it is a global APP_GUARD. So a new principal placed on
   req.user is authorized on every undecorated route, including the
   customers controller. The planned pack-station device principal would
   have shipped exactly that, with an endpoint allowlist described as
   "the security boundary". Latent today only because every principal is
   currently an OL user with a role. The rule is the split MCP already
   uses: @public() plus a dedicated verifier, never req.user.

Both point at their tracking issues (#2082, #2079) rather than
duplicating the fix.

Refs #1032

Signed-off-by: Piotr Swierzy <piotr.swierzy@blockydevs.com>
@piotrswierzy
piotrswierzy merged commit f69067c into main Aug 14, 2026
8 checks passed
@piotrswierzy
piotrswierzy deleted the 2082-lessons-adr-numbering-rolesguard branch August 14, 2026 12:21
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