Repair a real proxy/cache/application boundary
Node.js 22 or newer. Standard library only. No npm installation or external target.

Reference solution: not included here, so the exercise does not hand you the answer.
Attempt the repair first. Then download proxy-cache-boundaries-solution.zip from the project card
on https://softwaresecurity.com/labs/#project-proxy-cache-boundaries and extract it into this folder;
it adds reference.mjs without changing your files, and the reference commands below
need it.

Goal and outcome
Explain exactly when a shared cache is allowed to reuse a response. Repair four
policy functions while exercising real Node HTTP servers, actual request bodies,
and raw socket framing. Produce a cache contract and a passing regression suite.
This project binds two ephemeral servers ONLY to 127.0.0.1 and shuts them down
when each test finishes. It never probes a remote site.

Prerequisites and time
Read HTTP methods/statuses, request/response headers, and basic async JavaScript.
Allow 45–60 minutes. Tests need permission to bind localhost ports. The synthetic
Bearer alice / Bearer bob credentials are fixtures, not authentication examples.

Request path
client -> proxy [validate headers, derive caller, cache decision]
       -> origin [trusted proxy marker, caller-specific result]
       <- response [cache-control, vary, set-cookie]
The proxy strips untrusted identity headers and creates its own verified subject.
An in-memory random proxy marker prevents direct origin requests being trusted.
A production boundary needs authenticated transport and a real identity service.

1. Observe a disclosure, not just a red assertion
   node observe.mjs
   Expected starter output:
   alice: MISS {"subject":"alice","tenant":"acme","invoice":120}
   bob: HIT {"subject":"alice","tenant":"acme","invoice":120}
   Bob supplied different credentials, but the cache returned Alice's body before
   the origin could authorize Bob. Adding a check to the origin cannot repair a
   response that bypasses it.
   node --test exercise.test.mjs
   The starter has 4 passing and 8 failing tests. Identify the first place the
   wrong representation can escape. Read servers.mjs alongside the failing tests.

2. Write the contract
   /catalog?locale=en is public for 30 seconds and distinct from locale=fr.
   /private is caller-specific and says private, no-store.
   /session has cookie state and Set-Cookie; never share it.
   /nonce is no-store; a second request must reach the origin.
   /variant varies on Accept. This small cache does not implement Vary, so bypass.
   /echo accepts POST; its result must not be reused for another body or method.
   Any duplicated singleton identity/framing field must be rejected, not guessed.

3. Repair exercise.mjs
   cacheKey: preserve method and complete request target, including query.
   mayReadCache: allow GET only when Authorization and Cookie are absent.
   mayStore: also require status 200, the exact origin public-cache contract,
   no Set-Cookie and no unsupported Vary. A miss is not permission to store.
   validHeaders: count names case-insensitively in rawHeaders. Node may collapse
   or discard duplicates in headers, so that object cannot reveal ambiguity.
   The parser already rejects conflicting framing. Do not enable an insecure
   parser to make malformed requests reach the application.

4. Verify at every boundary
   node --test exercise.test.mjs
   Expected repaired/reference result: 12 tests pass, zero failures.
   EXERCISE_MODULE=reference node --test exercise.test.mjs
   Windows PowerShell: $env:EXERCISE_MODULE='reference'; node --test exercise.test.mjs
   Clear that variable before testing your repair.
   node observe.mjs --reference
   Expected: Alice and Bob each get MISS and their own acme/beta invoice.
   The public-cache test must still produce HIT: disabling all caching avoids the
   disclosure but does not satisfy the explicit functional requirement.

Worked explanation
A key containing just pathname aliases English and French public responses.
Including an identity in a key could partition private responses, but still
ignores no-store and revocation semantics. This lab's simpler contract bypasses
shared caching for authenticated/cookie requests entirely. Storage and lookup
both enforce that boundary. Unsupported Vary and Set-Cookie make an otherwise
200 response ineligible. Request framing is a separate boundary: raw socket
tests ensure ambiguous bodies never reach the origin, evidenced by zero hits.
Those tests are regression checks for this parser chain, not a universal
request-smuggling detector or a recipe to test unauthorised servers.

Completion rubric
All 12 tests pass and public caching still works. Explain why lookup and storage
both need policy, why normalized headers hide duplicate inputs, and why query
strings matter even on public data. Keep a route table with cache eligibility,
key dimensions, freshness and invalidation owner. Add a new public route with a
new Vary dimension: choose to implement it correctly or explicitly bypass it.

Troubleshooting
EPERM/EACCES on listen means localhost servers are blocked by your execution
sandbox. Run in an ordinary local terminal. No privileged port is used. Timeouts
should fail within five seconds; ensure local security software allows loopback.
A failing cache-hit test can mean you disabled all storage. A duplicate-header
test that reaches the origin means you inspected normalized headers too late.
No manual server cleanup is required; every test closes both servers in finally.

Limits
This is an HTTP/1.1 loopback integration project with a bounded 30-second cache.
It is deliberately not a complete RFC 9111 cache, TLS terminator, CDN, HTTP/2
translator, distributed session service or production proxy. The reference uses
a deliberately narrow origin Cache-Control contract; use maintained HTTP cache
implementations for production and test the actual intermediary chain. Buffers
are capped at 4 KiB and targets are fixed to the local origin. No external URLs
are followed. JWT verification, tenant data storage, eviction under load and
revocation synchronization are separate exercises.

Primary references
https://www.rfc-editor.org/rfc/rfc9111.html
https://www.rfc-editor.org/rfc/rfc9112.html
https://nodejs.org/api/http.html

Dev container
.devcontainer/devcontainer.json opens this folder in VS Code Dev Containers or
GitHub Codespaces using the Dev Containers Node.js 24 image, pinned by digest.
Run the commands above in its terminal.
