LearnNewsExamplesServices
Frontmatter
id10772
titleUnit test coverage for proxy-identity injection in TransportService
stateClosed
labels
enhancementaitestingarchitecture
assigneesneo-gemini-3-1-pro
createdAtMay 5, 2026, 8:26 PM
updatedAtMay 6, 2026, 10:10 AM
githubUrlhttps://github.com/neomjs/neo/issues/10772
authorneo-opus-4-7
commentsCount0
parentIssue10721
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtMay 6, 2026, 10:10 AM

Unit test coverage for proxy-identity injection in TransportService

Closedenhancementaitestingarchitecture
neo-opus-4-7
neo-opus-4-7 commented on May 5, 2026, 8:26 PM

Context

Follow-up to PR #10768 (feat(auth): add proxy identity injection via X-PREFERRED-USERNAME (#10727)). Surfaced during cross-family review by @neo-opus-4-7 (review comment) — the trust-boundary code in TransportService.mjs ships without unit-test coverage. Substantively correct (verified end-to-end in review), but security-relevant trust-boundary code without tests is a structural risk: regressions to OIDC precedence or source-tag propagation would be caught only at integration time.

The Problem

The proxy-identity injection logic added in PR #10768 (TransportService.mjs:142-170) handles three distinct branches:

  1. OIDC-active path: req.auth populated → proxy header IGNORED, OIDC takes precedence
  2. Proxy-active path: req.auth undefined + trustProxyIdentity=true + header present → identity injected with source: 'proxy-header'
  3. Gate-disabled path: header present + trustProxyIdentity=false → no identity injected, single-tenant fallthrough

Each branch has trust-boundary semantics. Without tests, future refactors could silently flip precedence, drop the source-tag, or leak proxy-header values into OIDC-active deployments.

The Architectural Reality

  • ai/mcp/server/shared/services/TransportService.mjs:142-170 — the branch logic introduced by PR #10768
  • aiConfig.auth.trustProxyIdentity — the gate-config flag (default false)
  • Header-name conventions: x-preferred-username (canonical) + x-auth-request-preferred-username (oauth2-proxy variant)
  • Server.mjs#buildRequestContext:259 — downstream consumer that propagates source: reqAuth.source || 'oidc'

Existing test infrastructure: test/playwright/unit/ai/mcp/server/memory-core/services/HealthService.spec.mjs provides the pattern (module-scope pure-function unit tests with test.beforeAll import). TransportService is testable similarly if the branch logic is extractable into a pure function, OR via mock req shape with the existing class instance.

The Fix

Add a unit-test spec at test/playwright/unit/ai/mcp/server/shared/services/TransportService.spec.mjs covering the three branches:

  1. OIDC precedence test: req.auth populated + trustProxyIdentity=true + proxy header set → identity comes from req.auth, NOT proxy header. source is whatever req.auth.source was (typically 'oidc').
  2. Proxy-active test: req.auth undefined + trustProxyIdentity=true + x-preferred-username header set → identity injected with userId matching header, source: 'proxy-header'.
  3. Proxy oauth2-proxy variant test: same as #2 but with x-auth-request-preferred-username header instead — both header conventions yield the same result.
  4. Gate-disabled test: req.auth undefined + trustProxyIdentity=false + header present → no identity injected, requestContext is single-tenant fallthrough shape.
  5. Both-headers test: both x-preferred-username AND x-auth-request-preferred-username present → first non-empty wins (canonical name preferred, currently).
  6. Header-spoof test (load-bearing security): req.auth undefined + trustProxyIdentity=false + adversarial proxy headers → identity NOT injected. Pins the gate behavior so a future refactor can't accidentally enable header reading without the explicit opt-in flag.

Acceptance Criteria

  • Unit-test spec file at test/playwright/unit/ai/mcp/server/shared/services/TransportService.spec.mjs (or extend an existing TransportService spec if one exists post-PR)
  • All 6 test cases above pass against the current TransportService.mjs:142-170 branch logic
  • If the branch logic isn't pure-function-testable in its current shape, refactor to extract a resolveAuthContext(req, cfg) pure function (similar to buildIdentityBlock/buildTopologyBlock/buildEmbeddingProviderBlock precedents); delegate from TransportService.setup
  • Tests run via existing npx playwright test test/playwright/unit/... infrastructure
  • No mock-bypassed gates (per feedback_makesafe_strips_internal_flags discipline — direct module imports for trust-boundary tests, not test-harness wrappers)

Out of Scope

  • Integration tests against a live oauth2-proxy deployment (operator-territory L3)
  • Per-request audit-log assertions (separate observability concern)
  • Header-name configurability beyond the two currently-supported variants

Related

  • Adjacent shipped: PR #10768 (the code under test)
  • Adjacent doc: PR #10769 (operator-doc + threat-model statement)
  • Parent epic: #10721 (Shared deployment MVP completeness gaps)
  • Pattern precedents: HealthService.spec.mjs (module-scope pure-function unit testing pattern)

Origin Session ID: 23b9cbcd-4938-4a46-b21a-0d48dd12e7e7

Retrieval Hint: query_raw_memories(query="TransportService proxy-identity unit test trust-boundary OIDC precedence header spoof gate 10727 10768")

tobiu referenced in commit 6664a9a - "test(mcp): add proxy-identity unit tests (#10772) (#10797) on May 6, 2026, 10:10 AM
tobiu closed this issue on May 6, 2026, 10:10 AM