Skip to content

Commit c5d1217

Browse files
committed
Review comments
1 parent 562b339 commit c5d1217

3 files changed

Lines changed: 48 additions & 21 deletions

File tree

.devcontainer/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ ENV PYTHONUSERBASE="/home/dev/.local"
5353
COPY --chown=dev:dev . .
5454

5555
RUN pip install --no-cache-dir --root-user-action=ignore --upgrade pip==26.0.1 \
56-
&& pip install --no-cache-dir --root-user-action=ignore -e .[development,docs,test,casts,build] \
56+
&& pip install --no-cache-dir --root-user-action=ignore -e .[development,docs,test,casts,build,security] \
5757
&& pre-commit install --install-hooks
5858

5959
# Set bash as the default shell

doc/explanation/security.rst

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ The model is aligned with the `Cyber Resilience Act (CRA)`_ and the
1414
classification methodology.
1515

1616
.. _`Cyber Resilience Act (CRA)`: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:32024R2847
17-
.. _`EN 18031`: https://www.etsi.org/deliver/etsi_en/18031_18031_18031_18031/
17+
.. _`EN 18031`: https://www.cenelec.eu/
1818

1919
The threat model is maintained as executable code in ``security/threat_model.py``
2020
using the `pytm`_ framework. Regenerate analysis output with:
@@ -25,6 +25,14 @@ using the `pytm`_ framework. Regenerate analysis output with:
2525
python -m security.threat_model --dfd # Graphviz DFD (stdout)
2626
python -m security.threat_model --report # STRIDE findings report
2727
28+
.. note::
29+
30+
The ``--seq`` and ``--dfd`` commands require **PlantUML** and **Graphviz**
31+
to be installed on the system. These are not Python packages and are not
32+
installed by ``pip install .[security]``. Install them separately (e.g.
33+
``apt install plantuml graphviz`` or via your platform package manager)
34+
before running the diagram commands.
35+
2836
.. _pytm: https://github.com/izar/pytm
2937

3038
.. note::
@@ -202,7 +210,7 @@ Supporting Assets
202210
* - SA-06
203211
- GitHub Actions Workflows
204212
- Restricted
205-
- ``/.github/workflows/*.yml`` — 11 CI/CD pipeline definitions checked into
213+
- ``.github/workflows/*.yml`` — 11 CI/CD pipeline definitions checked into
206214
the repository. A malicious pull request modifying workflows can
207215
exfiltrate secrets or publish a backdoored release. Mitigated by
208216
SHA-pinned actions and ``persist-credentials: false``. Risk: ``secrets:
@@ -407,7 +415,7 @@ The following controls are already in place and are reflected in the
407415
- PA-02, SA-05
408416
- ``check_no_path_traversal()`` uses ``os.path.realpath`` to reject any
409417
path that escapes the destination root.
410-
``dfetch/util/util.py:265–285``
418+
``check_no_path_traversal()`` in ``dfetch/util/util.py``
411419
* - Decompression-bomb protection
412420
- SA-05, PA-02
413421
- Archives are rejected if uncompressed size exceeds 500 MB or the member
@@ -473,7 +481,9 @@ The following controls are already in place and are reflected in the
473481
* - OpenSSF Scorecard
474482
- EA-03, SA-10
475483
- Weekly OSSF Scorecard analysis uploaded to GitHub Code Scanning covers
476-
17 supply-chain health checks.
484+
the full set of OpenSSF Scorecard checks (see the
485+
`OpenSSF Scorecard project <https://github.com/ossf/scorecard>`_ for
486+
the authoritative list).
477487
``.github/workflows/scorecard.yml``
478488
* - CodeQL static analysis
479489
- SA-01, SA-06

security/threat_model.py

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
Datastore,
2020
ExternalEntity,
2121
Process,
22-
Server,
2322
)
2423

2524
# ── Threat model metadata ────────────────────────────────────────────────────
@@ -134,12 +133,14 @@
134133
"Invokes Git and SVN as subprocesses (shell=False, list args). "
135134
"Extracts archives with decompression-bomb limits and path-traversal checks."
136135
)
137-
dfetch_cli.controls.validatesInput = True # StrictYAML + SAFE_STR regex
138-
dfetch_cli.controls.sanitizesInput = True # check_no_path_traversal realpath-based
139-
dfetch_cli.controls.usesParameterizedInput = True # shell=False, list-based subprocesses
140-
dfetch_cli.controls.checksInputBounds = True # 500MB / 10k-member archive limits
141-
dfetch_cli.controls.isHardened = True # BatchMode=yes, --non-interactive, type checks
142-
dfetch_cli.controls.providesIntegrity = True # hmac.compare_digest SHA-256/384/512
136+
dfetch_cli.controls.validatesInput = True # StrictYAML + SAFE_STR regex
137+
dfetch_cli.controls.sanitizesInput = True # check_no_path_traversal realpath-based
138+
dfetch_cli.controls.usesParameterizedInput = (
139+
True # shell=False, list-based subprocesses
140+
)
141+
dfetch_cli.controls.checksInputBounds = True # 500MB / 10k-member archive limits
142+
dfetch_cli.controls.isHardened = True # BatchMode=yes, --non-interactive, type checks
143+
dfetch_cli.controls.providesIntegrity = True # hmac.compare_digest SHA-256/384/512
143144

144145
gh_actions_workflow = Process("GitHub Actions Workflow")
145146
gh_actions_workflow.inBoundary = boundary_github
@@ -149,8 +150,12 @@
149150
"All actions pinned by commit SHA. "
150151
"harden-runner used in every workflow (egress: audit only)."
151152
)
152-
gh_actions_workflow.controls.isHardened = True # SHA-pinned actions, persist-credentials:false
153-
gh_actions_workflow.controls.providesIntegrity = True # CodeQL + Scorecard + dependency-review
153+
gh_actions_workflow.controls.isHardened = (
154+
True # SHA-pinned actions, persist-credentials:false
155+
)
156+
gh_actions_workflow.controls.providesIntegrity = (
157+
True # CodeQL + Scorecard + dependency-review
158+
)
154159
gh_actions_workflow.controls.hasAccessControl = True # minimal permissions per workflow
155160

156161
python_build = Process("Python Build (wheel / sdist)")
@@ -182,7 +187,7 @@
182187
manifest_store.isSQL = False
183188
manifest_store.classification = Classification.SENSITIVE
184189
manifest_store.controls.isEncryptedAtRest = False
185-
manifest_store.controls.validatesInput = True # StrictYAML validation on read
190+
manifest_store.controls.validatesInput = True # StrictYAML validation on read
186191

187192
fetched_source = Datastore("PA-02: Fetched Source Code")
188193
fetched_source.inBoundary = boundary_dev_env
@@ -209,7 +214,9 @@
209214
"authenticity relies entirely on transport security (TLS/SSH)."
210215
)
211216
integrity_hash_record.storesSensitiveData = False
212-
integrity_hash_record.hasWriteAccess = False
217+
integrity_hash_record.hasWriteAccess = (
218+
True # developers and processes write this field in dfetch.yaml
219+
)
213220
integrity_hash_record.classification = Classification.SENSITIVE
214221
integrity_hash_record.controls.providesIntegrity = True
215222

@@ -222,7 +229,9 @@
222229
"Compromise of the PyPI account or registry affects every consumer."
223230
)
224231
pypi_package.storesSensitiveData = False
225-
pypi_package.hasWriteAccess = False
232+
pypi_package.hasWriteAccess = (
233+
True # publish pipeline writes new releases to this package
234+
)
226235
pypi_package.classification = Classification.SENSITIVE
227236
pypi_package.controls.usesCodeSigning = False # no Sigstore/cosign signing
228237

@@ -244,6 +253,11 @@
244253
# SA-01 … SA-10: assets that enable primary assets; their loss degrades
245254
# security or availability of primary assets.
246255

256+
# SA-01: dfetch Process — modelled as dfetch_cli (Process above)
257+
# The running Python interpreter dispatching all commands. Subprocess
258+
# injection or dependency confusion could compromise build-time execution.
259+
# Classification: Restricted.
260+
247261
vcs_credentials = Data(
248262
"SA-02: VCS Credentials",
249263
description=(
@@ -382,6 +396,9 @@
382396
# EA-03: GitHub Repository — modelled as gh_repository (ExternalEntity above)
383397
# EA-04: GitHub Actions Infrastructure — modelled as gh_actions_runner (ExternalEntity above)
384398
# EA-05: PyPI / TestPyPI — modelled as pypi (ExternalEntity above)
399+
# EA-06: Developer Workstation / CI Runner — modelled as developer / gh_actions_runner (Actors/ExternalEntity above)
400+
# EA-07: Consumer Build System — modelled as consumer_build (ExternalEntity above)
401+
# EA-08: Network Transport — modelled as boundary_network (Boundary above); symbol: network_transport
385402

386403
# ── DATA FLOWS ───────────────────────────────────────────────────────────────
387404
#
@@ -402,8 +419,8 @@
402419
"no protocol enforcement in manifest schema."
403420
)
404421
df03.protocol = "HTTPS / SSH / svn"
405-
df03.controls.isEncrypted = True # for HTTPS/SSH paths; NOT for svn:// or http://
406-
df03.controls.isHardened = True # BatchMode=yes, --non-interactive
422+
df03.controls.isEncrypted = True # for HTTPS/SSH paths; NOT for svn:// or http://
423+
df03.controls.isHardened = True # BatchMode=yes, --non-interactive
407424

408425
df04 = Dataflow(remote_git_svn, dfetch_cli, "DF-04: VCS content (inbound, untrusted)")
409426
df04.description = (
@@ -431,7 +448,7 @@
431448
)
432449
df06.protocol = "HTTP or HTTPS"
433450
df06.controls.providesIntegrity = False # hash is optional; may be absent
434-
df06.controls.isEncrypted = False # http:// allowed
451+
df06.controls.isEncrypted = False # http:// allowed
435452

436453
df07 = Dataflow(dfetch_cli, fetched_source, "DF-07: Write vendored files")
437454
df07.description = (
@@ -453,7 +470,7 @@
453470
"RISK: patch files are not integrity-verified (no hash in manifest schema). "
454471
"patch-ng path safety depends on its own internal implementation."
455472
)
456-
df10.controls.validatesInput = False # no integrity hash on patch files
473+
df10.controls.validatesInput = False # no integrity hash on patch files
457474

458475
df11 = Dataflow(contributor, gh_repository, "DF-11: Submit pull request")
459476
df11.description = (

0 commit comments

Comments
 (0)