You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs: verify threat models for completeness and fix silent gaps
Cross-checking the threat catalog, responses, controls, and generated
reports against each other and the codebase surfaced several gaps:
- DFT-06 (command injection) and DFT-34 (long-lived stored credential)
had documented responses and mitigating controls (C-006/C-007,
C-015/C-017, C-042) but never appeared in any rendered threat table:
DFT-06's condition no longer matches once parameterized subprocess
input is modelled, and pytm's resolve() never evaluates threats
against Data assets (it iterates TM._elements only). Render such
responses from the threat catalog so every documented risk decision
stays visible, and fail generation when a finding has no response or
a response names an unknown threat ID.
- Asset IDs A-09/A-10 meant different assets in the two models (Remote
VCS Server / Archive HTTP Server in the usage model, Winget repo /
WINGET_TOKEN PAT in the supply-chain model). Renumber the Winget
assets to A-28/A-29 so every asset ID is unique across the doc set.
- Gap control IDs C-018/C-019/C-020/C-035 cited by the usage model were
defined nowhere; add an 'Identified Gaps' section to the security
page so each reserved ID resolves to a description.
- Fix the SBOM cross-reference in the usage model (A-04 -> A-03) and
the stale OSCAL version (1.1.2 -> 1.2.2) in security.rst and
security/README.md.
- Regenerate both threat-model reports (also picks up the stale
C-045/C-036 control-row ordering in the usage report).
Copy file name to clipboardExpand all lines: doc/explanation/threat_model_supply_chain.rst
+27-11Lines changed: 27 additions & 11 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -82,7 +82,7 @@ Boundaries
82
82
- Python Package Index and its staging registry. dfetch publishes via OIDC trusted publishing - no long-lived API token stored.
83
83
84
84
* - Winget Community Repository
85
-
- The Windows Package Manager Community Repository (https://github.com/microsoft/winget-pkgs) where dfetch's Winget manifest is hosted. Manifest PRs are submitted automatically by the CI release pipeline (winget-publish.yml) using the stored WINGET_TOKEN PAT (A-10). Consumer installations resolve manifests from this repository; winget downloads the MSI installer from the URL declared in the manifest (pointing to GitHub Releases, A-01) and verifies its SHA256 hash.
85
+
- The Windows Package Manager Community Repository (https://github.com/microsoft/winget-pkgs) where dfetch's Winget manifest is hosted. Manifest PRs are submitted automatically by the CI release pipeline (winget-publish.yml) using the stored WINGET_TOKEN PAT (A-29). Consumer installations resolve manifests from this repository; winget downloads the MSI installer from the URL declared in the manifest (pointing to GitHub Releases, A-01) and verifies its SHA256 hash.
- CI/CD pipelines: test, build (wheel/msi/deb/rpm), lint, CodeQL, Scorecard, dependency-review, docs, release, winget-publish. All actions pinned by commit SHA. harden-runner used in every workflow that executes steps on a runner (egress: block with endpoint allowlist); ci.yml is a dispatcher-only workflow with no runner steps and does not include harden-runner. winget-publish.yml uses a stored PAT (WINGET_TOKEN, A-10) to submit manifest PRs to the Winget Community Repository (A-09).
557
+
- CI/CD pipelines: test, build (wheel/msi/deb/rpm), lint, CodeQL, Scorecard, dependency-review, docs, release, winget-publish. All actions pinned by commit SHA. harden-runner used in every workflow that executes steps on a runner (egress: block with endpoint allowlist); ci.yml is a dispatcher-only workflow with no runner steps and does not include harden-runner. winget-publish.yml uses a stored PAT (WINGET_TOKEN, A-29) to submit manifest PRs to the Winget Community Repository (A-28).
558
558
- Process
559
559
- Medium / Medium / Medium
560
560
* - A-07: dfetch Build / Dev Dependencies
@@ -569,11 +569,11 @@ Asset Identification
569
569
- GitHub Actions cache entries written and restored across pipeline runs. Used to speed up dependency installation (pip, gem) and incremental builds. Cache-poisoning from forked PRs (DFT-28, SLSA E6: poison the build cache) is mitigated by ref-scoped cache keys: build.yml includes ``${{ github.ref_name }}`` in both ``key`` and ``restore-keys`` (C-033), which isolates PR and release caches per branch so a fork cannot write into the release cache namespace.
570
570
- Datastore
571
571
- High / High / —
572
-
* - A-09: Winget Community Repository (microsoft/winget-pkgs)
573
-
- The Windows Package Manager Community Repository where the dfetch ``DFetch-org.DFetch`` manifest is hosted (https://github.com/microsoft/winget-pkgs). CI submits manifest update PRs via ``vedantmgoyal9/winget-releaser`` using a stored PAT (A-10); PRs are reviewed by ``microsoft/winget-pkgs`` maintainers before merging (C-041). Manifests contain SHA256 hashes of the installer binary; winget verifies the hash before installation. A compromised PAT or a fraudulent PR that passes review could redirect consumers to a malicious installer (DFT-35).
572
+
* - A-28: Winget Community Repository (microsoft/winget-pkgs)
573
+
- The Windows Package Manager Community Repository where the dfetch ``DFetch-org.DFetch`` manifest is hosted (https://github.com/microsoft/winget-pkgs). CI submits manifest update PRs via ``vedantmgoyal9/winget-releaser`` using a stored PAT (A-29); PRs are reviewed by ``microsoft/winget-pkgs`` maintainers before merging (C-041). Manifests contain SHA256 hashes of the installer binary; winget verifies the hash before installation. A compromised PAT or a fraudulent PR that passes review could redirect consumers to a malicious installer (DFT-35).
574
574
- ExternalEntity
575
575
- High / High / —
576
-
* - A-10: WINGET_TOKEN PAT
576
+
* - A-29: WINGET_TOKEN PAT
577
577
- Long-lived classic GitHub Personal Access Token (fine-grained PATs are not supported by the ``vedantmgoyal9/winget-releaser`` action) with ``public_repo`` scope, stored as a GitHub Actions environment secret in the ``winget`` environment. Used by ``winget-publish.yml`` to push a manifest branch to the pre-existing fork ``dfetch-org/winget-pkgs`` and open a PR against ``microsoft/winget-pkgs``. Unlike the PyPI OIDC token (A-05) which is short-lived and not stored, this PAT persists indefinitely until rotated. If exfiltrated from the CI environment, an attacker could submit fraudulent manifest PRs from outside the project's pipeline.
578
578
- Data
579
579
- High / High / —
@@ -677,16 +677,16 @@ Dataflows
677
677
678
678
* - DF-27: Winget manifest PR submission
679
679
- A-02: GitHub Actions Infrastructure
680
-
- A-09: Winget Community Repository (microsoft/winget-pkgs)
680
+
- A-28: Winget Community Repository (microsoft/winget-pkgs)
681
681
- HTTPS
682
682
683
683
* - DF-28: winget install dfetch
684
684
- Consumer / End User
685
-
- A-09: Winget Community Repository (microsoft/winget-pkgs)
685
+
- A-28: Winget Community Repository (microsoft/winget-pkgs)
686
686
- HTTPS
687
687
688
688
* - DF-29: Consumer downloads MSI via winget
689
-
- A-09: Winget Community Repository (microsoft/winget-pkgs)
689
+
- A-28: Winget Community Repository (microsoft/winget-pkgs)
690
690
- Consumer / End User
691
691
- HTTPS
692
692
@@ -728,6 +728,14 @@ Threats
728
728
|**STRIDE:** T S
729
729
|**Status:** Mitigate
730
730
- C-021
731
+
* - DFT-06
732
+
- Command injection via unsanitised subprocess input
733
+
- A-01: GitHub Repository (main / protected)
734
+
- | **Sev:** 🟠H
735
+
|**Risk:** 🟠H
736
+
|**STRIDE:** T E
737
+
|**Status:** Mitigate
738
+
- C-015 (CodeQL) and C-017 (bandit) perform static analysis that detects command injection patterns before code reaches production.
731
739
* - DFT-07
732
740
- CI/CD secret exfiltration via supply-chain attack on build environment
733
741
- A-08: Python Build (wheel / sdist)
@@ -856,9 +864,17 @@ Threats
856
864
|**STRIDE:** T
857
865
|**Status:** Mitigate
858
866
- C-038
867
+
* - DFT-34
868
+
- Long-lived stored credential enables persistent unauthorised publication after exfiltration
Copy file name to clipboardExpand all lines: doc/explanation/threat_model_usage.rst
+13-5Lines changed: 13 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -679,7 +679,7 @@ Asset Identification
679
679
- Datastore
680
680
- Critical / Critical / High
681
681
* - A-15: SBOM Output (CycloneDX)
682
-
- CycloneDX JSON/XML produced by ``dfetch report -t sbom``. Enumerates vendored components with PURL, license, and hash. Falsification hides actual dependencies from downstream CVE scanners. NOTE: this SBOM covers vendored deps only - dfetch itself has a separate machine-readable SBOM published on PyPI (see A-04 in tm_supply_chain.py).
682
+
- CycloneDX JSON/XML produced by ``dfetch report -t sbom``. Enumerates vendored components with PURL, license, and hash. Falsification hides actual dependencies from downstream CVE scanners. NOTE: this SBOM covers vendored deps only - dfetch itself has a separate machine-readable SBOM published on PyPI (see A-03 in tm_supply_chain.py).
683
683
- Datastore
684
684
- High / High / —
685
685
* - A-16: VCS Credentials
@@ -952,6 +952,14 @@ Threats
952
952
|**STRIDE:** T S
953
953
|**Status:** Mitigate
954
954
- C-005 mitigates archive deps when hash present. Git/SVN: no integrity mechanism; pinning to an immutable commit SHA is recommended but not enforced by dfetch.
955
+
* - DFT-06
956
+
- Command injection via unsanitised subprocess input
957
+
- A-22: dfetch Process
958
+
- | **Sev:** 🟠H
959
+
|**Risk:** 🟠H
960
+
|**STRIDE:** T E
961
+
|**Status:** Mitigate
962
+
- C-006 suppresses interactive credential prompts for Git and SVN; C-007 invokes all external commands with ``shell=False`` and list-form arguments, eliminating the shell-injection vector for non-interactive VCS operations.
955
963
* - DFT-07
956
964
- CI/CD secret exfiltration via supply-chain attack on build environment
957
965
- A-25: Patch Application (patch-ng)
@@ -1190,10 +1198,6 @@ Controls
1190
1198
- Manifest input validation
1191
1199
- DFT-04, DFT-08
1192
1200
- StrictYAML schema with ``SAFE_STR = Regex(r"^[^\x00-\x1F\x7F-\x9F]*$")`` rejects control characters in all string fields. ``dfetch/manifest/schema.py``
1193
-
* - C-045
1194
-
- Plaintext transport detection
1195
-
- DFT-26
1196
-
- ``plaintext_warning()`` (``dfetch/manifest/project.py``) inspects the resolved remote URL immediately before each VCS command is issued (inside the ``check_for_update`` and ``update`` spinners in ``subproject.py``). If the scheme is ``http://``, ``git://``, or ``svn://``, a visible warning is emitted naming the redacted URL (credentials stripped from the userinfo component) and recommending ``https://`` or ``svn+ssh://``. Detection only — dfetch still proceeds with the plaintext connection; the control raises user awareness but does not enforce scheme selection. ``dfetch/manifest/project.py, dfetch/project/subproject.py``
1197
1201
* - C-034
1198
1202
- Hash algorithm allowlist (SHA-256/384/512 only)
1199
1203
- DFT-30
@@ -1202,3 +1206,7 @@ Controls
1202
1206
- Persisted-metadata credential redaction
1203
1207
- DFT-13
1204
1208
- ``Metadata.dump()`` rebuilds the netloc of every persisted URL from ``parsed.hostname`` and ``parsed.port`` via ``urllib.parse.urlsplit`` / ``urlunsplit``, dropping any ``user:password@`` userinfo before writing ``.dfetch_data.yaml``. The same stripper is applied to each ``dependencies[].remote_url`` entry (git submodule, svn:external) so a credential in a nested upstream URL also never reaches disk. The in-memory ``Metadata`` object held by the running command keeps the original URL — only the on-disk representation is redacted, so an in-flight authenticated fetch is unaffected. ``dfetch/project/metadata.py``
1209
+
* - C-045
1210
+
- Plaintext transport detection
1211
+
- DFT-26
1212
+
- ``plaintext_warning()`` (``dfetch/manifest/project.py``) inspects the resolved remote URL immediately before each VCS command is issued (inside the ``check_for_update`` and ``update`` spinners in ``subproject.py``). If the scheme is ``http://``, ``git://``, or ``svn://``, a visible warning is emitted naming the redacted URL (credentials stripped from the userinfo component) and recommending ``https://`` or ``svn+ssh://``. Detection only — dfetch still proceeds with the plaintext connection; the control raises user awareness but does not enforce scheme selection. ``dfetch/manifest/project.py, dfetch/project/subproject.py``
0 commit comments