# DPDPA Threat Modeling Implementation Checklist

**8-week rollout plan: from first Data Flow Diagram to production CI/CD gate.**

Applicable to all Data Fiduciaries under Section 8(5). Steps marked **[SDF]** are additionally required for Significant Data Fiduciaries under Section 10(2)(c)(i) and Rule 13(1)–(2).

---

## How to Use This Checklist

1. **Assign an owner** to each phase (AppSec lead, DPO, CISO, Engineering Lead)
2. **Check off items** as you complete them
3. **Never skip the CI/CD gate** (Week 7-8) — a threat model that isn't gated gets stale
4. **Review trigger:** Repeat Phase 3 onwards for any new system or material DFD change

---

## Phase 0 — Scope and Prerequisites (Week 1)

**Owner: AppSec / Security Lead**

### 0.1 Identify Systems in Scope

- [ ] List all systems that collect, process, or store personal data as defined under Section 2(t)
- [ ] Classify each system: All Data Fiduciary → Section 8(5); SDF → also Section 10(2)(c)(i)
- [ ] Identify which systems have the highest personal data volume or sensitivity (prioritise first)
- [ ] Note any systems processing children's data (under 18) — Section 9 applies: verifiable consent (9(1), Rule 10), no detrimental processing (9(2)), no tracking, behavioural monitoring or targeted ads (9(3))
- [ ] Note any systems with cross-border data flows — Section 16 transfer obligations apply

**Deliverable:** Scope document listing systems, their DPDPA actor types, and SDF classification

### 0.2 Assemble the Team

- [ ] **AppSec / Threat Modeler** — leads LINDDUN and STRIDE analysis
- [ ] **Engineering Lead** — provides DFD source data; owns mitigation implementation
- [ ] **DPO** — reviews Part B (Data Principal rights) and Part C (risk matrix) of DPIA [SDF]
- [ ] **Legal / Compliance** — confirms lawful basis, purpose definitions, retention anchors
- [ ] **CISO** — signs off on accepted residual risks and production gate policy

### 0.3 Install Tools

- [ ] Python 3.11+ (for CI/CD gate validation script)
- [ ] `pip install pyyaml jsonschema`
- [ ] OPA CLI: `curl -sSL https://openpolicyagent.org/downloads/latest/opa_linux_amd64_static -o opa && chmod +x opa`
- [ ] Conftest: download from [github.com/open-policy-agent/conftest/releases](https://github.com/open-policy-agent/conftest/releases)
- [ ] Mermaid preview (VS Code extension or `mermaid.live`) for DFD review

---

## Phase 1 — Data Flow Diagram (Week 2)

**Owner: Engineering Lead + AppSec**

### 1.1 Draw the DFD

- [ ] Document all external entities (Data Principals, Data Processors, third parties)
- [ ] Document all processes (APIs, microservices, batch jobs, data pipelines)
- [ ] Document all data stores (databases, object stores, caches, queues, logs)
- [ ] Document all data flows (arrows between elements)
- [ ] Draw trust boundaries (internal/external, Fiduciary/Processor, India/abroad)

**Tool:** Use Mermaid (`.mmd` file in git) for version-controlled, reviewable DFDs

### 1.2 Annotate the DFD for DPDPA

Apply the four DPDPA annotation fields to every element using `dfd-annotation-guide.md`:

**External entities:**
- [ ] `dpdpa_actor`: data_principal | data_processor | consent_manager | third_party
- [ ] `trust_level`: untrusted | contractual | regulated
- [ ] `section16_applies`: true | false (cross-border)

**Processes:**
- [ ] `purposes`: list of purpose IDs — only listed purposes may be processed (Section 6(1))
- [ ] `lawful_basis`: consent | legitimate_use | legal_obligation | vital_interest
- [ ] `section5_notice_sent`: true | false — notice must precede or accompany collection
- [ ] `languages_supported`: list of BCP-47 codes covering all Data Principal locales (Section 5(3))

**Data stores:**
- [ ] `encrypted_at_rest`: true | false — Rule 6(1)(a) data security measures (encryption, obfuscation, masking or tokens)
- [ ] `retention_days`: integer — anchor to Section 8(7)
- [ ] `retention_legal_anchor`: e.g., "S.8(7) + Rule 8(3)"
- [ ] `erasure_mechanism`: hard_delete | crypto_shred | anonymise
- [ ] `purpose_segregated`: true | false — avoid cross-purpose co-mingling

**Data flows:**
- [ ] `purpose`: purpose ID string (Section 6(1))
- [ ] `transport_security`: tls13 | mtls | none — Rule 6(1)(a) security measure; TLS 1.3+ is our baseline (the Rule names no protocol)
- [ ] `section16_transfer`: true | false (cross-border flows)

**Deliverable:** Annotated DFD as Mermaid file committed to the service repository

---

## Phase 2 — Threat Model (Week 3–4)

**Owner: AppSec**

### 2.1 Prepare the Threat Model YAML

- [ ] Copy `threat-model-template.yaml` to the service repository (e.g., `docs/threat-model.yaml`)
- [ ] Fill in `system_info`: system name, version, date, SDF flag, DPDPA sections in scope
- [ ] Add one `dfd_elements` entry per DFD element
- [ ] Link the DFD (`dfd_source`) in `system_info`

### 2.2 Run LINDDUN Analysis (per element)

For each DFD element, assess all 7 LINDDUN categories:

| Category | Key Question | DPDPA Hook |
|----------|-------------|-----------|
| Linkability | Can two flows be joined to reveal a pattern beyond consented purpose? | S.6(1) |
| Identifiability | Can quasi-identifiers allow re-identification? | S.2(t), S.8(7) |
| Non-repudiation | Does the audit log prevent erasure rights? | S.6(10), S.8(7) |
| Detectability | Does a status code or timing difference reveal a resource exists? | S.8(5) |
| Disclosure | Is personal data accessible beyond minimum-necessary set? | S.8(5), Rule 6(1)(a) |
| Unawareness | Does the Data Principal know what is collected and why? | S.5(1), S.5(3) |
| Non-compliance | Is any processing past retention, outside purpose, or missing a control? | All sections |

**For each `present: true` finding:**
- [ ] Write a concrete `description` (1-2 sentences, specific to this element)
- [ ] Write a `mitigation` with a testable control (code, config, or process)
- [ ] List `dpdpa_sections` that this finding implicates
- [ ] Set `severity`: critical | high | medium | low
- [ ] Set `mitigation_tested: false` — mark true only when a passing test exists

### 2.3 Run STRIDE Analysis (per element)

For each DFD element, assess all 6 STRIDE categories:

| Category | Key Question | DPDPA Hook |
|----------|-------------|-----------|
| Spoofing | Can an attacker submit DSR requests as another Data Principal? | S.11, S.12 |
| Tampering | Can an attacker modify consent or personal data? | Rule 6(1)(a) |
| Repudiation | Can the Fiduciary prove consent was given or breach was notified? | S.6(10) |
| Information Disclosure | Can personal data escape the trust boundary? | S.8(5), S.8(6) |
| Denial of Service | Can an attack deny Data Principals the ability to exercise rights? | Rule 6(1)(d) |
| Elevation of Privilege | Can a caller access data outside their consented purpose? | S.6(1), S.8(1)-(2) |

**Same quality rules as LINDDUN:** every `present: true` needs description + mitigation + DPDPA sections + severity.

### 2.4 Validate the Threat Model Locally

```bash
python - <<'EOF'
import yaml, glob, sys

REQUIRED_LINDDUN = ["linkability","identifiability","non_repudiation",
                    "detectability","disclosure","unawareness","non_compliance"]
REQUIRED_STRIDE  = ["spoofing","tampering","repudiation",
                    "information_disclosure","denial_of_service","elevation_of_privilege"]

errors = []
for path in glob.glob("**/threat-model*.yaml", recursive=True):
    tm = yaml.safe_load(open(path))
    for elem in (tm or {}).get("dfd_elements", []):
        eid = elem.get("id","<unknown>")
        if not eid:
            continue
        for c in REQUIRED_LINDDUN:
            e = elem.get("linddun",{}).get(c)
            if e is None:
                errors.append(f"{eid}: missing LINDDUN.{c}")
            elif e.get("present") and not e.get("mitigation","").strip():
                errors.append(f"{eid}: LINDDUN.{c} present but mitigation empty")
            elif e.get("present") and not e.get("dpdpa_sections"):
                errors.append(f"{eid}: LINDDUN.{c} present but dpdpa_sections empty")
        for c in REQUIRED_STRIDE:
            e = elem.get("stride",{}).get(c)
            if e is None:
                errors.append(f"{eid}: missing STRIDE.{c}")
            elif e.get("present") and not e.get("mitigation","").strip():
                errors.append(f"{eid}: STRIDE.{c} present but mitigation empty")
            elif e.get("present") and not e.get("dpdpa_sections"):
                errors.append(f"{eid}: STRIDE.{c} present but dpdpa_sections empty")

if errors:
    [print(f"  • {e}") for e in errors]; sys.exit(1)
else:
    print(f"✅ Threat model valid ({len(glob.glob('**/threat-model*.yaml',recursive=True))} file(s))")
EOF
```

- [ ] Zero errors before proceeding to Phase 3

**Deliverable:** `threat-model.yaml` committed; all `present: true` findings have mitigations and DPDPA section tags

---

## Phase 3 — DPIA (Week 4–5) [SDF]

**Owner: DPO + AppSec**

*Non-SDFs: This phase is recommended but not legally mandatory. If you choose to skip, document the decision and review annually.*

### 3.1 Populate the DPIA Template

Using `dpia-template.md`:

- [ ] **Part A — Description of Processing**
  - [ ] A1: System overview (2-4 sentences on personal data lifecycle)
  - [ ] A2: Purposes table — one row per purpose with lawful basis and retention
  - [ ] A3: Data categories — check applicable categories; flag children's data and cross-border
  - [ ] A4: Estimated Data Principal count (no statutory SDF threshold; a signal to monitor for S.10(1) notification)
  - [ ] A5: DFD reference — link the annotated DFD from Phase 1

- [ ] **Part B — Rights of Data Principals**
  - [ ] B1: Notice implementation (Section 5(1) and 5(3)); list languages covered
  - [ ] B2: Access, correction, erasure, grievance mechanisms; SLA for each
  - [ ] B3: Consent withdrawal mechanism; cascade deletion; proof-of-consent retention

- [ ] **Part C — Risk Assessment** (generated from threat-model.yaml)
  - [ ] C2: Populate risk matrix by copying findings from the threat model YAML
  - [ ] C3: List open findings requiring DPO decision (Critical and High open items)
  - [ ] C4: Document residual risks accepted with rationale and responsible person

- [ ] **Part D — Consultation**
  - [ ] D1: Record internal consultations (DPO, CISO, Legal, Engineering, Product)
  - [ ] D2: Confirm Data Processing Agreements exist for all Data Processors

- [ ] **Part E — Conclusion**
  - [ ] E1: Overall assessment (Approved / Approved with conditions / Escalate to Board)
  - [ ] E2: Recommendations table with owners and target dates
  - [ ] E3: Identify next review triggers

- [ ] **Part F — Sign-Off**
  - [ ] DPO, CISO, Engineering Lead, Legal Counsel have signed

### 3.2 Place DPIA Alongside Threat Model

- [ ] Store `dpia-template.md` in the same directory as `threat-model.yaml` in the service repo
- [ ] The CI/CD gate (Phase 4) will fail if this is missing for SDF systems

**Deliverable:** Signed `dpia-template.md` in git alongside `threat-model.yaml` [SDF mandatory]

---

## Phase 4 — OPA Policy Integration (Week 5–6)

**Owner: AppSec / Platform Engineering**

### 4.1 Deploy OPA Policies to IaC Pipeline

- [ ] Copy `policies/dpdpa_purpose_scope.rego` into your service repository
- [ ] Run Conftest locally against your Terraform files:

```bash
conftest test *.tf --policy policies/dpdpa_purpose_scope.rego --namespace dpdpa --output table
```

- [ ] Fix any OPA violations before proceeding (encryption-at-rest, retention, cross-border)

### 4.2 Choose Runtime Deployment (pick one or more)

| Deployment | Use When |
|-----------|---------|
| **Conftest in CI/CD** | IaC gate — Terraform, Helm, K8s manifests |
| **OPA sidecar (Kubernetes)** | Runtime API gateway policy enforcement |
| **Envoy ext_authz** | Service mesh — per-request purpose-scope checks |
| **Kong plugin** | API gateway — centralized access control |

- [ ] For each deployment target, configure the OPA policy bundle
- [ ] Verify `access_allowed` policy denies cross-purpose requests in test environment
- [ ] Verify `encryption_compliant` policy catches unencrypted storage resources
- [ ] Verify `retention_compliant` policy flags records past their retention window
- [ ] Verify `transfer_allowed` policy blocks flows to countries restricted by Section 16(1) notification (default allow)

### 4.3 Tune Purpose Registry

In `dpdpa_purpose_scope.rego`, update the purpose registry to match your actual purpose IDs:

```rego
# Replace example purposes with your system's actual purposes
valid_purposes := {
    "loan_origination",
    "kyc_verification",
    "fraud_detection",
    # Add your purposes here
}
```

- [ ] Purpose IDs in Rego match purpose IDs in `threat-model.yaml` and DFD annotations

**Deliverable:** OPA policies running in at least one environment; Conftest passing in local CI

---

## Phase 5 — CI/CD Gate (Week 7)

**Owner: Platform Engineering / DevOps**

### 5.1 Install the GitHub Actions Gate

- [ ] Copy `pipeline/threat-model-gate.yml` to `.github/workflows/` in the service repository
- [ ] Verify the gate triggers on PRs that change `threat-model*.yaml`, `.tf`, and K8s manifests
- [ ] Verify Job 1 (`validate-threat-model`) runs the Python validation script
- [ ] Verify Job 2 (`opa-iac-policy`) runs Conftest against changed IaC files
- [ ] Verify Job 3 (`dpia-coverage`) checks for `dpia-template.md` alongside SDF threat models
- [ ] Verify Job 4 (`post-summary`) posts the results table as a PR comment

### 5.2 Set Branch Protection Rules

In your GitHub repository settings:

- [ ] Require status checks: `Validate DPDPA Threat Model`, `OPA — DPDPA IaC Policies`, `DPIA Coverage (SDF)`
- [ ] Set these checks as required (not optional) — the gate only works if PRs cannot merge on failure
- [ ] Apply to `main` (and `release/*` if applicable)

### 5.3 Test the Gate End-to-End

- [ ] Open a test PR with a `threat-model.yaml` that has a `present: true` finding with empty mitigation
- [ ] Verify the gate fails with the expected error
- [ ] Fix the finding and re-push — verify the gate passes
- [ ] For SDF systems: test that a threat model with `is_significant_data_fiduciary: true` but no `dpia-template.md` fails Job 3

**Deliverable:** Gate active on `main`; branch protection rules enforced

---

## Phase 6 — Operationalization (Week 8)

**Owner: AppSec + DPO + CISO**

### 6.1 Team Training

- [ ] **Engineers:** 30-minute session using `quick-reference-card.md` — explain the 7 LINDDUN and 6 STRIDE categories with examples; walk through filling in one DFD element in the template
- [ ] **DPO:** Walk through DPIA template Parts A-F; explain how Part C is generated from the threat model YAML
- [ ] **On-call / SRE:** Ensure the team knows that Critical and High open findings block production deployment

### 6.2 Backfill Existing Systems

For systems already in production without a threat model:

- [ ] Prioritise by data sensitivity: children's data → biometric → financial → behavioural → contact
- [ ] Complete Phases 1-3 for each backfill system
- [ ] Add the CI/CD gate — it will apply to future changes even if the initial threat model is partially complete
- [ ] Set a review cadence: quarterly for Critical systems; annually for Standard systems

### 6.3 Establish Review Triggers

Document the triggers that require a threat model refresh in your SDLC process:

- [ ] New personal data field added to any DFD element
- [ ] New purpose of processing introduced
- [ ] New Data Processor boundary created (Rule 6(1)(f) DPA required)
- [ ] Cross-border transfer added or destination country changes (Section 16)
- [ ] Personal data breach occurs (Section 8(6))
- [ ] Data Protection Board issues new guidance
- [ ] 12 months since last review

### 6.4 Board Inquiry Readiness [SDF]

Verify your evidence package is ready for a Data Protection Board inquiry:

- [ ] `threat-model.yaml` version-controlled in git with full history
- [ ] `dpia-template.md` signed and version-controlled alongside threat model
- [ ] CI/CD gate logs demonstrate continuous enforcement
- [ ] All Critical and High findings are either mitigated (with `mitigation_tested: true`) or accepted by DPO with documented rationale
- [ ] Breach notification pipeline tested against Rule 7(2)(b) 72-hour clock

**Deliverable:** Threat modeling program operational; on-call runbook updated; all backfill systems on schedule

---

## Summary Checklist (Print-and-Post)

| Phase | Week | Owner | Gate |
|-------|------|-------|------|
| 0 — Scope & Prerequisites | 1 | AppSec | Scope doc signed |
| 1 — Data Flow Diagram | 2 | Engineering + AppSec | DFD committed with DPDPA annotations |
| 2 — Threat Model YAML | 3–4 | AppSec | Inline validator (Phase 2.4) returns 0 errors |
| 3 — DPIA [SDF] | 4–5 | DPO + AppSec | Part F sign-off complete |
| 4 — OPA Policy Integration | 5–6 | Platform Eng | Conftest passing locally |
| 5 — CI/CD Gate | 7 | Platform Eng / DevOps | Gate active; branch protection enforced |
| 6 — Operationalization | 8 | AppSec + DPO + CISO | Backfill plan in place; team trained |

**Legal anchor:** DPDPA 2023 Section 8(5) — "reasonable security safeguards" — the threat model is the primary engineering evidence for this standard.

---

**StateCheck Security | DPDPA Compliance Resources**
[https://statechecksecurity.com/free-assessment](https://statechecksecurity.com/free-assessment)
