Skip to content

feat(wsi): v3 slide capabilities with encrypted sources - #44

Draft
raylim wants to merge 2 commits into
mainfrom
codex/wsi-deid-slide-key-v3
Draft

raylim wants to merge 2 commits into
mainfrom
codex/wsi-deid-slide-key-v3

Conversation

@raylim

@raylim raylim commented Oct 3, 2026 •

Copy link
Copy Markdown
Collaborator

Slide capabilities move to wsi_auth_version 3 so the browser never learns a real slide image_id or the slide/thumbnail object URI.

Deploy together with cBioPortal/cbioportal#12378, cBioPortal/cbioportal-core#193 and cBioPortal/cbioportal-frontend#5732 (all updated to the same contract). A v2 portal and this tile server are not compatible.

Changes

  • Token v3. The JWT carries slide_key (opaque 32-hex) and enc: AES-256-GCM over {image_id, tile_source, thumbnail_source}, key = HKDF-SHA256(WSI_AUTH_SECRET, info wsi-claim-enc-v3), AAD = slide_key. Plaintext image_id, tile_source, thumbnail_source and *_source_sha256 claims are rejected. No new secret: the key is derived from the existing shared secret, and previous-secret rotation still works.
  • Sources come only from the token. X-WSI-Source and ?source= now return 400 on /tiles and /thumbnails.
  • Decrypted claims are cached per token, consulted only after the signature/expiry checks pass; failures are never cached.
  • Rate-limit scope is study_id + slide_key. FileNotFoundError messages no longer include URIs.
  • De-id rules (app/deid.py, institution-neutral): SLIDE_KEY is required on WSI rows; IMAGE_ID(S) is forbidden in timeline rows; hex fingerprints and opaque keys are not mistaken for dates. Institution-specific identifier formats (e.g. accession numbers) are left to the data provider.
  • /health and /ready report auth contract 3; readiness expects wsi-serving-v5. cryptography is now a runtime dependency.

Testing

  • uv run pytest: 238 passed. Includes the shared contract test vector (derived key and exact enc, also verified in the Java backend), tamper and rebinding cases, v2 rejection and header/query source rejection.
  • The same runtime change (on the earlier integration branch) was verified end to end on a dev stack with the full MSK-IMPACT study: tiles and thumbnails render with v3 tokens, and hierarchy/access/JWT/clinical-event payloads contained no image ids, accessions, barcodes or object URIs.

🤖 Generated with Claude Code

- Accept only wsi_auth_version 3. Decrypt the `enc` claim (AES-256-GCM, key
  HKDF-SHA256(secret, "wsi-claim-enc-v3"), AAD = slide_key) and serve the
  tile/thumbnail source from it; X-WSI-Source and ?source= are rejected (400).
  Plaintext image_id/source/source-digest claims are rejected. Decrypted
  claims are cached per token after signature/expiry checks; failures are
  not cached. Previous-secret rotation still works.
- Rate-limit scope uses study_id + slide_key; error/log text never includes
  ids or URIs (FileNotFoundError messages no longer echo source URIs).
- de-id: reject accession numbers in WSI/timeline text columns; opaque
  part/block/specimen keys, image_id and artifact URIs skip only the compact
  YYYYMMDD heuristic; hex source_fingerprint is validated as a digest;
  SLIDE_KEY required on WSI rows; IMAGE_ID(S) forbidden in timeline rows.
- Health/readiness report auth contract 3; readiness expects wsi-serving-v5.
- cryptography is a runtime dependency.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Specimen accession formats are institution-specific, so the open-source
tile server no longer hard-codes one. Remove ACCESSION_PATTERN,
contains_accession and _assert_no_accession, and their uses in WSI and
timeline row validation and the safe-text scan. Data providers enforce
their own accession rules before publishing rows.

Unchanged: v3 token/encrypted-source auth, SLIDE_KEY requirement and
pattern, source_fingerprint digest handling, opaque-key/server-only date
exemptions, IMAGE_ID(S) rejection on timeline rows, date/MRN checks.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
raylim added a commit to knowledgesystems/knowledgesystems-k8s-deployment that referenced this pull request Oct 6, 2026
Pins beta blue and green, and the beta tile server, to the de-identified
resource-data WSI release beta-wsi-rd-20261006-1:

- portal: cBioPortal/cbioportal#12378 head b479612a95 (schema 3.6.0)
- frontend: cBioPortal/cbioportal-frontend#5732 head 045105b07, immutable
  Netlify deploy 6ac48117bcbba50008ca68db
- tile server: cBioPortal/cbioportal-tile-server#44 head 75dce4f488
  (wsi_auth_version 3: slide_key plus an encrypted source claim)

The tile server rejects a client-supplied X-WSI-Source, so the ingress hashes
on the bearer capability instead. The release validator and the routing smoke
test now expect wsi-serving-v5 and auth contract 3, and the smoke test checks
that a browser-supplied source is refused.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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