nigig-org/PAYMENT_RISK_REGISTER.md

118 lines
5.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Payment Risk Register
> Each row is one discrete payment feature or flow in nigig-pay.
> Risk = Likelihood × Impact. Updated after Phase 0 fixes.
---
## 1. USSD Dispatch
| Attribute | Value |
|---|---|
| **Feature** | `dispatch_ussd()` — types into Safaricom USSD dialog |
| **Current Gate** | `#[cfg(feature = "demo")]` — disabled in production builds |
| **Residual Risk** | Low (production), High (demo/development) |
| **Remaining Issues** | No idempotency key. No server-side confirmation. |
| **Next Action** | Phase 1: provider integration with idempotency |
---
## 2. Biometric Authentication
| Attribute | Value |
|---|---|
| **Feature** | Fingerprint gate before USSD dispatch |
| **Previous Risk** | P0 — fail-open on error dispatched without auth |
| **Current Gate** | Fail-closed: error marks payment as `Failed` |
| **Residual Risk** | Low |
| **Remaining Issues** | `cfg!(feature = "demo")` wraps the biometric prompt itself; in non-demo builds, biometric is skipped entirely (but dispatch is also blocked). |
| **Next Action** | Phase 1: biometric in all builds, dispatch gated by provider auth |
---
## 3. PIN Handling
| Attribute | Value |
|---|---|
| **Feature** | M-Pesa PIN stored in SharedPreferences for AccessibilityService |
| **Previous Risk** | P0 — PIN persisted indefinitely across sessions |
| **Current Gate** | Phase 0 removes the Rust `KEY_PIN` write and the Android AccessibilityService now removes any stale key then fails closed at the PIN step. No normal or demo flow can hand a PIN to SharedPreferences. |
| **Residual Risk** | No payment PIN persistence path remains. The legacy AccessibilityService must still be removed or kept disabled until an authorised provider integration exists. |
| **Next Action** | Phase 5: replace the legacy USSD bridge with an authorised provider integration; do not restore PIN handoff. |
---
## 4. Auto-Retry
| Attribute | Value |
|---|---|
| **Feature** | 5 retries with exponential backoff on retryable errors |
| **Previous Risk** | High — no idempotency key, can duplicate payments |
| **Current Gate** | Automatic replay was removed. A dispatch error is surfaced and requires explicit reconciliation/new user action. |
| **Residual Risk** | No automatic retry path remains. Provider-side idempotency is still required before any future production gateway is enabled. |
| **Next Action** | Phase 5: provider idempotency + reconciliation before enabling an authorised gateway. |
---
## 5. Bulk Payments
| Attribute | Value |
|---|---|
| **Feature** | Sequential batch USSD from CSV |
| **Previous Risk** | Critical — no provider, no reconciliation, no audit |
| **Current Gate** | Legacy bulk dispatch is disabled because the PIN handoff is disabled and no provider gateway exists. |
| **Residual Risk** | No functioning batch payment path remains. A future provider batch feature requires idempotency, reconciliation, limits, and audit export. |
| **Remaining Issues** | No authorised provider rail. No post-batch audit export. |
| **Next Action** | Phase 5: only after provider integration + reconciliation model. |
---
## 6. SMS Observation
| Attribute | Value |
|---|---|
| **Feature** | Parse M-Pesa SMS from inbox to detect transaction outcomes |
| **Previous Risk** | High — raw SMS persisted to disk, no sender validation |
| **Current Gate** | `raw_message` no longer written to PSV. Renamed `Verified``ObservedEvidence`. |
| **Residual Risk** | Medium — SMS remains untrusted evidence and sender labels can be spoofed. |
| **Remaining Issues** | Sender provenance is not provider authentication. Evidence must remain non-settlement data. |
| **Next Action** | Phase 5: provider confirmation; Phase 7: consent-safe adversarial SMS corpus. |
---
## 7. Transaction Storage (PSV)
| Attribute | Value |
|---|---|
| **Feature** | Flat pipe-delimited file for transaction records |
| **Residual Risk** | Medium — legacy tracker still uses `f64` money and remains a compatibility path. |
| **Remaining Issues** | Domain/storage use exact money and integrity checks; tracker/UI consumers still need the Phase 6 money migration. |
| **Next Action** | Phase 6: migrate tracker UI/parser consumers to exact minor-unit money. |
---
## 8. R-SEC-001: Cloudflare API token committed to the repository
| Attribute | Value |
|---|---|
| **Discovered** | 2026-07-27, during Phase 1 completion |
| **Severity** | High |
| **Description** | A live Cloudflare API token (`cfut_Jviss2Pz…`) was committed in plaintext in `README.md`. |
| **Exposure** | Present since the `Initial commit` (`cc05abd`) and pushed to a public remote. It is readable in git history by anyone who has ever cloned or can clone the repository. |
| **Immediate action taken** | Removed from `README.md` in the working tree; the example now reads the token from `$CLOUDFLARE_API_TOKEN`. |
| **Required action (owner)** | **Rotate the token in the Cloudflare dashboard.** Redaction from the tip commit does **not** revoke it and does **not** remove it from history. Treat it as compromised. |
| **Optional follow-up** | Purge from history with `git filter-repo`, accepting that this rewrites every published commit hash and requires all clones to re-clone. Rotation is mandatory; purging is not a substitute for it. |
| **Preventive control** | Add secret scanning to CI. Never place a credential in a tracked file. |
---
## Summary: Post-Phase 0 Status
| Severity | Count | Notes |
|---|---|---|
| **Critical (production)** | 0 | Tracker-only production path has no working PIN/USSD dispatch route. |
| **Critical (legacy demo)** | 0 | PIN handoff and automatic replay are disabled; legacy automation fails closed at the PIN step. |
| **High** | 3 | No provider confirmation, legacy tracker `f64` money, Android key provisioning not implemented. |
| **High (credential)** | 1 | R-SEC-001 Cloudflare token in git history — rotation required. |
| **Medium** | 2 | SMS sender/provenance is untrusted; legacy tracker remains a compatibility path. |
| **Low** | 1 | Hard-coded fee table. |