Skip to content

Threat model

This page enumerates the threats facing each surface of Lending Agent Oversight, the technical mitigations that address them, and the residual risk that remains. The decomposition follows STRIDE (spoofing, tampering, repudiation, information disclosure, denial of service, elevation of privilege), surface by surface, because the trust assumptions and the available mitigations differ between the principal-side workspace, the AR-user surface, the terminal-action paths (FCA notification, annual-review sign-off, AR off-boarding), and the audit log.

Three trust zones. The public marketing landing and the demo are in the untrusted zone (the open internet). The AR-user surface is semi-trusted; the AR is a regulated individual with named accountability, but the AR’s own machine is not part of the principal’s estate. The principal-side workspace (/principal/*) is trusted: behind email-and-password plus TOTP, with step-up authentication on terminal actions. The server boundary holds the Postgres database (with row-level security keyed on tenant_id), the audit chain, and the rate limiters.

Surface 1: principal-side workspace (/principal/*)

Section titled “Surface 1: principal-side workspace (/principal/*)”

The principal-compliance-officer and principal-admin work in this surface daily. It exposes the AR register, the breach triage queue, the file review workspace, the annual review packets, and the firm-settings panel.

Spoofing. An attacker in possession of a stolen session cookie can act as the principal-side user until the session expires. Mitigation: httpOnly Secure SameSite=Lax cookies, opaque session ids, sliding 12-hour expiry with a 7-day absolute cap, TOTP required at sign-in for both principal-admin and principal-compliance-officer, IP and user-agent recorded on the session row. Step-up authentication (re-enter password and TOTP) is required for every terminal action, so a stolen session cannot record an FCA notification, sign off an annual review, or off-board an AR without a second factor.

Tampering. Writes to AR records, breach reports, file reviews, and annual reviews are guarded by optimistic concurrency: every PATCH carries If-Match against the prior updatedAt, returning 412 with a merge dialog on conflict. The audit log itself is append-only at the storage layer (Postgres INSERT-only grants on the application role; UPDATE and DELETE revoked; trigger blocks TRUNCATE). Each AuditEvent includes prevHash, the SHA-256 of the prior event, so a covert edit anywhere in the chain detaches a hash sequence and is detected by the daily integrity-check job. Detail in tampering and replay.

Repudiation. Every write is attributed to actorUserId and actorRole, with ip, userAgent, and a server-assigned at timestamp. Terminal actions (FCA notification, annual-review sign-off, AR off-boarding) record an additional step-up authentication event, so a director cannot later deny that the second factor was presented at the moment of sign-off.

Information disclosure. Tenant isolation is enforced twice: first at the database, where a Postgres row-level security policy compares tenant_id to current_setting('app.tenant_id')::uuid on every row; second in the API handler, which sets the GUC at the start of each connection from the session cookie. A code path that omits the GUC fails closed (no rows returned). The RBAC matrix gates per-resource access inside the tenant; AR-user reads scoped to own AR via users.ar_id. The audit log is read-only at the API layer; export is logged.

Denial of service. Per-session token-bucket limits at the edge: 120 reads per minute on list endpoints, 240 reads per minute on AR-detail, 30 writes per minute on most write paths, 10 per minute on terminal actions. Detail in rate limiting.

Elevation of privilege. Role assignment is a principal-admin-only operation and is itself a write that records to the audit log with the prior and next role. There is no path from principal-compliance-officer to principal-admin without an explicit invitation, and there is no path from ar-user to any principal-side role on the same account; the AR-user must be invited as a separate user.

Residual risk. Account takeover at the IdP layer (the principal’s email provider, where TOTP enrolment lives). This is bounded by TOTP itself and by the step-up requirement on terminal actions; a complete takeover would still need to defeat the second factor a second time at the moment of FCA notification or annual-review sign-off.

The AR-user signs in to submit MI returns, file breach reports, view their own audit log, and challenge file-review findings. AR-user accounts are scoped to a single AR via users.ar_id.

Spoofing. Email + password by default; per-tenant flag forces TOTP for AR-users at principal-firm discretion. Same session-cookie format as the principal-side, same lockout policy (10 failed sign-ins per email per hour). The AR-user surface is the primary credential-stuffing target because AR-user accounts are more numerous and less likely to have TOTP without the per-tenant flag. The lockout plus per-IP rate limit is the defence; firms with a higher risk appetite turn the per-tenant TOTP flag on.

Tampering. AR-user writes are limited (own MI return submission, own breach report submission, own challenge on a completed file review). Each is rate-limited and audit-logged. AR-users cannot edit completed file reviews or breach reports filed by the principal; a Challenged affordance flips the file review back to in-progress and records the challenge but does not let the AR alter findings directly.

Repudiation. Every AR-user submission is attributed (actorUserId, actorRole = "ar-user", arId, IP, user-agent, server timestamp). The MI return state machine makes a submitted return immutable (submit is a terminal transition, with subsequent queried or accepted transitions writeable only by principal-side roles).

Information disclosure. AR-users read only their own AR. The Postgres RLS predicate is composed: tenant_id = $tenant AND (role IN ('principal-admin','principal-compliance-officer','fca-auditor') OR ar_id = $ar). The audit log endpoint, when called by an ar-user, returns events whose subjectId is the AR’s own ar_id or a child resource of it.

Denial of service. AR-user write paths are rate-limited tightly because human pace is much slower than automation: 10 MI submissions per minute, 30 breach submissions per minute, 5 sign-in attempts per minute per IP.

Elevation of privilege. No path from AR-user to principal-side. The AR-user surface and the principal-side surface share the same session-cookie scheme but the role check on every handler is explicit; an AR-user with a tampered cookie that claimed principal-admin would still fail the database RLS predicate and the handler-level role check.

Residual risk. AR-user account takeover via credential reuse from an unrelated breached service. Mitigated by the per-tenant TOTP flag where the principal firm has chosen to enable it; otherwise bounded by the lockout policy and by the limited blast radius (one AR’s records, no cross-AR or cross-tenant access).

Three actions are terminal: recording an FCA notification on a breach (POST /api/breaches/:id/notify-fca), signing off an annual review (POST /api/annual-reviews/:id/sign-off), and off-boarding an AR (PATCH /api/ars/:id with status transition to terminated).

Spoofing. Each terminal action requires step-up authentication: re-enter password and TOTP within the last 5 minutes. The step-up itself is a recorded event in the audit log.

Tampering. The state machines (see Architecture / state machines) gate the legal transitions; a notify-fca against a breach in Resolved state returns 409. The breach’s notification timestamp is server-assigned at the moment of step-up success.

Repudiation. The terminal action records actorUserId, the step-up event id, and the timestamp. A director’s sign-off on an annual review carries the director’s name, the FRN of their firm, and the signOffNotes string verbatim.

Information disclosure. Terminal actions return only the new state of the affected record. They do not expose neighbouring data.

Denial of service. Step-up has a separate rate limit: 10 per minute per session, with a TOTP-failure lockout that escalates to a sign-in-level lockout after three consecutive failures.

Elevation of privilege. No terminal action grants further privilege; all are operations on existing records.

Residual risk. A complete account compromise that defeats both the password and the TOTP at sign-in and again at step-up. This is the operational ceiling of the auth model. Higher assurance (hardware key as the second factor, or a second director’s co-sign on terminal actions) is documented as production hardening but not in v1.

The audit log read path (GET /api/audit) and the export path (POST /api/audit/export) are scoped by tenant and by RBAC. The FCA-auditor role is a guest read role gated by a per-tenant feature flag.

Spoofing. Same session-cookie scheme as the rest of the workspace.

Tampering. The audit log is append-only at the storage layer. The read path cannot mutate. Export produces a derived JSONL bundle plus a SHA-256 manifest that allows the recipient to verify against the live chain.

Repudiation. Reads and exports are themselves audit-logged with action = "audit.read" and action = "audit.export", including the cursor or query window.

Information disclosure. The export endpoint is rate-limited (5 per hour per session) so a compromised session cannot exfiltrate the entire tenant in seconds.

Denial of service. Read at 60 per minute per session; export at 5 per hour per session.

Elevation of privilege. No write path exists.

Residual risk. Bulk read by a compromised principal-side session within the rate-limit ceiling. Bounded by the limit and by the audit-of-the-audit (every access leaves a trail).

Network-layer threats (TLS, BGP, edge DDoS) are inherited from Vercel. Platform-level Vercel compromise is covered by Vercel’s own attestations. Browser-level exploits on a user’s device are out of scope. Sub-processor compromise is documented separately in sub-processors.

The next sibling pages drill into the specific mitigations called out above: rate limiting, insider threat, and tampering and replay.