Skip to main content

Threat Model

Design status: This page includes earlier planning assumptions. The current CLI contract and ADR-011 govern the CLI-first agent integration; MCP, SQLite preview handles, and server-side confirmation described below are not shipped.

AttributeValue
ProjectDevWorkWire
Version0.1
StatusDraft

Scope and Method​

Method: STRIDE (Spoofing, Tampering, Repudiation, Information Disclosure, Denial of Service, Elevation of Privilege).

Scope: the DevWorkWire package as installed and run on a developer's machine — its CLI and MCP surfaces, local config and state, the outbound Jira client, and the release pipeline that delivers it.

Explicitly excluded:

  • The Jira instance itself. Its access control, availability, and admin configuration are Atlassian's and the customer's concern.
  • The MCP harness and the LLM behind it. DevWorkWire cannot verify what is calling it, authenticate it, or constrain its reasoning. This is an accepted limitation of stdio MCP and the reason the confirm gate — not caller identity — is the primary control.
  • The user's machine posture. OS account security, disk encryption, malware, and shell history hygiene are outside what a CLI can enforce.
  • Denial of service in the conventional sense. There is no service to deny: no listener, no shared resource, one local user. DoS threats below are limited to self-inflicted resource exhaustion.

What makes this threat model unusual: the highest-severity threats here are not unauthorized access. DevWorkWire runs with the user's legitimate credentials and correct authorization. The threats that matter are actions taken with valid authority but without the user's intent, and compromise of the distribution channel.

Trust Boundaries​

#BoundaryCrossingWhy it matters
TB-1Untrusted content → DevWorkWireSource Markdown; Jira issue text read backContent may be agent-authored or attacker-influenced, and is returned to an LLM
TB-2Plan → Write (the confirm gate)*.commit with a live handle + explicit confirmationThe only irreversible transition in the system
TB-3DevWorkWire → JiraHTTPS REST v3 with the user's tokenCredential in use; network in path
TB-4Process → local disk.devworkwire.yaml, state.dbContent and identifiers at rest
TB-5Release pipeline → user machinePyPI / Homebrew installCode executing with the user's tracker credentials
TB-6Harness → MCP serverstdio spawnAny local process running as the user can invoke the tools

Assets to Protect​

AssetSensitivityOwnerNotes
Jira API tokenHighUserFull user-level tracker access. Env-only, memory-only, never persisted
Integrity of the user's backlogHighUser / teamThe asset the product exists to protect. Unintended writes are the core harm
Preview plans (preview.plan_json)MediumUserIssue content at rest for a TTL window
Import index (state.db)MediumUserIssue identifiers and content hashes. Corruption causes duplicates
Release artifacts (PyPI, tap)HighMaintainerExecuted on machines holding live credentials
CI credentials (tap push)HighMaintainerPath to publishing malicious releases
Config (.devworkwire.yaml)LowUserBase URL and project key — identifies the organization, not a credential

Threats and Attack Vectors​

IDThreatCategoryVectorImpactMitigation
T-001Prompt injection in a source document steers the agent into committing changes the user did not intendTampering / EoPTB-1 — Markdown containing text aimed at the calling LLMUnintended backlog writes under the user's credentialsThe confirm gate: an injected instruction still faces explicit confirmation on a human-readable preview. Returned content delimited and labeled as untrusted data. Accurate, non-inflating tool descriptions
T-002Prompt injection in Jira content (issue text, comments) returned by workitem.query manipulates the agentTampering / EoPTB-1 — attacker-controlled text inside the trackerAgent takes attacker-chosen actionsSame as T-001. Query results are read-only and gated on any resulting write. Content is delimited as data
T-003Agent presents a preview as a completed write, or a commit as a previewRepudiationTB-2 — ambiguous tool resultsUser believes work is done that is not, or vice versa; erodes trust in the gateMandatory result_type on every result (NFR-004-01); distinct committed/failed/untried reporting
T-004A code change introduces a write path that skips the confirm gateEoPRefactor, new feature slice, or a "convenience" flagSilent loss of the product's core safety propertyGate lives in WorkItemService, not callers; CI architecture guard forbids ungated provider writes from slices; explicit gate regression tests; no bypass flag exists to be widened
T-005Malicious or hijacked release published under the devworkwire nameTamperingTB-5 — stolen PyPI token, name-squatting, compromised CIArbitrary code on machines holding live Jira tokens. Highest-impact threat in this modelTrusted Publishing (no long-lived token to steal); PEP 740 attestations; tag-triggered releases from a protected branch; early name registration; pinned tap hash
T-006Malicious dependency introduced via a compromised or typosquatted packageTamperingTB-5 — transitive dependencyCode execution with user credentialspip-audit failing CI on Critical/High; Dependabot; minimal pinned dependency set; every new dependency justified in review
T-007Token leaked through logs, error output, or a crash tracebackInfo DisclosureTB-3/TB-4 — verbose logging, exception text, bug reportsFull tracker access for anyone reading the outputRedaction enforced by a logging filter, not call-site discipline; never persisted; never a CLI argument; regression test at maximum verbosity
T-008Token exposed via process listing, environment inspection, or shell historyInfo DisclosureTB-3 — same-user processes; ps; shell historyFull tracker accessAccepted, inherent to env-var secrets. Never accepted as a CLI argument (which would be world-readable); documentation recommends git-ignored .env or profile export; OS keychain deferred. Recorded as a known risk in F-008
T-009state.db or .devworkwire.yaml committed to a shared repositoryInfo DisclosureTB-4 — missing gitignoreTracker content and org identifiers in shared history.devworkwire/ gitignored by setup docs and any dwire init; config holds no secret by design, which bounds the damage
T-010Man-in-the-middle on the Jira connection captures the tokenSpoofing / Info DisclosureTB-3 — hostile network, TLS interceptionCredential theftTLS 1.2+ with mandatory certificate verification and no disable option; http:// base URLs rejected at config validation
T-011Drift overwrite silently destroys a teammate's tracker editTamperingTB-2 — re-import over a changed issueLost work, and loss of confidence in re-running importsDrift detected against a recorded baseline (FR-002-03); per-item overwrite-or-skip required, never defaulted (FR-002-04); commit rejected if any drifted item lacks a resolution
T-012Duplicate issues created by a blind retry of a timed-out createTamperingTB-3 — network timeout during commitBacklog pollution — the exact harm the product exists to preventCreates are never blind-retried; state re-established by searching the reference field; idempotency keys with stored-result replay (FR-004-04); per-item index writes make fail-fast resumable
T-013Terminal escape sequences in Jira content corrupt the rendered previewTamperingTB-1 → terminalUser confirms a change different from what was displayed — consent obtained under false pretensesControl characters and ANSI escapes stripped from all rendered tracker content; fields length-capped so the real change cannot be pushed off screen
T-014SSRF via the PR-reference URLSSRF / Info DisclosureTB-1 — user- or agent-supplied URL (FR-007-03)Requests to internal endpoints from the user's machineThe URL is validated as a URL and stored as text. DevWorkWire never fetches it. No PR-creation or PR-reading logic exists (Out of Scope)
T-015Malicious YAML config achieves code executionTampering / EoPTB-4 — attacker-supplied or committed configArbitrary code executionyaml.safe_load only, enforced by a lint rule; Pydantic validation of the resulting structure
T-016Path traversal via a source-document pathTampering / Info DisclosureTB-1 — crafted path from an agentReading files outside the intended treePaths resolved to absolute and confined; symlink escape refused
T-017Pathological document exhausts memory or hangs the processDoS (self-inflicted)TB-1 — enormous or deeply nested documentLocal hang; a partially-committed batch if it happens mid-commitSize and item-count limits; explicit connect/read timeouts on every Jira call
T-018A hostile local process invokes the MCP server and drives writesSpoofing / EoPTB-6 — any process running as the user can spawn itUnintended writesAccepted. A process running as the user could use the token directly and does not need DevWorkWire. The gate still requires explicit confirmation; stdio has no remote surface
T-019Interrupted commit leaves an unclear tracker stateRepudiationTB-2 — crash or kill mid-batchUser cannot tell what was writtenFail-fast with per-item index writes; commit_run marked in_progress before the first write; COMMIT_INTERRUPTED on replay rather than a fabricated result; re-preview matches what already exists

Risk Assessment Matrix​

Risk IDThreatLikelihoodImpactPriorityNotes
R-001T-005 Malicious releaseLowHighP1Low likelihood only if Trusted Publishing is in place from the first release
R-002T-004 Gate bypass introduced in codeMediumHighP1The most likely way the core property is lost is a well-intentioned refactor, not an attack
R-003T-007 Token in logsMediumHighP1Easy mistake, severe consequence; must be structurally prevented
R-004T-001 / T-002 Prompt injectionMediumMediumP1Likelihood rises as agent use grows. Gate caps impact; it does not eliminate it
R-005T-011 Silent drift overwriteMediumMediumP1Directly undermines confidence in safe re-runs — the product's second promise
R-006T-012 Duplicate on retryMediumMediumP1The exact harm the product exists to prevent
R-007T-015 Unsafe YAML loadLowHighP1Trivially prevented; catastrophic if missed
R-008T-006 Malicious dependencyLowHighP2Mitigated by CI, but a real ecosystem risk
R-009T-013 Terminal escapes in previewLowMediumP2Subtle, and it attacks consent itself
R-010T-003 Preview/commit confusionMediumMediumP2Schema-level fix already specified
R-011T-009 State/config committed to gitMediumLowP2Bounded — no secrets in either file
R-012T-019 Interrupted commit ambiguityMediumLowP2Recoverable via re-preview
R-013T-016 Path traversalLowMediumP2
R-014T-014 SSRF via PR URLLowMediumP2Currently N/A by design; becomes real the moment anything fetches that URL
R-015T-010 MITM on JiraLowHighP2Standard TLS handling suffices; the risk is adding a --insecure escape hatch later
R-016T-008 Token via env/process/historyHighHighP3 (accepted)Inherent to the chosen mechanism; see Accepted Risks
R-017T-017 Resource exhaustionLowLowP3Self-inflicted, locally visible
R-018T-018 Hostile local processLowMediumP3 (accepted)Attacker with local user access has better options

Mitigation Plan by Priority​

P1 — Critical (Must Fix Before MVP)​

  • R-002 — Place the confirm gate in WorkItemService; add the CI architecture guard forbidding ungated provider writes from slices; write gate regression tests (no handle → rejected, no confirmed → rejected, expired handle → rejected, preview issues zero writes). These are security tests and should be labeled as such.
  • R-003 — Implement redaction as a logging filter, not call-site discipline. Add a test asserting the token never appears in captured output at maximum verbosity.
  • R-007 — yaml.safe_load only, with a lint rule enforcing it.
  • R-005 — Reject any commit where a drifted item lacks an explicit resolution. Never default to overwrite.
  • R-006 — Never blind-retry a create; re-establish state via the reference field. Implement idempotency keys with stored-result replay and per-item index writes.
  • R-004 — Delimit and label returned tracker content as untrusted data; keep tool descriptions accurate about what writes. Add the injection-resistance test.
  • R-001 — Register the devworkwire PyPI name and configure Trusted Publishing before the first release. Retrofitting after a token has existed is strictly worse.

P2 — Post-MVP (Fix in Phase 1)​

  • R-008 — pip-audit gating CI on Critical/High; enable Dependabot and CodeQL.
  • R-009 — Strip control characters and ANSI escapes from all rendered tracker content; length-cap preview fields.
  • R-010 — Enforce result_type in the tool result schemas, with a test.
  • R-011 — Ship .devworkwire/ gitignore guidance in setup docs and dwire init.
  • R-012 — Mark commit_run in_progress before the first write; return COMMIT_INTERRUPTED rather than a fabricated replay.
  • R-013 — Resolve and confine source paths; refuse symlink escape.
  • R-015 — Reject http:// base URLs at config validation; never add a certificate-verification bypass.
  • Publish SECURITY.md with a private disclosure channel and realistic response expectations for a single part-time maintainer.

P3 — Future (Monitor and Revisit)​

  • R-016 — Evaluate OS keychain integration, already noted as deferred in F-008.
  • R-017 — Add document size/item-count limits and explicit Jira call timeouts.
  • R-014 — Revisit immediately if any feature ever fetches a user-supplied URL.
  • R-018 — Revisit only if a non-stdio transport is introduced.

Residual Risk and Review Cadence​

Accepted Risks​

RiskReason AcceptedReview Trigger
R-016 — Token visible via environment, process listing, or shell historyInherent to environment-variable secrets, which F-008 selected deliberately over a keychain wizard for MVP. Mitigated by documentation, and by never accepting the token as a CLI argumentKeychain support is implemented, or a credible report of real-world leakage
R-018 — Any local process running as the user can invoke the MCP serverAn attacker with local user access can read the environment and call Jira directly; DevWorkWire adds no privilege. Caller verification is not possible over stdioA network transport is added, or multi-user execution is considered
Prompt injection cannot be eliminated, only bounded by the gateNo control at this layer can make an LLM immune to manipulation. The design ensures manipulation cannot produce an unconfirmed writeAn unattended/auto-confirm mode is ever proposed — which would remove the bound entirely
No runtime security monitoringTelemetry would mean exfiltrating users' tracker activity from their machines. Wrong trade for this productA hosted component is ever introduced
Already-installed copies cannot be recalledInherent to distributed CLI software; there is no control planeNever resolvable — it is why prevention is weighted so heavily
preview.plan_json stores issue content at rest for a TTL windowRequired for a commit to execute the plan that was actually reviewed. Tension with NFR-X02 is unresolvedProduct Owner decision on amending NFR-X02 — see Database Design

Review Triggers​

  • A new provider adapter is added (Linear, Azure DevOps — Phase 2): new outbound boundary, new credential handling, new field-mapping surface.
  • A network transport is added to the MCP server: introduces inbound authentication, remote attack surface, and multi-tenancy — a different threat model, not an increment.
  • Any bypass of the confirm gate is proposed, in any form, for any reason.
  • Anything begins fetching a user-supplied URL (re-opens T-014).
  • The policy/governance layer deferred in Out of Scope is picked up.
  • A security incident occurs in a comparable tool (MCP server, Jira integration, Python CLI ecosystem).

Continuous Improvement​

  • Reviewed at least once per release cycle, and whenever a trigger above fires.
  • New features add their threats here before merge, not after.
  • Mitigation status is tracked against the P1/P2/P3 plan; P1 items block MVP sign-off per the rule in Architecture Overview.
  • Every P1 mitigation should have a test that fails without it. A mitigation with no test is an intention, not a control.

Source References​


Last Updated: 2026-09-01