Repository navigation
feat(authz): Layer C — fail-closed global route guard + route-inventory fitness test (cccvu/wiki-v2#13) - #3
Merged
Merged
Conversation
…ry fitness test (#13) Add the fork-side Layer C the platform already had. The fork had no global guard and no route-inventory test, so a new unguarded single-object route (Top Risk #3's unauthenticated half) could merge silently — a real leak (collab/stats) already existed. - route-guard/: a static route-inventory FITNESS TEST (TS-AST scan of the source, no boot/DB — robust to workspace-build/.tsx resolution), a shared pure classify() both the test and the guard funnel through, the intentional-unguarded LEDGER (13 reasoned entries), fork @PlatformAuthz/@PlatformPublic decorators, and the fail-closed PlatformAuthorizationGuard (offender -> 403; pass-through for decided routes so the controller's own JwtAuthGuard still enforces). - app.module.ts (documented seam #4): register the guard as APP_GUARD. - platform-authz.client.ts: fail-closed response-shape guards (Array.isArray / strict-boolean) so a malformed/Byzantine PDP 200 can't flow downstream as an allow. - collab/stats ledgered as a known-gap (separate CollabAppModule process, env-gated off) -> tracking issue cccvu/wiki-v2#80. The build-time fitness test keeps the ledger complete, so the runtime guard can never fail-close a legitimate route in a green build. The test proves AUTHENTICATION, not object-level authorization (that residual stays with Layer A) — documented in-code. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This was referenced Sep 2, 2026
PMQ9
added a commit
that referenced
this pull request
Sep 8, 2026
…ge, DTO validation, keyset hardening
All four round-justifying (non-blocking Level-3) findings from the 7-lens review, plus overlapping Level-2
robustness folded in while in the same files:
- [Testing L3 / DevOps L3] listByPage: give it opt-in {limit,before} keyset paging (consistent with the
members/ACL reads), and add service-attachment.pg.spec.ts — resolvePage + listByPage now execute on real
Postgres (a raw-sql column/table typo no longer ships green), and the paging keyset walks a shared-ms tie
with no skip / no duplicate.
- [Testing L3] Add dto-validation.spec.ts — the new class-validator DTOs (ContentSearchDto, ContentSortDto,
ContentCursorDto, ContentListDto incl. nested sort) now run through validate(), so dropping a constraint
(@Max/@IsUUID/@IsIn/@IsNotEmpty/@IsISO8601, …) reds a test.
- [Correctness L3] Add a real-Postgres walk for the generalized TIMESTAMP keyset (only title was walked) and
for value='' (null-title) as a mid-walk cursor bound.
- [Security F1 / Correctness #1 / Testing P2] A malformed keyset/cursor timestamp now 400s instead of
500-ing at the ::timestamptz cast: a shared isIsoInstant() guard (rejects Date.parse-lenient-but-PG-invalid
values like '2026') used by parseSubCollectionQuery and the content keyset builder.
- [Correctness #2] A text sort (title/name) now REQUIRES its cursor `value` (400) instead of silently
falling back to a timestamp `updatedAt` and paginating on the wrong boundary.
- [DevOps #3] Bump service-bridge.openapi.json version 1.1.1 → 1.2.0 (additive expansion).
Non-blocking Level ≤2 polish is deferred to a follow-up issue. Verified: nest build, 19 service-bridge suites
(179 tests), 3 real-Postgres pg specs (30 tests), eslint, import-boundary — all green.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
This was referenced Sep 11, 2026
PMQ9
added a commit
that referenced
this pull request
Sep 17, 2026
… content write when Redis off (docmost#344) Two pre-existing content-write defects on the collaboration path, both found while tracing docmost#282 and both able to lose or silently drop a page's content. docmost#342 — a content `replace` emptied the Yjs fragment BEFORE building the new document from the request body. If `TiptapTransformer.toYdoc` threw (a body that passed `jsonToNode` but the Yjs transformer rejects), the connection's closing store persisted the emptied fragment: the page was WIPED while the request also failed. Build the new state first, then delete+apply — a conversion failure now aborts before any mutation and the original content survives. Shared by the ordinary `updatePageContent` handler and the docmost#282 conditional write. docmost#344 — custom collab events route only through the RedisSync extension, so with COLLAB_DISABLE_REDIS (single-node standalone) `handleYjsEvent('updatePageContent')` returned undefined and `PageService.update` ignored it: a REST/`/v1` content write returned 200 while persisting nothing. Fail that one un-routable write loudly instead. Narrowest boundary: the supported standalone mode still boots, interactive editing (direct Hocuspocus path) is untouched, and the docmost#282 seams keep their fail-closed `undefined` contract. Tests: build-before-delete leaves the fragment intact on a throwing toYdoc; the gateway guard throws for updatePageContent with Redis off, stays quiet for best-effort events, and routes normally with Redis on. UPSTREAM_MODIFICATIONS.md seams #2/#3 updated. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This was referenced Sep 19, 2026
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.
Fork half of cccvu/wiki-v2#13. Companion to the super-repo PR (which bumps the submodule to this commit).
What
Adds the Layer C the fork lacked (the platform already had it):
authz/route-guard/route-inventory.spec.ts— a static TS-AST route-inventory fitness test: every controller route must be@Public/@PlatformPublic, guarded byJwtAuthGuard/CollabServiceSecretGuard,@PlatformAuthz, or in the reasoned intentional-unguarded ledger (13 entries) — else the build fails. AST scan (not import-reflection) so it's immune to workspace-build /.tsxresolution fragility.PlatformAuthorizationGuardregistered as a globalAPP_GUARD(seam fix(authz): harden PLATFORM_AUTHZ_TIMEOUT_MS parse (empty/typo → deny-all self-DoS) #4app.module.ts) — 403s any offender at runtime, pass-through for decided routes (the controller's own guard still enforces). Shares the exactclassify()the test uses.platform-authz.client.ts— fail-closedArray.isArray/strict-boolean guards so a Byzantine 200 can't flow downstream as an allow.Boundary
One upstream-file edit (
app.module.ts, documented seam #4); everything else is fork-owned underauthz/route-guard/.check-upstream-boundary.shpasses. The fitness test proves authentication, not object-level authorization (that residual stays with Layer A — documented in-code).Notes
collab/statsledgered as aknown-gap(separate CollabAppModule process, env-gated off) → cccvu/wiki-v2#80.nest buildclean,jest src/authz179/179; new-unguarded-route mutation → RED, reverted.🤖 Generated with Claude Code