docs(lessons): record the ADR-numbering collision and the RolesGuard fail-open trap - #2116
Merged
Merged
Conversation
…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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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:
041was already merged onmain039was claimed by feat(orders,analytics): order analytics read model + sales/channel/top-products endpoints + per-line tax rate + net tax basis #2014 and already referenced by name six times fromdocs/plans/implementation-plan-order-cancellation-record-state.mdonmain— merging would have silently repointed a live link to the wrong ADR040was claimed by docs(orders,currency): order-time FX rate snapshot + reporting-currency stamping #2050Compounding 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-0NNacrossdocs/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 tomain.Points at #2082 for mechanical enforcement rather than duplicating it.
2. A new authenticated principal must never land on
req.userRolesGuard.canActivatereturnstruewhen a route carries no@Roles()decorator, and it is registered as a globalAPP_GUARD. So any principal satisfyingJwtAuthGuardthat reachesreq.useris 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.userwith 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 splitmcp-tokens.controller.tsdocuments). The entry records that separation as the rule, and notes that copying only the storage half of themcp_tokenspattern is not enough.Points at #2079 for the guard hardening.
Why
lessons.mdrather than a canonical docPer 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 toscopes and cite their tracking issue, so they graduate cleanly if either hardens into a rule.Refs #1032