Skip to content

fix(isolation): scope the legacy code fallback and sketch writes to the identity - #161

Merged
ttbombadil merged 1 commit into
mainfrom
fix/isolate-legacy-global-state
Oct 3, 2026
Merged

ttbombadil merged 1 commit into
mainfrom
fix/isolate-legacy-global-state

Conversation

@ttbombadil

Copy link
Copy Markdown
Collaborator

Purpose

R2 of the refactoring series (docs/UNOSIM_REFACTORING_OPL.md), audit findings S2 and S3.

  • S2 (confirmed): start_simulation without code ran the code that any user compiled last (lastCompiledCode was one global variable).
  • S3 (confirmed): POST/PUT/DELETE /api/sketches acted on one global store; the client opens sketches[0] as everyone's start sketch, so any user could replace or delete it.

Why "secure" instead of "remove"

  • The code declares the code-less start a compatibility path until the next WebSocket protocol major, and docs/ARCHITECTURE.md requires a new major version for incompatible REST changes. A routes test calls the sketch CRUD the public API.
  • Removing either would break a declared contract, so both are scoped to the authenticated identity instead. Shapes and status codes are unchanged.

Change

  • LastCompiledCodeStore (bounded LRU, config.compilation.lastCompiledCodeMaxSubjects = 1000): /api/compile records per subject; start_simulation without code falls back only to the same subject's code, otherwise the existing "No compiled code available" path.
  • OwnedSketchStore over MemStorage: created sketches belong to their subject; the seeded default sketch stays visible to all and read-only; foreign and unknown ids both answer 404. The routes moved unchanged into server/routes/sketches.routes.ts.

Tests

  • RED → GREEN: simulation-last-compiled-code.test.ts (another subject's code is never run; the same subject keeps the fallback), last-compiled-code-store.test.ts, sketches.routes.test.ts (seed read-only; foreign GET/PUT/DELETE 404; own CRUD works).
  • Two existing tests encoded the global behaviour and were adapted with unchanged expected values:
    • compiler.routes.test.ts: setLastCompiledCode now receives the fixture subject ("student-a") with the code.
    • routes-core.test.ts: the CRUD sequence reuses the local session cookie of its creating request, as a browser does. Before, every request implicitly got a new local identity.
  • New HTTP-listener tests are in the serialized unit-node-http group.
  • npm run check, check:docs, ESLint, unit 2703 passed, pre-push incl. Sonar quality gate PASSED.

🤖 Generated with Claude Code

…he identity

A code-less start_simulation ran whatever any user compiled last, and any user
could overwrite or delete the shared start sketch. Both now hold per subject;
the REST and WebSocket contracts keep their shape and status codes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ttbombadil
ttbombadil merged commit 18c7d89 into main Oct 3, 2026
5 checks passed
@ttbombadil
ttbombadil deleted the fix/isolate-legacy-global-state branch October 3, 2026 21:00
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