---
schema: codex-delivery/threat-model@1
document_id: "TM-<<required: change ID or stable model ID>>"
change_id: "CHG-<<required: YYYYMMDD-slug>>"
risk_tier: "<<required: R0 | R1 | R2 | R3>>"
document_status: draft
decision_state: UNKNOWN
owner: "<<required: risk owner>>"
evidence_as_of: "<<required: ISO 8601 or UNKNOWN>>"
commit_sha: "<<required: exact SHA for implementation delta or N/A with reason>>"
supersedes: "<<required: prior threat model ID or N/A>>"
refs:
  - TASK-<<required>>
---

# Threat-model delta: <<required: system/change>>

This is a change-specific delta. Reference the system baseline instead of rewriting it.

## Scope and review trigger

- System/baseline model: `<<required: path/ID + version/hash>>`
- Change and exact fingerprint: `<<required>>`
- Included assets/flows: `<<required>>`
- Excluded scope and owner: `<<required>>`
- Risk tier / methodology used: `<<required: e.g. scenario-based, ASVS mapping; no score-only conclusion>>`
- Required re-review triggers: `<<required: boundary, data, provider, permission, model/tool, migration, incident>>`

## Assets and impact

| Asset ID | Asset/data class | Confidentiality | Integrity | Availability | Maximum impact | Owner |
|---|---|---|---|---|---|---|
| ASSET-01 | <<required>> | low/medium/high | low/medium/high | low/medium/high | <<required>> | <<required>> |

Include customer trust, money, credentials, domain/email/cloud control, audit evidence, and founder recovery capacity where relevant.

## Actors and capabilities

| Actor ID | Legitimate role or attacker | Starting access | Capability/motive | Explicitly excluded capability |
|---|---|---|---|---|
| ACTOR-01 | <<required>> | <<required>> | <<required>> | <<required>> |

## Entry points and trust boundaries

| ID | Source | Boundary crossed | Authentication/validation | Destination/sink | Data/action |
|---|---|---|---|---|---|
| FLOW-01 | <<required>> | <<required>> | <<required>> | <<required>> | <<required>> |

Provide a small diagram when the table does not make ordering or privilege visible.

```text
<<required: source → validation → trusted service → sink / external effect>>
```

## Data handling delta

| Data class | New collection/use? | Storage/retention | Provider/region | Log/eval use | Delete/export | Evidence |
|---|---|---|---|---|---|---|
| <<required>> | yes/no | <<required>> | <<required>> | <<required>> | <<required>> | <<required>> |

Do not paste raw credentials, personal data, payment data, or customer prompts here. Reference a controlled/redacted artifact.

## Threat scenarios

Copy one block per material scenario. High/Critical scenarios require positive verification and named residual-risk acceptance after mitigation. Active residual Critical is `STOP`; active residual High remains at least `PAUSE` until mitigation or scope reduction lowers the residual severity below High. Acceptance or a formal policy does not relabel severity.

### TH-01 — <<required: source-to-sink abuse/failure scenario>>

- Assets: `<<required: ASSET-*>>`
- Actor and prerequisites: `<<required>>`
- Entry/flow/sink: `<<required: FLOW-* and exact effect>>`
- Scenario: `<<required: attacker or failure sequence, not just an OWASP category>>`
- Impact/severity: `low | medium | high | critical` — `<<required: concrete consequence>>`
- Likelihood basis: `<<required: evidence/range/unknown; do not hide impact in a single score>>`
- Prevent controls: `<<required: server-side, least privilege, validation, constraint>>`
- Detect controls: `<<required: signal, query, threshold, owner>>`
- Contain controls: `<<required: flag, scope/authority limit, key revoke, isolation>>`
- Recover controls: `<<required: rollback, restore, compensation, communication>>`
- Verification IDs: `<<required: test/eval/manual EVID-*>>`
- Verification result and fingerprint: `<<required or UNKNOWN>>`
- Residual severity: `low | medium | high | critical | unknown`
- Residual risk statement: `<<required: consequence and remaining uncertainty>>`
- Active?: `yes | no`
- Waivable under an applicable formal policy?: `yes | no`
- Owner / due / state: `<<required>> / <<required>> / identified | mitigating | verified | accepted_risk | closed | reopened`
- Risk acceptance: `<<required for accepted_risk: actor ID + authority/policy basis + exact scope + expiry + rationale + reopen trigger; otherwise N/A>>`
- Reopen trigger: `<<required>>`

## Required negative/abuse cases

| Case ID | Scenario | Expected safe result | Evidence | Result |
|---|---|---|---|---|
| SEC-NEG-01 | another tenant/resource ID | no read, no mutation, auditable denial | EVID-<<required>> | UNKNOWN |
| SEC-NEG-02 | duplicate/replayed external event | one idempotent effect | EVID-<<required>> | UNKNOWN |
| SEC-NEG-03 | untrusted input attempts to reach privileged sink | blocked or bounded; no unsafe side effect | EVID-<<required>> | UNKNOWN |

Delete inapplicable examples with a reason and add change-specific cases.

### Authorization, tenant, and state-consumer coverage

A single “other tenant” case is an example, not coverage. Enumerate changed paths plus every semantically affected reader/writer reachable from the changed state, schema, event, permission, or API contract. Reconcile the denominator; a file with no diff can still mis-handle a new persisted state.

| Path ID | Entry point/channel | Identity/role/tenant state | Resource/operation/condition | Sink/effect | Positive cases | Denial/isolation cases | Evidence | Covered? |
|---|---|---|---|---|---|---|---|---|
| AUTH-PATH-01 | API/UI/auth middleware/job/queue/search/cache/serializer/export/support | <<required>> | list/read/create/update/delete/bulk/file/state interpretation | <<required>> | <<required>> | <<required>> | EVID-<<required>> | yes/no/unknown |

- Affected path inventory source/hash: `<<required: route/policy/schema/event manifest + consumer/reference/dataflow graph>>`
- Paths scheduled / covered / excluded: `<<required: x / n / IDs>>`
- Exclusion reason / owner / expiry: `<<required or N/A>>`
- Generated/property/policy test used: `<<required or N/A with reason>>`

`SHIP` requires every semantically affected reachable path to be covered or explicitly removed from scope. “No diff” is not an exclusion. Prove the path unreachable/unexposed or prove its consumed contract unchanged with positive evidence. Missing list/bulk/cache/search/file/job/queue/serializer/old-client/export/support-impersonation paths are `UNKNOWN`, not “0 authorization failures.”

## Dependency and build delta

| Dependency/action | Identity/version/digest | Why needed | Maintainer/license/install scripts | Transitive/CI privilege | Vulnerability/provenance evidence | Removal path | Decision |
|---|---|---|---|---|---|---|---|
| <<required or N/A with reason>> | <<required>> | <<required>> | <<required>> | <<required>> | <<required>> | <<required>> | UNKNOWN |

### Privileged CI/build privilege flow

Fill one row for every changed or release-relevant privileged producer, artifact consumer, publisher, or deployer job, even when its workflow file has no diff. `N/A` requires proof that this change uses no privileged build/publication/deployment flow at all.

| Job/workflow | Trigger and actor trust | Checked-out/ref executed | Runner trust | Token permissions | Secrets/OIDC | Network/write targets | Artifact input producer/trust | Artifact output/consumer | Decision/evidence |
|---|---|---|---|---|---|---|---|---|---|
| CI-JOB-01 | pull_request / pull_request_target / workflow_run / push | <<required>> | hosted/self-hosted | <<required>> | <<required>> | <<required>> | <<required>> | <<required>> | STOP/PAUSE/UNKNOWN/SHIP + EVID-<<required>> |

Untrusted contributor-controlled code or artifact must not execute in a context that has write token, secret/OIDC, trusted self-hosted runner, production network, or publication authority. If that source-to-sink path exists, it is a non-waivable veto until the path is removed or isolated and reverified.

## Residual-risk summary

| Threat | Initial severity | Residual severity | Active? | Waivable? | Control evidence | Acceptance policy/actor/scope/expiry/rationale/reopen | State |
|---|---|---|---:|---:|---|---|---|
| TH-01 | <<required>> | low/medium/high/critical/unknown | yes/no | yes/no | <<required>> | <<required or N/A>> | UNKNOWN |

## Decision

- Active critical/high unverified scenarios: `<<required: count and IDs>>`
- Required evidence missing/stale: `<<required: count and IDs>>`
- Derived state: `STOP | PAUSE | UNKNOWN | SHIP`
- Decision event / actor / time: `<<required>>`
- Reason: `<<required>>`
- Next action / owner / due: `<<required>>`

`0 identified threats` is not proof of safety. Record review scope and blind spots.

Derive the gate from the structured fields. Residual `unknown` or missing/stale evidence is `UNKNOWN`. Any active residual Critical is non-waivable `STOP`. Any active residual High is at least `PAUSE`; marking it `accepted_risk` or `waivable=yes` does not lower its severity. `SHIP` requires mitigation/scope reduction that moves every active residual below High, plus any policy-required acceptance for the remaining Low/Medium risk.
