Skip to content

fix(interop)!: reject reserved namespaces ns and nsapi (LAB-5876) - #143

Open
27Bslash6 wants to merge 3 commits into
mainfrom
lab-5876-reserve-ns-nsapi-interop-namespace
Open

27Bslash6 wants to merge 3 commits into
mainfrom
lab-5876-reserve-ns-nsapi-interop-namespace

Conversation

@27Bslash6

@27Bslash6 27Bslash6 commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

This PR reserves ns and nsapi as interop namespaces in the TypeScript SDK. Keys starting with ns: or nsapi: are parsed by the CachekitIO server as namespace-prefixed, so they would be rejected or misrouted. This implements the reservation defined in cachekit-io/protocol#78.

Public API changes (breaking)

  • cache.wrap(fn, { interop, namespace, ... }): throws ConfigurationError at wrap time when namespace is exactly 'ns' or 'nsapi'.
  • generateInteropKey(...): throws ConfigurationError for the same namespaces. The JSDoc @throws now lists this case.
  • validateInteropSegment(kind, value): when kind === 'namespace', it now also rejects reserved values. The check runs after the grammar check, and the error message contains "reserved" and names the conflicting '<value>:' prefix.
  • WrapOptions.interop JSDoc (types/cache.ts, shipped in .d.ts): documents the reservation and states that operation names are unaffected.

Scope of the reservation

  • It is an exact match against a private RESERVED_INTEROP_NAMESPACES set. Values such as nsx, nsfw, nsapi2 and nsapix remain valid namespaces.
  • It applies to namespaces only. ns and nsapi are still accepted as operation names.
  • INTEROP_SEGMENT_PATTERN is unchanged, because it also governs operation names.

Supporting changes

  • Protocol fixture: interop-mode.json 1.1.0 is re-vendored byte-for-byte from the protocol repo.
    • It adds vectors for both reserved namespaces and a scope case (namespace nsapix, operation nsapi).
    • The protocol test file header records provenance (PR link and sha256).
  • Tests:
    • Unit tests cover rejection of reserved namespaces and acceptance of the reserved values as operations.
    • Unit tests cover acceptance of near-miss namespaces.
    • A cache.wrap test asserts both reserved namespaces throw /reserved/.
    • An existing oversized-value test in cache.test.ts switches from namespace: 'ns' to 'blobs'.
  • .secrets.baseline: line numbers shift for the regenerated fixture, and one new vector hash entry is added.

Migration impact

Deployments that used ns or nsapi as an interop namespace on non-CachekitIO backends must rename the namespace. The rename makes every existing entry in that namespace a cache miss.


Summary

This PR hardens interop namespace validation so that the reserved namespaces ns and nsapi cannot be bypassed with non-string input. It also adds tests for invalidation logging and for the integrity of the vendored interop test-vector fixture.

Changes

validateInteropSegment(kind, value) (serialization/interop.ts)

  • Behavior change: the function now throws ConfigurationError when value is not a string, before the grammar and reservation checks run.
  • Reason: RegExp.test converts its argument to a string, but Set.has does not. An untyped input such as ['ns'] or new String('nsapi') could pass the grammar check, skip the reserved-namespace check, and produce an ns:-prefixed key.
  • JSDoc: the @throws contract now includes the non-string case.
  • generateInteropKey goes through this validation, so it now rejects the same inputs.

Tests

  • interop.test.ts: confirms that ['ns'] and new String('nsapi') are rejected by both validateInteropSegment and generateInteropKey.
  • cache.test.ts (LAB-4336): confirms that cache.invalidate('namespace') with no namespace logs [cachekit] invalidate("namespace") called with no namespace; nothing invalidated at the caller. It also confirms that a call with a namespace logs nothing.
  • interop-mode.protocol.test.ts:
    • Pins the vendored interop-mode.json fixture by its SHA-256 hash and by its vector counts: 34 key, 4 value and 11 error vectors.
    • Without this check, a truncated fixture would simply run fewer it.each cases and still pass.
    • The provenance comment now references upstream commit 965aeb01… and explains how to re-vendor the fixture.

Maintenance

  • .secrets.baseline line numbers and timestamp are updated to match the shifted test file.

Compatibility

Callers that pass non-string values as interop namespace or operation segments will now get a ConfigurationError. This is the breaking change marked with ! in the PR title.

Summary by CodeRabbit

  • Bug Fixes
    • Interop namespaces exactly matching ns or nsapi are now rejected, while these values remain valid as operation names. Non-string namespace segments are also rejected.
  • Tests
    • Added coverage for reserved namespace rules, invalid segment types and protocol fixture integrity.
  • Documentation
    • Clarified which namespace values are reserved and documented the related validation error.

An interop key {namespace}:{operation}:{args_hash} in namespace `ns` or
`nsapi` starts `ns:` / `nsapi:`, which the CachekitIO server parses as a
namespace-prefixed key: it is rejected or scoped to a namespace named after
the operation. interop/v1 1.1.0 reserves both names (exact-match,
namespace-only); re-vendor the vectors byte-for-byte from protocol.

BREAKING CHANGE: cache.wrap(fn, { interop, namespace: 'ns' | 'nsapi' }) and generateInteropKey now throw ConfigurationError. Such keys were already rejected or misrouted by CachekitIO, but worked on other backends.
@coderabbitai

coderabbitai Bot commented Sep 27, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: cachekit-io/cachekit-ts/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: a434a5e5-3c24-4ac4-b47b-d87d9080d3c7

📥 Commits

Reviewing files that changed from the base of the PR and between eed8a79 and a1fa153.

📒 Files selected for processing (8)
  • .secrets.baseline
  • packages/cachekit/src/cache.interop.test.ts
  • packages/cachekit/src/cache.test.ts
  • packages/cachekit/src/serialization/interop.test.ts
  • packages/cachekit/src/serialization/interop.ts
  • packages/cachekit/src/types/cache.ts
  • packages/cachekit/test/protocol/fixtures/interop-mode.json
  • packages/cachekit/test/protocol/interop-mode.protocol.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.


Walkthrough

Interop segment validation now rejects ns and nsapi as namespace values and rejects non-string segments. These values remain valid as operation names. Tests, documentation, protocol vectors, fixture integrity checks, and the secrets baseline record the updated rules and fixture.

Changes

Interop namespace reservations

Layer / File(s) Summary
Enforce reserved namespace values
packages/cachekit/src/serialization/interop.ts, packages/cachekit/src/types/cache.ts, packages/cachekit/src/serialization/interop.test.ts, packages/cachekit/src/cache.interop.test.ts, packages/cachekit/src/cache.test.ts
validateInteropSegment rejects non-string segments and exact namespace values ns and nsapi. Operation names and longer namespace values remain valid. Documentation and tests cover the validation rules.
Record reservation in protocol vectors
packages/cachekit/test/protocol/fixtures/interop-mode.json, packages/cachekit/test/protocol/interop-mode.protocol.test.ts, .secrets.baseline
The fixture advances to version 1.1.0 and adds reservation vectors. The protocol test pins the fixture digest and vector counts. Existing argument values are reformatted without value changes. The secrets baseline updates fixture line references and its generation timestamp.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix

Merge Risk: ⚪ Minimal · up to a1fa1

Interop now rejects exact ns and nsapi namespace values and non-string segments while preserving valid operation names and longer namespaces. The implementation and pinned protocol fixture agree, with no material merge risk evident.

Security Architecture Review

Security architecture risk: 🔵 Low · up to a1fa1

The change narrows which interop namespaces the SDK accepts and rejects invalid configurations before cache access. No introduced security weakness was established, but server-side behavior and the impact on existing users of the newly reserved names remain unverified.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The demonstrated control covers interop keys generated through this SDK, including cache.wrap. The evidence does not establish the server parser’s behavior, alternate producers’ reachability, or a tenant- or service-wide security effect.

Trust Boundaries and Controls

  • observed — The SDK checks configured interop segments at wrap time and again when generating a key; rejected namespaces fail before that key is used for a cache lookup.

Hardening Proposals

  • proposed — Verify the reserved-prefix behavior against the server parser and assess other key producers before relying on the SDK check as an end-to-end namespace boundary.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 5 functions across 6 files. (2 skipped: 2… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarises the main change: rejecting the reserved interop namespaces ns and nsapi. It is concise, specific, and includes the breaking-change marker and issue reference.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 5 functions across 6 files. (2 skipped: 2 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Sep 27, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@kodus-27b

kodus-27b Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Code Review Completed! 🔥

The code review was successfully completed based on your current configurations.

Kody Guide: Usage and Configuration
Interacting with Kody
  • Request a Review: Ask Kody to review your PR manually by adding a comment with the `@kody start-review` command at the root of your PR.

  • Provide Feedback: Help Kody learn and improve by reacting to its comments with a 👍 for helpful suggestions or a 👎 if improvements are needed.

Providing Context (Files & MCPs)

Add these hints in your PR description (or a comment) to unlock deeper checks:

  • Ticket / Acceptance Criteria: `Refs: ABC-123` (Linear/Jira/Asana/ClickUp/Trello) or a direct ticket link.
  • Bugfix Validation: a Sentry/Datadog/Bugsnag event link (or paste the stack trace/error message).
  • Endpoint Risk: mention the route (e.g., `POST /api/payments`) or controller/action name.
  • Attach a repo file as context: use an explicit marker like `@file:docs/guide.mdx#L10-L50` (replace with your real path).
  • API Contract Docs: include `@file:openapi.yaml` or `@file:swagger.json` when changing routes/schemas.
  • Definition of Done / Standards: include `@file:DOD.md` or `@file:CONTRIBUTING.md` if your repo has them.
  • Design System Source of Truth: include `@file:ui/index.ts` (replace with your DS entrypoint path).
  • Feature Flags: include the flag key/name and `@file:flags.ts` / `@file:config.json` (and optionally the PostHog flag name).
  • Edge/CDN Rules: link the Cloudflare rule/zone or describe the intended redirect/header behavior.
  • Attach an MCP tool output: use `@mcp<provider|tool>` (replace with an installed MCP provider + tool, e.g., `@mcp<sentry|events.search>`).
Current Kody Configuration
Review Options

The following review options are enabled or disabled:

Options Enabled
Bug ✅
Performance ✅
Security ✅
Business Logic ✅

Access your configuration settings here.

kodus-27b[bot]
kodus-27b Bot previously approved these changes Sep 28, 2026
…ure (LAB-5876)

RegExp.test string-coerces its argument but Set.has does not, so an untyped
['ns'] or new String('nsapi') passed the segment grammar, skipped the
reserved-namespace check, and generateInteropKey minted an `ns:` key.
validateInteropSegment now rejects a non-string first, at both wrap time and
call time.

The interop protocol suite now pins the fixture sha256 and its key/value/error
vector counts: it.each over a truncated fixture silently runs fewer cases.
Provenance points at the protocol squash commit.
@27Bslash6

Copy link
Copy Markdown
Contributor Author

Merged main (eed8a79) into this branch in f2fbf53. The only conflict was generated_at in .secrets.baseline, and it was regenerated with detect-secrets. Also pushed a1fa153. With it, validateInteropSegment rejects a non-string segment before the reserved-namespace check, and the interop protocol suite pins the fixture sha256 and vector counts. CI will re-run.

Comment thread packages/cachekit/test/protocol/interop-mode.protocol.test.ts
@kodus-27b

kodus-27b Bot commented Sep 28, 2026

Copy link
Copy Markdown

Code Review Completed! 🔥

The code review was successfully completed based on your current configurations.

Kody Guide: Usage and Configuration
Interacting with Kody
  • Request a Review: Ask Kody to review your PR manually by adding a comment with the `@kody start-review` command at the root of your PR.

  • Provide Feedback: Help Kody learn and improve by reacting to its comments with a 👍 for helpful suggestions or a 👎 if improvements are needed.

Providing Context (Files & MCPs)

Add these hints in your PR description (or a comment) to unlock deeper checks:

  • Ticket / Acceptance Criteria: `Refs: ABC-123` (Linear/Jira/Asana/ClickUp/Trello) or a direct ticket link.
  • Bugfix Validation: a Sentry/Datadog/Bugsnag event link (or paste the stack trace/error message).
  • Endpoint Risk: mention the route (e.g., `POST /api/payments`) or controller/action name.
  • Attach a repo file as context: use an explicit marker like `@file:docs/guide.mdx#L10-L50` (replace with your real path).
  • API Contract Docs: include `@file:openapi.yaml` or `@file:swagger.json` when changing routes/schemas.
  • Definition of Done / Standards: include `@file:DOD.md` or `@file:CONTRIBUTING.md` if your repo has them.
  • Design System Source of Truth: include `@file:ui/index.ts` (replace with your DS entrypoint path).
  • Feature Flags: include the flag key/name and `@file:flags.ts` / `@file:config.json` (and optionally the PostHog flag name).
  • Edge/CDN Rules: link the Cloudflare rule/zone or describe the intended redirect/header behavior.
  • Attach an MCP tool output: use `@mcp<provider|tool>` (replace with an installed MCP provider + tool, e.g., `@mcp<sentry|events.search>`).
Current Kody Configuration
Review Options

The following review options are enabled or disabled:

Options Enabled
Bug ✅
Performance ✅
Security ✅
Business Logic ✅

Access your configuration settings here.

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