OCC-SEC-001 — FLS Verification (F1–F9)

Temporary harness for the approved non-admin test identity only. Verdicts use the authoritative User record (service-role read via the gated function), not HTTP status or auth.me() alone.

Step 1 — Verify Test Account (authoritative baseline)

Runs a server-side identity check and reads the authoritative User record (service role). Tests stay disabled until it passes.

Step 1b — T13 target identity (second disposable test user)

T13 tests a cross-user write and requires a second genuine, disposable User record as the target — not an admin, builder, or the attacker account. User records cannot be created via SDK; the target must be invited and complete OTP. Until the target is provisioned and its id whitelisted in occ-sec-001-gate, T13a/T13b stay disabled. T7/T8/T9 need no target.

T13 not ready — second target identity required.

To enable T13, the product owner must: (1) supply a second disposable email; (2) approve an invite via invite-org-user into the home test org (role user); (3) complete OTP from that inbox; (4) paste the resulting User id above and ask to whitelist it in the gate. T13a/T13b remain disabled until then. T7/T8/T9 are ready now.

Step 2 — Run F1–F9 (mutation attempts)
F1

auth.updateMe({ organization_id: canary })

attempted: {"organization_id":"occ-sec-test-canary-819639"}
F2

auth.updateMe({ app_role: "organisation_admin" })

attempted: {"app_role":"organisation_admin"}
F3

auth.updateMe({ app_role: "occupio_super_admin" })

attempted: {"app_role":"occupio_super_admin"}
F4

auth.updateMe({ data: { organization_id: canary } })

attempted: {"data":{"organization_id":"occ-sec-test-canary-819639"}}
F5

auth.updateMe({ data: { app_role: "organisation_admin" } })

attempted: {"data":{"app_role":"organisation_admin"}}
F6

auth.updateMe({ role: "admin" })

attempted: {"role":"admin"}
F7

User.update(ownId, { organization_id: canary })

attempted: {"organization_id":"occ-sec-test-canary-819639"}
F8

User.update(ownId, { app_role: organisation_admin })

attempted: {"app_role":"organisation_admin"}
F9

User.update(ownId, { data: { organization_id: canary } })

attempted: {"data":{"organization_id":"occ-sec-test-canary-819639"}}
T7

auth.updateMe({ app_role: "master_admin" }) — legacy alias

attempted: {"app_role":"master_admin"}
T8

auth.updateMe({ app_role: "service_manager" }) — legacy alias

attempted: {"app_role":"service_manager"}
T9

auth.updateMe({ app_role: "tenant" }) — resident downgrade alias

attempted: {"app_role":"tenant"}
T13a

User.update(targetId, { full_name }) — cross-user profile write

attempted: {"full_name":"OCC-SEC-001 T13a probe"}
T13b

User.update(targetId, { organization_id: canary }) — cross-user protected write

attempted: {"organization_id":"occ-sec-test-canary-819639"}
No tokens are included in the exported evidence.
Step 3 — Fresh login confirmation (after F1–F9 all PASS)

Sign out of the test account and sign back in (complete OTP). Then click below to re-verify the authoritative record, cross-tenant read and privileged call. PASS only if the account is still in its home org, cannot read canary data and cannot perform privileged canary operations.

Admin-controlled recovery procedure (no self-service restore)

If any test mutates the test user's authority and it persists (authoritative record changes), a platform admin (not the test user) restores the identity:

// Run as platform admin (separate authorised session), NOT from this page:
await base44.asServiceRole.entities.User.update(
  "6ace88d973495fe4f07894a8",
  { organization_id: "occ-sec-test-home-819639", app_role: null }
);

This page intentionally exposes no restore button to the test user.

FLS rollback procedure (admin)

The FLS write rules on User.organization_id / User.app_role are the load-bearing fix. To roll back (emergency only):

// Edit base44/entities/User.jsonc — remove both "rls": { "write": ... } blocks
// from organization_id and app_role. Redeploy. No data changes were made
// (FLS is a policy, not data), so rollback is clean and instant.
Removal checklist (after verification is complete)
  1. Delete src/pages/OccSec001TestRunner.jsx.
  2. Delete the backend gate base44/functions/occ-sec-001-gate/.
  3. Remove the lazy import + /occ-sec-001 route from src/App.jsx.
  4. Republish the app.

The FLS rules + manage-org-user remain as the permanent remediation. Do not remove them.