Skip to content

Feat: detect account-scoped storage written without an owner discriminator - #214

Merged
ivory-code merged 1 commit into
mainfrom
feat/account-scoped-storage-risk
Aug 22, 2026
Merged

Feat: detect account-scoped storage written without an owner discriminator#214
ivory-code merged 1 commit into
mainfrom
feat/account-scoped-storage-risk

Conversation

@ivory-code

Copy link
Copy Markdown
Contributor

Summary

Client-side persistence written for account-scoped data without an owner discriminator survives account switches: the second account inherits the first account's drafts, acceptance flags, or cached entitlements. This class of defect passes single-account QA and only surfaces on account switch or session expiry, so per-file review rarely catches it. QAMap now promotes such writes to risk evidence and plans the account-switch scenario.

Behavioral Contract

When a diff adds a localStorage/sessionStorage write in a product module whose full text references an authenticated user (session hook, user id, auth token), and neither the key expression, the resolved key literal, nor the surrounding write statement carries an owner discriminator, QAMap emits diff risk evidence (account-scoped-storage:<storage>) anchored to the write line and adds the QA scenario "Account switch inherits storage without an owner discriminator" (account switch, session expiry then re-login, second tab).

Writes stay unflagged when the key or nearby payload carries an owner discriminator (ownerId, userId, template interpolation over user/account identifiers), when the key names a device-scoped value (theme, locale, dismissed banner), or when the module has no authenticated-user reference. Detection reads the changed file's head text through the same path as runtime-activation tracing; unified-0 diffs alone cannot see the surrounding auth context.

Evidence

Closes #212.

  • Minimized fixture: test/benchmarks/web-account-scoped-storage — a terms page using a session hook gains localStorage.setItem("terms:accepted-at:v1", ...) with no owner scope; contract pins the intent, reached file, and the new scenario title.
  • Focused regression: flagged write resolves the key through a module constant and anchors startLine to the added write.
  • Second unrelated positive: a checkout draft page persisting sessionStorage under a useUser hook (relaxed value expression — JSON.stringify(...) payloads match).
  • Negative controls: owner-scoped payload ({ ownerId: userId, ... }), device-scoped app:theme key inside an authenticated module, and an anonymous visit counter with no auth reference — none flagged, scenario absent.

Checks

  • Focused regression test
  • pnpm test
  • pnpm bench:ci for inference, routing, trace, or output
  • pnpm bench:execution for E2E compiler or execution fixtures
  • pnpm scan for scanner, security, or repository policy
  • pnpm plugin:check and pnpm plugin:smoke for plugin changes
  • Documentation links and commands verified

Public OSS Check

  • No private repository, source, path, customer data, credential, or internal smoke output is included.
  • Shared inference has unrelated positive and negative coverage, or this is not applicable.
  • User-facing commands and claims match actual behavior.

Review Notes

pnpm scan (0 findings), pnpm bench:context (10/10), pnpm plugin:check, and pnpm plugin:smoke also pass locally; they are marked N/A above because this change touches none of those surfaces. The detector is intentionally conservative: string-literal and single-identifier keys only (identifier keys resolve through a same-module literal assignment), module-level auth signals limited to explicit hooks/identifiers/tokens, and the existing generic "Scoped persisted context isolation and cleanup" scenario still fires alongside the new one — the two answer different questions (cleanup semantics vs cross-account inheritance). Dynamic keys built outside the module and auth context imported behind aliases remain review work.

@ivory-code ivory-code added type: feat Feature work that adds product or CLI capability area: scanner Static scanning rules and repository policy checks labels Aug 22, 2026
@ivory-code ivory-code self-assigned this Aug 22, 2026
@ivory-code
ivory-code merged commit c38813d into main Aug 22, 2026
2 checks passed
@ivory-code
ivory-code deleted the feat/account-scoped-storage-risk branch August 22, 2026 08:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: scanner Static scanning rules and repository policy checks type: feat Feature work that adds product or CLI capability

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feat: detect account-scoped client storage written without an owner discriminator

2 participants