# S3 tenant-isolation — band-1 recall MEASURED (2026-06-17)

audience: AI coding agents first. Measured log. Tags [MEASURED] vs [DESIGN]. Do not prettify.

## BLUF

S3 (tenant isolation) was a band-1 BLIND SPOT (0 cells). Now **CLOSED: 3 git-canonical cells, band-1 recall 3/3
cells CAUGHT, RED 3/3 rolls each, clean GREEN.** This is the project's first class measured as a RATE (n=3), not
an n=1 point estimate. Headline finding: the band-1 generalist prompt is payments-domain-FRAMED yet its IDOR /
tenant-isolation passes (Pass 7, Pass 8) generalize to NON-payments files — every cell here is calendar / session /
reporting code, zero payments surface. The advisor-selected fork C ("close a named blind spot beats a band-2 rate
for a rare shape") is executed for S3.

## Cells [MEASURED] — all `domains/security/corpus/S3-*`

Each cell = one planted canonical (the real pre-fix IDOR) in an otherwise-correct, tenant-scoped file; siblings are
the discriminator pressure. RED = vuln.ts (canonical present), GREEN = safe.ts (canonical fixed). k=3 rolls,
`gate.py --config-dir /tmp/sg_cfg` (blind clean config), v2@sonnet/MED band-1 leg.

| cell | fix SHA | canonical | IDOR kind | RED (vuln) | GREEN (safe) |
|---|---|---|---|---|---|
| S3-calendar-tenant-isolation-idor | 55fcaf4 | `getSchedulingConnectionById` | read (SELECT by id, no tenant) | **3/3 rolls, [critical]** | canonical 0/3 — not flagged |
| S3-session-tenant-isolation-idor | 7ec19d6 | `touchSession` | write (UPDATE by id, no tenant) | **3/3 rolls (2 crit + 1 high)** | canonical 0/3 — only low UX no-op nits |
| S3-report-schedule-tenant-isolation-idor | 5715f6d | `getReportScheduleById` | read, **self-documented gap** | **3/3 rolls, [high] IDOR** | canonical 0/3 — only "dead code" note |

## Why these cells are honest, not gimmes [DESIGN]

Discriminator pressure was built in so a trigger-happy detector would FALSE-POSITIVE on GREEN and on safe-by-design
siblings — it did not:
- **Same-shape correct sibling.** Each file holds a function of the SAME shape as the canonical but correctly
  tenant-scoped (calendar `getSchedulingConnection`; sessions `revokeSession` UPDATE-by-id with tenant+user;
  report-sched `getReportSchedule`). The detector flagged the canonical and left the sibling alone.
- **Legit-unscoped traps.** sessions `getSessionByTokenHash` / `revokeSessionByTokenHash` lack tenant scope *by
  design* (the token hash IS the bearer credential); report-sched `listDueSchedules` is a cross-tenant cron sweep.
  None were mis-flagged as IDOR — the detector correctly read these as intentional.
- **Self-rationalizing comment.** The report-sched canonical ships a doc comment "Get a single schedule by id
  without tenant scoping (for queue consumers)". The detector saw THROUGH the justification and flagged it IDOR
  3/3. This is the strongest single result — a real-world rationalization did not buy the bug a pass.

## Recall-counting [MEASURED — honesty]

Per-cell recall = canonical flagged at high/critical by ≥1 roll. All 3 cells: CAUGHT, and in fact all 3 rolls
flagged each canonical (the k-roll union sometimes splits one canonical into multiple titles — e.g. report-sched's
three IDOR-titled findings are the same bug from three rolls). GREEN discrimination = the canonical symbol draws
NO IDOR finding once fixed; residual GREEN findings are unrelated genuine observations (e.g. report-sched safe:
"getReportSchedule and getReportScheduleById are now functionally identical dead code" — a correct consequence of
the fix, not a false IDOR).

## Implication [DESIGN]

- S3 single-file Shape-A IDOR needs **no new band** and **no prompt change** — band-1 already covers it. The
  band-2 work (task #36) is for Shape-B (imported insecure default), a measured-rare shape; S3 is not in its scope.
- The payments-framing of `security-generalist.prompt.txt` did NOT block non-payments IDOR detection. This narrows
  the prompt-generalization task (#16/#22): the *framing* is payments, but the IDOR/tenant/injection passes
  transfer. Generalization should be measured per-class, not assumed blocked by the title. [hypothesis: holds for
  S3 IDOR; other non-payments classes — e.g. S2 RBAC — still need their own measurement.]

## Next [DESIGN]

S2 (RBAC) is the remaining named band-1 BLIND SPOT. Per advisor: check `git log` for real RBAC fix commits before
assuming — do NOT fabricate cells. If ground-truth exists, measure band-1 the same way (3 cells, RED/GREEN, k=3).

## Provenance

Source repo `~/Projects/zync.is`; all 3 fix SHAs raw-git verified. Cells reconstructed with `command git --no-pager
show` (RTK truncates routed git show), secret-scrubbed (query helpers — no literal secrets), faithful to the real
pre/post-fix function shapes. No fabricated cells.
