Control Register
| Owner | Classification | Version | Effective | Next review | Status |
|---|---|---|---|---|---|
| Sekhar Prakash | Internal | 0.1 | 2026-08-27 | 2026-11-27 | Draft |
Purpose. The canonical control set for the security programme.
- Each security domain is decomposed into discrete, individually testable controls; compliance frameworks are mapped onto these controls as lenses.
- Authoritative source for what controls exist, who owns them, how they are tested, and their current state.
- A control in a failed state corresponds to an entry in the Findings Register.
- Excluded controls carry a rationale in the Statement of Applicability.
Basis. The controls consolidated here derive from security assessments conducted continuously since January 2026 (see Programme History); this register formalises that prior work into a single versioned reference.
SCAFFOLD — HAND-MAINTAINED
This register is intended to be generated from controls.yml once the data loader exists (see _MIGRATION-PLAN.md step 2). Neither the file nor the loader exists yet, so this page is hand-maintained — edit it directly, and keep the schema below as the target shape. The earlier "do not edit directly" notice described the planned end state as though it were current. The domains below are the seeded grain sample (6–10 controls per domain, ~150–200 total).
Control record schema
- id: CTL-D7-01
domain: 7
statement: "..." # the condition the control asserts
type: preventive # preventive | detective | corrective
owner: backend-platform
maps_to:
soc2: [CC6.1] soc1: [] iso: [A.8.4] gdpr: [] dpdp: []
iso42001: [] euaiact: [] pci: [] hipaa: [] cis: [3,4] owasp: [A01]
test: "how design and operation are verified"
cadence: continuous
evidence: "artifact and location (link)"
status: fail # pass | partial | fail | planned | risk-accepted | n-a
applicability: applicable
finding: F-01Status legend (status column only):
| Symbol | Meaning |
|---|---|
| 🟢 | pass |
| 🟠 | partial |
| 🔴 | fail |
| ⚪ | planned / n-a |
| ✅ | resolved |
Register — Domain 4: Identity & Access Management
| ID | Control statement | Maps to | Status | Finding |
|---|---|---|---|---|
| D4-01 | Server-side RBAC, deny-by-default on sensitive routes | CC6.1-3 · A.8.5 · A01 | ✅ | F-02 |
| D4-02 | Multi-factor authentication required (mandatory for admin/approver) | CC6.1 · A.5.17 · A07 | 🔴 | F-23 |
| D4-03 | Enterprise SSO / SAML | CC6.1 · A.5.16 | ⚪ | — |
| D4-04 | Session tokens httpOnly + Secure + SameSite with refresh rotation | CC6.1 · A.8.5 · A07 | 🔴 | F-07 |
| D4-05 | No authentication token transported via query parameter | CC6.6 · A09 | 🔴 | F-28 |
| D4-06 | Passwords hashed with a work-factor KDF (argon2/bcrypt/scrypt) | CC6.1 · A.8.5 | 🔴 | F-40 |
| D4-07 | Brute-force / credential-stuffing protection on authentication | CC6.6 | 🔴 | F-41 |
| D4-08 | Least-privilege machine identities | CC6.1 · A.8.2 | 🟠 | F-12 |
| D4-09 | Periodic access reviews and joiner/mover/leaver process | CC6.2-3 · A.5.18 | 🔴 | — |
| D4-10 | No secrets in source or version-control history; rotation enforced | CC6.1 · A.8.24 | 🟠 | F-34, F-35 |
2026-09-15: the exposed GCP key was regenerated (confirmed), so the rotation half of this control is demonstrated. Score held at 🟠 — not raised — because (a) the blobs remain reachable in both git histories, (b) no .gitleaks.toml or push protection exists in either repo, so recurrence is unprevented, and (c) the LinkedIn client id/secret pairs from F-35 have no confirmed rotation. Rotation happened; it is not yet enforced. | ||||
| D4-11 | Authorisation role derived from verified identity, not client input | CC6.1 · A.8.5 · A01/A07 | 🔴 | F-42 |
Register — Domain 7: Tenant Isolation
| ID | Control statement | Maps to | Status | Finding |
|---|---|---|---|---|
| D7-01 | Request tenant bound to the JWT claim; mismatched header rejected | CC6.1 · A.8.4 · A01 | 🔴 | F-01 |
| D7-02 | PostgreSQL row-level security as defence-in-depth backstop | CC6.1 · A.8.4 | 🔴 | F-01 |
| D7-03 | Tenant-namespaced cache keys | CC6.1 · A.8.4 | 🟢 | — |
| D7-04 | Tenant-scoped object-storage prefixes | CC6.1 · A.8.4 | 🟢 | — |
| D7-05 | Tenant-scoped asynchronous/workflow visibility | CC6.1 | 🟠 | F-38 (risk-accepted 2026-08-09 — see Findings) |
| D7-06 | Automated cross-tenant access tests in CI | CC7.1 · A.8.29 · CIS 4 | 🔴 | — |
| D7-07 | Conversation/checkpoint state tenant-scoped (Finni) | CC6.1 · A.8.4 · A01 | 🔴 | (F-43, pending) |
Register — Domain 14: Secure SDLC & DevSecOps
| ID | Control statement | Maps to | Status | Finding |
|---|---|---|---|---|
| D14-01 | Threat modeling performed on changes to auth, tenant boundaries, financial data, or AI | CC8.1 · A.8.25 · A04 | 🔴 | — |
| D14-02 | Automated security scanning gated in CI (SCA, SAST, secret, IaC, image) | CC7.1 · A.8.28 | 🟠 | F-31 |
| D14-03 | Branch protection + required review on all repositories | CC8.1 · A.8.32 | 🟠 | F-37 |
2026-09-24: branch protection is live via the org-level ruleset DefaultBranchProtection on blitz, blitz-ui, fin-infra and fin-internal-docs (PR required, no force-push, no deletion; fin-infra#55 closed 2026-09-09). Held at 🟠 because (a) required review is 0 approvals, kept by owner decision 2026-09-24, (b) required status checks were removed 2026-09-15, (c) finwebsite and fin-works are outside the ruleset, and (d) org admins bypass always. | ||||
| D14-04 | Independent penetration test + vulnerability-disclosure programme | CC7.1 · A.8.29 | 🔴 | F-24 |
CI control state — authoritative
THE SINGLE SOURCE FOR THIS FACT
Other pages link here; none restate it. Per _MAINTAINING.md: "no page restates another. If a fact appears twice, one copy is a bug." This fact previously appeared in nine places, which is why one CI change invalidated sixteen documents.
As of 2026-09-15, what runs in CI, and what does not:
| Check | State | Trigger | Blocking? |
|---|---|---|---|
bun audit (SCA) | ✅ live | weekly, in each repo's Dependency Health workflow | ❌ no — reports only |
Lockfile guard (--frozen-lockfile, lockfile committed, manifest↔lock sync) | ⛔ withdrawn 2026-09-15 | — | — |
| Import guard (unresolved relative imports) | ⛔ withdrawn 2026-09-15 | — | — |
| SAST · secret scanning · IaC scan · image scan | 🔴 not implemented | — | — |
Nothing blocks a merge on a security or dependency check. The org ruleset DefaultBranchProtection no longer carries a required_status_checks rule. Branch protection itself is unchanged and active on blitz, blitz-ui, fin-infra and fin-internal-docs: pull request required, no force-push, no branch deletion, review threads must resolve.
Why the guards were withdrawn. Neither was a vulnerability scanner — both asserted consistency, not safety. Measured over their full lifetime:
| Guard | Runs | Failures | Genuine catches | Hit rate |
|---|---|---|---|---|
| Lockfile guard | 504 | 51 | 1 | 0.2% |
| Import guard | 312 | 0 | 0 | 0% |
Of the lockfile guard's 51 failures, 47 were release-bot noise on manifest/* branches and 1 was the PR that introduced the guard testing itself — leaving one real catch (a package.json↔bun.lock mismatch) in about a year. Cost was ~750 billed minutes/month across three repos, at a 13–17s median billed as full minutes, on PRs where 75–84% touched no manifest, lockfile, Dockerfile or workflow.
Accepted residual risk. A manifest↔lockfile mismatch is no longer caught pre-merge; it now surfaces as a failed build or deploy instead of a blocked PR. The failure is self-announcing within the hour and diagnosable in ~0.3s with bun install --frozen-lockfile --dry-run. The guards are disabled, not deleted — re-enabling is one API call per repo.
Direction of travel. D14-02 moved 🔴 → 🟠 rather than staying red: SCA genuinely runs now, weekly, where before it ran nowhere. The maturity ladder in Coverage & Methodology has no symbol for scheduled but non-blocking, which is the actual state — worth adding rather than forcing this into "gated" or "absent".
Threat modeling (D14-01) also runs as a scheduled activity — see the Assessment & Evidence Cadence — and its output feeds the Risk Register.
Coverage notes
- D4-09, D7-06 — defined target-state controls with no corresponding finding; they represent untracked gaps this register surfaces.
- D4-11, D7-07 — originate from the AI-governance assessment; pending entry into the Findings Register (renumbered to clear the existing F-01 sequence).
- Domains 16 and 17 (financial-data integrity and business logic — the straight-through-processing / ICFR controls) are the next to be seeded.
Source of detail
Full remediation detail for each failed control resides in the linked GitHub issue via the Findings Register.
Related
- → Security Domains — objective and target-state per domain
- → Findings Register — failed controls, with tracking issues
- → Framework Crosswalk — controls viewed per framework
Revision history
| Version | Date | Author | Change |
|---|---|---|---|
| 0.1 | 2026-08-27 | Sekhar Prakash | Initial register; Domains 4 and 7 seeded. |
| 0.2 | 2026-09-24 | Sekhar Prakash | D14-03 annotated with the live ruleset state; status held at 🟠. |
Finaisse Internal — Confidential. Not for external distribution.