Files
OMT-SM/docs/THREAT_MODEL.md
T

9.1 KiB
Raw Blame History

Threat Model — OMT / Recharge Cell Shop

Purpose. Document, before any code is written, every realistic way an employee (or a colluding pair) can steal money or goods from the shop, and the specific control the system must enforce to block each path. Every later migration, RLS policy, trigger, and UI rule must trace back to a row in this table.

This file is append-only in spirit: when a new theft vector is discovered, add a row; do not delete history.

Actors

Actor Description
cashier Operates a till during a shift. Highest fraud-risk role.
manager Approves voids, refunds, overrides. Can collude with cashier.
owner Read-everything. Surprise inspections. Sets prices/fees.
auditor Read-only third party (accountant).
customer May be an accomplice (fake refund, fake cancellation).

Trust boundaries

  1. Browser/POS ↔ Supabase: client is hostile. Never trust client-supplied prices, fees, FX rates, timestamps, user IDs, or shift IDs.
  2. App role ↔ Postgres: even the service role must not be able to DELETE from the ledger or rewrite hashes. Enforce with table grants + triggers.
  3. Shop ↔ Provider (OMT / Alfa / touch / Bank): the provider's statement is the source of truth for reconciliation. Any local row not present on the provider statement is suspect.

Theft vectors and required controls

# Vector Control (must exist before go-live) Enforced in
1 Pocket cash, never enter the transaction Mandatory printed receipt, sequential reference_no per shop, customer SMS with reference, gap-detection report DB + server
2 Enter transaction, then edit / delete it after customer leaves Append-only ledger; UPDATE/DELETE revoked; void requires reason + manager PIN; row hash chain DB triggers
3 Recharge own/friend's number flagged as "test" No "test" flag exists; every recharge debits stock or e-float Schema
4 Sell a scratch card, keep cash, claim "lost card" Voucher serial scanned on intake and on sale; loss requires manager approval; variance assigned to cashier DB + UI
5 Use shop OMT terminal to send to themselves at zero / reduced fee Fee schedule server-side and immutable per cashier; OMT statement reconciliation; alert on cashier as sender or beneficiary DB + recon job
6 Manipulate FX rate (e.g. "I gave 89,000 LBP/USD instead of 90,000") fx_rates populated by scheduled job; cashier selects, never types; rate stamped on each txn DB + cron
7 Skim cash from drawer, blame "shortfall" Blind closing count (declared before expected revealed); per-cashier variance trend; chronic shortage alert UI + report
8 Collect customer cash, "deposit later", never deposit Forced shift close before leaving; aging-outstanding alert > N hours; bank-deposit reconciliation DB + recon
9 Share login with a colleague Per-user PIN re-prompt, device binding, session timeout, no shared accounts Auth
10 Print fake receipts on a second printer Receipt carries server-signed QR (JWT of txn_id); spot-scan by owner returns DB row or "FAKE" Server
11 Refund/void to themselves Void/refund requires manager PIN on same device; cashier cannot self-approve; daily void report per cashier DB + UI
12 Sell recharge below price ("friend discount") Price list server-controlled; cashier UI has no price field; override only by manager and logged DB + UI
13 Take goods (phones/accessories) without sale Per-shift inventory count; CCTV ↔ txn time sync; variance report UI + ops
14 Tamper with the closing count Declared count entered first and locked; expected revealed only after; both stored DB
15 Accomplice customer "cancels" after cash handed Cancellations require manager + reason + photo of voided receipt; CCTV cross-check UI + ops
16 Structure large transfers under multiple fake walk-in identities KYC threshold per (customer, day) and (beneficiary, week); customer record mandatory ≥ threshold DB + AML report
17 Off-hours transaction when nobody is watching Shift hours per shop; after-hours flag + alert DB + alert
18 Managercashier collusion to mass-void real sales Void rate per (cashier, manager) pair trended; owner-only weekly review; voids count against shift variance Report
19 Replay an old OMT receipt to a new customer external_ref unique per provider; duplicate detection on insert DB constraint
20 Cashier opens a second, undeclared till on the same device One open shift per till_id; device fingerprint pinned to till DB + auth
21 Cashier marks recharge "failed at provider" and keeps cash Provider e-recharge response stored as external_ref and reconciled; "failed" requires provider failure id DB + recon
22 Cashier "exchanges currency" at a worse rate than recorded, pocketing the spread FX swap is a typed cash_movement with both legs at the system rate; deviation requires manager override DB
23 Cashier deletes their browser data to "lose" pending offline transactions Offline queue persisted with server-issued idempotency key; missing key sequences flagged on reconnect Client + server
24 Insider (developer/DBA) silently edits the database Hash chain on ledger; daily hash anchor exported off-site; restricted DB roles; audit of all DDL and privileged SQL DB + ops
25 Backdated transaction to fit a doctored shift count occurred_at server-side now(); cashier cannot set; backdate only by owner role with reason DB

Non-negotiables (no go-live without these)

  1. RLS on every table; no table is publicly readable or writable.
  2. INSERT-only ledger with row hash chain; DELETE revoked from every role.
  3. All money writes go through SECURITY DEFINER Postgres functions, not raw table writes.
  4. Server-controlled prices, fees, and FX rates. No cashier-typed money rules.
  5. Blind cash close per shift, with declared-vs-expected variance stored.
  6. External reconciliation (OMT, Alfa, touch, bank) before any month-close.
  7. Per-user account, device-bound, with PIN re-prompt for sensitive ops.
  8. Receipt with signed QR linking back to the ledger row.

Review cadence

  • Every new feature PR must reference at least one row above (or add one).
  • Quarterly walk-through: pick 5 random rows, demonstrate the control still works on a staging environment.