Security reviews
Each part of DigiFIR had its own security review in late October and November 2026. Every review records each finding, how serious it is, what was done about it, and where. A finding that came with a reproduction is kept as a regression test, so the test that showed the defect now passes because the defect is gone.
NOTE
These are internal reviews by the project. An audit by a CERT-In-empanelled auditor, with GIGW 3.0 clearance, is planned before go-live and has not yet taken place.
Summary
| Part | Review | Findings | Outcome |
|---|---|---|---|
| Backend | Five lenses: authorisation (23), data protection (23), integrity (16), web (13), completeness (12) | 87: 7 high, 21 medium, 59 low | 59 fixed (some partly), 27 refuted, 1 superseded. All 7 high findings fixed. |
| Officials' console | Web review | 12 | 2 confirmed and fixed, 10 refuted |
| Network | Trust (TRUST-01 to 25) and exposure (H1 to L18), 1 to 2 November 2026 | 25 trust findings, 1 critical; 30 exposure findings (H1 to H3, M1 to M9, L1 to L18), 1 refuted | Most fixed or mitigated; the critical TRUST-01 fixed; open items below |
| Chaincode | An initial audit, four further passes, three batch-3 parts and an adversarial pass | Recorded in the audit log | Coverage 86 to 98 % per package; no reachable vulnerability in govulncheck |
| Citizen app and portal | CIT-SEC findings | 27 handed to the repository: 2 high, 11 medium, 14 low | 21 fixed, 4 partly fixed, 1 open until the backend changes lock-screen notifications, 1 left by decision (no certificate pinning); none refuted |
| Police app | F01 to F31 | 27 with a severity: 1 high, 11 medium, 15 low | 18 fixed, 2 partly, 5 refuted, 1 confirmed and left for a design change, 1 fixed in part with the backend |
| Lab app | LAB-SEC-01 to 24 | 24: 2 high, 6 medium, 16 low | 18 fixed, 4 partly, 2 decided otherwise (key authentication, certificate pinning); none refuted as a whole |
| Court app | CRT-SEC-01 to 32 | 32: 1 high, 9 medium, 22 low | 28 fixed, 3 partly, 1 confirmed and left for a design change; none refuted |
Backend
The backend's review looked at it through five lenses: who may do what (authorisation), what personal data goes where (data protection), whether the ledger's word can be trusted (integrity), web attack surface, and anything the others missed (completeness).
The seven high findings, all fixed:
| ID | Area |
|---|---|
| AUTHZ-01 | Authorisation |
| DP-01, DP-02 | Data protection |
| INT-01, INT-02 | Integrity of the projected ledger |
| WEB-01, WEB-02 | Web: request handling before authentication, attestation order |
Parts still open:
- DP-09: retention of some operational tables is a policy decision for the pilot states.
- INT-05: some registry records have no chaincode query by key, so the integrity check cannot compare them yet.
- INT-07: the pinned genesis block hash is not yet a required pilot setting.
Network
The critical finding was TRUST-01. In the review, three gateway keys committed a forged chaincode definition that every peer accepted, because the Kubernetes operator writes each organisation's policies as OR('<MSP>.member'). The fix was the channel's governance rule, under which the judiciary and two of the other three must agree, with roles named. The forgery now fails with ENDORSEMENT_POLICY_FAILURE on all six peers. Other notable fixes:
- TRUST-02: revocations were erased by the operator on every reconcile; fixed.
- TRUST-05: majority decisions needed no judiciary, and the lifecycle job committed after a timeout; fixed.
- TRUST-08: block validation accepted any orderer-organisation identity; fixed so only an orderer node can sign a block.
- TRUST-14: purged private data stayed readable in old CouchDB revisions; fixed with
network.sh compactand a 100-block transient store. - M3: peers logged their configuration, including the CouchDB password; fixed.
Open before go-live
- Fabric CA keys in each institution's HSM (TRUST-04, M2).
- Mutual TLS at peers and orderers (TRUST-10, M1).
- TLS with client authentication on the operations endpoints (TRUST-13).
- An HSM-backed key for the operations-window administrator (TRUST-03).
- A dedicated client CA for the orderers' admin endpoint (TRUST-25).
- Pod traffic encryption in every cluster (M7).
- A maintained ingress controller in place of the retired ingress-nginx (TRUST-24).
- The backend's secrets out of the ingress controllers' reach, and the backend chart's network policies brought in line with the platform's.
- A staging rehearsal of deployment phases 0 to 3 on clusters that enforce network policies.
Chaincode
The chaincode's audit log in docs/IMPLEMENTATION_NOTES.md records each pass: MVCC behaviour, determinism, erasure and input handling, with go vet, the seven analysers and coverage. Examples of what the passes found and fixed:
- A police record of a remand order could move an accused's custody state against the court's order. Now a police record never moves the state a court order holds for its day, and the custody clocks are computed from a summary of every remand order, sealed ones included.
- The batch-3 adversarial pass reviewed the session-only keys, the checkable private reads, the stricter canonical text, the laboratory's label check and supplementary reports, and court orders that must name a registered document.
govulncheck reports no reachable vulnerability on Go 1.27.3, against 43 on the earlier Go 1.24.7.
Apps
All four app reviews follow one rule: what you see is what you sign. Before the button, the screen shows every value the action records, and a value the record cannot hold is refused, never cut or changed. Examples of high findings, all fixed:
| App | Finding |
|---|---|
| Lab | The app reopened a backend session with the device key in the background, without the PIN, so "End all sessions now" did not cut the phone off |
| Lab | Lab notes were signed and sent without the analyst asking |
| Court | Leave, link-officer and notification switches signed a court hand-over in the background |
| Citizen | After an error that let the citizen try again, a changed complaint was filed as the first one signed |
| Citizen portal | On a shared computer, the next person in the browser resumed the citizen's session |
| Police | A final report signed an accused count and charged sections the officer never saw or chose |
The police, lab and court apps keep one finding confirmed but not fixed: the signing key itself needs no separate user authentication, and only the app PIN guards it. Changing that needs a design change in the backend and the chaincode; the PIN storage side of the same finding was fixed.
Sources: digifir-backend docs/architecture/SECURITY_REVIEW.md, apps/console/SECURITY_REVIEW.md, docs/PROJECT-STATUS.md; digifir-network docs/SECURITY_REVIEW.md; digifir-chaincode docs/IMPLEMENTATION_NOTES.md (audit log), README.md; each app's docs/SECURITY_REVIEW.md on the migration branch.