Skip to content

Gap-review follow-ups (remaining): @rangojs/router/testing refinements + cross-seam + self-review #582

Closed

Description

@ivogt

Split from #572. The Security defense-in-depth section of #572 was resolved by #580 (open-redirect guard, hardened during review to an unforgeable out-of-band brand + http(s)-only external validation; fail-closed origin guard; decode-error leak removed; the PE-decode-reject item declined as a false positive and the action-ownership item deferred — both documented). This issue tracks the remaining lower-severity items from #572 that #580 did not touch.

@rangojs/router/testing refinements

  • medium — dispatch() json route lacks the nested-Promise guard production enforces. src/testing/dispatch.ts still does a bare JSON.stringify(result) (~L180); production (src/rsc/response-route-handler.ts) uses a replacer that throws RESPONSE_NOT_SERIALIZABLE on a nested unresolved promise. A dispatch json test of () => ({ data: fetchSomething() }) (forgotten await) goes green while production would 500. Reuse production's replacer so dispatch rejects exactly as production does. (Not touched by feat(testing): #542 capability + coverage polish for @rangojs/router/testing #581.)
  • low — dispatch resolves cache config with executionContext=undefined, diverging from production. src/testing/dispatch.ts. (Not touched by feat(testing): #542 capability + coverage polish for @rangojs/router/testing #581.)
  • low (verify) — renderToFlightString/renderServerTree scoped route map. feat(testing): #542 capability + coverage polish for @rangojs/router/testing #581 added a routeName option but not a scoped routeMap; setRequestContextParams still rebuilds ctx.reverse against the global route map, so a server component that reverses stays order-dependent on whatever router registered last. Add a routeMap option (like renderHandler) or document. src/testing/flight.ts.
  • low (verify) — renderRoute still stamps synthetic ids onto the consumer's handle object. feat(testing): #542 capability + coverage polish for @rangojs/router/testing #581 added cloneHandleSeed for the seed maps, but ensureSyntheticId still writes handle[field] = id onto the passed object. Confirm no consumer-owned loader/locationState/handle object is mutated; clone before stamping if it is. src/testing/render-route.tsx.

Cross-seam

  • low — Prerendered route served on a prerender hit never records its cache({ tags }) into the document tag union. src/cache/.../cache-lookup.ts.
  • low — Document-cache tag union can miss first-write segment-DSL tags when combined with proactive/background segment caching (documented caveat; the proactive path widens the window).

Self-review

  • low — loaderParentId regex residual edge for a slot name matching D<digits>. (e.g. @D3.foo). src/segment-system.tsx. Pathological, but could be hardened by validating slot names at definition time.

File/line references are from the #572 audit; re-confirm against current main before fixing (the codebase moved with #580 and #581).

Activity

  1. ivogt commented on Jun 14, 2026

    @ivogt
    CollaboratorAuthor

    PR #583 (open) addresses the first two @rangojs/router/testing items:

    • medium — dispatch() json nested-Promise guard — extracted production's replacer into a shared rsc/json-route-result.ts (lightweight, to avoid dragging response-route-handler's virtual-module graph into the testing bundle); dispatch now 500s on a forgotten await exactly like production.
    • low — scoped routeMap for renderToFlightString/renderServerTree — added a routeMap option (mirroring renderHandler) so ctx.reverse() is no longer order-dependent on the global map.

    Re-verified the other two testing items against current main:

    • low — renderRoute synthetic-id stamping: already addressed by feat(testing): #542 capability + coverage polish for @rangojs/router/testing #581 — a stable syntheticIds WeakMap makes the id idempotent and render-route.tsx now documents the input-mutation contract. Cloning a handle risks breaking method/identity, so the documented-mutation approach stands. Considering this done.
    • info — dispatch executionContext=undefined: correct for an in-process harness (no platform execution context exists); leaving as-is.

    Still open here: the cross-seam (prerender tag-union) and self-review (loaderParentId regex) items.

  2. ivogt commented on Jun 16, 2026

    @ivogt
    CollaboratorAuthor

    Status after #583:

    Resolved:

    Still tracked here:

    • prerender hits do not appear to carry/record cache tags into the document-cache tag union. Runtime segment-cache hits record cached tags into _requestTags, but PrerenderEntry currently carries segments / handles, so a document-cache entry built on top of a prerender hit can be untagged.
    • loaderParentId has a low-severity regex edge for slot names like @D3.foo; the current fix handles names like @Detail, but the D<digits>. pattern can still match inside a user-controlled slot name.

    The first-write segment DSL tag-union caveat looks stale on current main: CacheScope.cacheRoute() records resolved DSL tags early, and document cache drains the render stream before snapshotting _requestTags. Leaving that as verify-only unless there is a concrete repro.

  3. added 6 commits that reference this issue on Jun 16, 2026
  4. ivogt commented on Aug 30, 2026

    @ivogt
    CollaboratorAuthor

    Remaining items from the last status comment are done or declined.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions