Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ jobs:
run: pytest --cov=dfetch tests # Run tests
- id: behave
run: coverage run --source=dfetch --append -m behave features # Run features tests
- run: coverage report --fail-under=72 # Enforce 72% coverage gate
- run: coverage xml -o coverage.xml # Create XML report
- run: pyroma --directory --min=10 . # Check pyproject
- run: find dfetch -name "*.py" | xargs pyupgrade --py310-plus # Check syntax
Expand Down
7 changes: 5 additions & 2 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,8 @@ We will keep you updated on our progress and let you know when the issue has bee

## Acknowledgements

Thank you to everyone who helps keep Dfetch secure!
We’re grateful for responsible disclosures and contributions from the community.
We publicly credit every reporter by name in the security advisory and the
release changelog entry that ships the fix. If you prefer to remain anonymous,
let us know and we will honour that.

Thank you to everyone who helps keep Dfetch secure.
5 changes: 4 additions & 1 deletion doc/_ext/latex_tabs.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,10 @@ def apply(self, **kwargs) -> None:
else:
# Non-HTML fallback: plain container outer_nodes with [tab, panel] children.
for outer in children:
if not isinstance(outer, nodes.container) or len(outer.children) < 2:
if (
not isinstance(outer, nodes.container)
or len(outer.children) < 2
):
continue
tab_container = outer.children[0]
panel = outer.children[1]
Expand Down
45 changes: 22 additions & 23 deletions doc/explanation/compliance_track.rst
Original file line number Diff line number Diff line change
Expand Up @@ -159,12 +159,12 @@ The table below summarises dfetch's implementation of each prEN 40000-1-4 Securi
- :ref:`C-001 <c-001>`, :ref:`C-002 <c-002>`
- Integrity hash verification (:ref:`C-005 <c-005>`) is opt-in; manifest entries without an ``integrity`` field are fetched without hash verification by default
- ⚠ Partial
* -
* -
- SO.SecureStartupConfig
- —
- —
- — N/A
* -
* -
- SO.FactoryReset
- —
- —
Expand All @@ -174,17 +174,17 @@ The table below summarises dfetch's implementation of each prEN 40000-1-4 Securi
- :ref:`C-010 <c-010>`, :ref:`C-039 <c-039>`, :ref:`C-043 <c-043>`
- —
- ✓ Implemented
* -
* -
- SO.AutomaticUpdates
- —
- —
- — N/A
* -
* -
- SO.UserUpdateNotification
- —
- —
- ✓ Implemented
* -
* -
- SO.PostponeUpdates
- —
- —
Expand All @@ -194,7 +194,7 @@ The table below summarises dfetch's implementation of each prEN 40000-1-4 Securi
- :ref:`C-006 <c-006>`, :ref:`C-036 <c-036>`
- dfetch has no native authentication or authorisation layer; access control is fully delegated to the underlying VCS server and host OS. C-006 prevents interactive credential prompts, and C-036 strips credentials from persisted metadata — both are confidentiality controls, not access-control mechanisms in the authentication/authorisation sense
- ⚠ Partial
* -
* -
- SO.AccessControlReport
- :ref:`C-045 <c-045>`
- No persistent log of unauthorised access attempts
Expand All @@ -204,22 +204,22 @@ The table below summarises dfetch's implementation of each prEN 40000-1-4 Securi
- :ref:`C-036 <c-036>`
- —
- ✓ Implemented
* -
* -
- SO.DataProcessedConfidentiality
- :ref:`C-005 <c-005>`, :ref:`C-034 <c-034>`
- —
- ✓ Implemented
* -
* -
- SO.DataTransmittedConfidentiality
- :ref:`C-045 <c-045>`
- C-045 warns on plaintext-scheme URLs but does not refuse to proceed; TLS/SSH confidentiality is provided by the underlying VCS client, not enforced by dfetch itself
- ⚠ Partial
* -
* -
- SO.ComAuth
- :ref:`C-045 <c-045>`
- Server authentication (TLS certificate verification, SSH host-key checking) is delegated to the OS trust store and VCS client; dfetch does not independently authenticate remote endpoints and cannot enforce authenticated channels when C-045's warning is overridden by the user
- ⚠ Partial
* -
* -
- SO.SecureProvisioning
- :ref:`C-005 <c-005>`
- —
Expand All @@ -229,17 +229,17 @@ The table below summarises dfetch's implementation of each prEN 40000-1-4 Securi
- :ref:`C-005 <c-005>`
- Integrity hash opt-in only; not enforced by default for git/svn
- ⚠ Partial
* -
* -
- SO.DataProcessedIntegrity
- :ref:`C-005 <c-005>`, :ref:`C-034 <c-034>`
- —
- ✓ Implemented
* -
* -
- SO.DataTransmittedIntegrity
- :ref:`C-005 <c-005>`
- C-005 provides end-to-end hash verification for archive sources only (opt-in); git and svn sources rely solely on VCS object integrity (SHA-1/SHA-256 object model) and TLS/SSH channel integrity — no dfetch-level hash verification
- ⚠ Partial
* -
* -
- SO.IntegrityReport
- :ref:`C-045 <c-045>`
- No persistent integrity-violation log
Expand All @@ -254,7 +254,7 @@ The table below summarises dfetch's implementation of each prEN 40000-1-4 Securi
- —
- —
- — N/A
* -
* -
- SO.IncidentResilience
- :ref:`C-002 <c-002>`, :ref:`C-007 <c-007>`
- No timeout on VCS operations (potential resource exhaustion)
Expand All @@ -264,12 +264,12 @@ The table below summarises dfetch's implementation of each prEN 40000-1-4 Securi
- :ref:`C-001 <c-001>`, :ref:`C-007 <c-007>`
- Archive HTTP operations time out at 15 s (reachability) and 60 s (download) via ``archive.py``; git and svn subprocess calls have no timeout and can stall indefinitely
- ⚠ Partial
* -
* -
- SO.PreventAttackPropagation
- :ref:`C-001 <c-001>`, :ref:`C-008 <c-008>`
- —
- ✓ Implemented
* -
* -
- SO.MonitorExternalImpact
- —
- —
Expand All @@ -289,17 +289,17 @@ The table below summarises dfetch's implementation of each prEN 40000-1-4 Securi
- —
- No persistent structured security event log (LGM-1/2/3/4 gap). dfetch prints operational output to stderr but does not retain it, does not record which credentials were used, which files were modified, or when remote access occurred. C-036 ensures credentials are excluded from operational output but is not a logging control
- ⚠ Partial
* -
* -
- SO.MonitorSecurityRelevantActivities
- :ref:`C-045 <c-045>`
- —
- ⚠ Partial
* -
* -
- SO.OptionDisableDataLogging
- —
- —
- — N/A
* -
* -
- SO.OptionDisableDataMonitoring
- —
- —
Expand All @@ -309,17 +309,17 @@ The table below summarises dfetch's implementation of each prEN 40000-1-4 Securi
- —
- —
- ✓ Implemented
* -
* -
- SO.DataTransmittedConfidentiality
- —
- —
- — N/A
* -
* -
- SO.DataTransmittedIntegrity
- —
- —
- — N/A
* -
* -
- SO.ComAuth
- —
- —
Expand Down Expand Up @@ -438,4 +438,3 @@ Both files are regenerated with:
--component security/dfetch.component-definition.json \\
--version 0.15.0 \\
--rst > doc/explanation/compliance_track.rst
1 change: 0 additions & 1 deletion doc/explanation/control_register.rst
Original file line number Diff line number Diff line change
Expand Up @@ -222,4 +222,3 @@ All controls implemented by dfetch, sorted by ID. Risk-driven controls emerge fr
- Exploit mitigation inventory
- Compliance-only
- :doc:`compliance_track`

46 changes: 46 additions & 0 deletions doc/explanation/governance.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@

.. _governance:

Governance
==========

Decision-making
---------------

Dfetch follows a *benevolent dictator* model: the lead maintainer holds final
say on direction, design, and releases. All non-trivial decisions happen
openly in GitHub issues and pull-request discussions, and consensus is
preferred. The maintainer resolves disagreements when discussion does not
converge.

Roles and responsibilities
--------------------------

Lead maintainer
~~~~~~~~~~~~~~~

- Merges pull requests to ``main``
- Cuts releases and publishes to PyPI and GitHub Releases
- Triages issues and sets project direction
- Holds PyPI, GitHub organisation, and release-signing credentials

Contributor
~~~~~~~~~~~

- Opens issues and pull requests following the :ref:`contributing` guide
- Reviews code — anyone may review; maintainer approval gates the merge
- Abides by the project's `Code of Conduct <https://github.com/dfetch-org/dfetch/blob/main/CODE_OF_CONDUCT.md>`_

Access continuity
-----------------

Project assets are held under the **dfetch-org** GitHub organisation rather
than a personal account, so access is not tied to a single individual.

- Additional maintainers can be added through the organisation's *People*
settings without touching the codebase.
- The PyPI project supports multiple owners; co-owners can be added via the
PyPI collaborator mechanism.
- If the lead maintainer becomes unavailable, any contributor wishing to step
up should open an issue to discuss. The project is MIT-licensed, so a
community fork is always a viable path of last resort.
2 changes: 2 additions & 0 deletions doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ upstream. See :ref:`vendoring` for background on the problem this solves.
reference/glossary
reference/cli_cheatsheet
reference/changelog
reference/roadmap
reference/legal

.. only:: latex
Expand All @@ -102,6 +103,7 @@ upstream. See :ref:`vendoring` for background on the problem this solves.
explanation/alternatives
explanation/architecture
explanation/security
explanation/governance
explanation/line-endings

.. only:: latex
Expand Down
8 changes: 8 additions & 0 deletions doc/reference/roadmap.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

.. _roadmap:

Roadmap
=======

Planned work will be listed here. To influence priorities, open or upvote
issues on `GitHub <https://github.com/dfetch-org/dfetch/issues>`_.
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ relative_files = true

[tool.coverage.report]
show_missing = true
fail_under = 72

[tool.codespell]
skip = "*.cast,./venv,**/plantuml-c4/**,./example,.mypy_cache,./doc/_build/**,./doc/landing-page/_build/**,./doc/_ext/sphinxcontrib_asciinema/**,./build,*.patch,.git,**/generate-casts/demo-magic/**,./doc/openssl/**"
Expand Down
16 changes: 8 additions & 8 deletions security/compliance.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@
SO_IMPLEMENTATIONS,
STANDARDS,
TRACK_B_CONTROLS,
SOImplementation,
)
from security.compliance_types import SOImplementation
from security.tm_controls_data import SC_CONTROLS, USAGE_CONTROLS, Control

CATALOG_PATH = os.path.join(
Expand Down Expand Up @@ -201,8 +201,8 @@ def _build_so_props(so_impl: SOImplementation) -> list[dict[str, str]]:
def _build_so_description(so_impl: SOImplementation) -> str:
"""Return the statement description for one SOImplementation."""
parts = []
if so_impl.description:
parts.append(so_impl.description)
if so_impl.doc.description:
parts.append(so_impl.doc.description)
if so_impl.gaps:
parts.append("Gaps: " + "; ".join(so_impl.gaps))
if so_impl.not_applicable:
Expand All @@ -214,7 +214,7 @@ def _build_evidence_links(so_impl: SOImplementation) -> list[dict[str, str]]:
"""Return OSCAL links pointing to code or CI evidence for one SO."""
return [
{"href": href, "rel": "evidence", "text": text}
for href, text in so_impl.evidence_hrefs
for href, text in so_impl.doc.evidence_hrefs
]


Expand Down Expand Up @@ -747,15 +747,15 @@ def _render_annex_v() -> None:

def _render_impl_notes() -> None:
"""Print notes on 'Implemented' rows that have no control assigned."""
noted = [so for so in SO_IMPLEMENTATIONS if so.note]
noted = [so for so in SO_IMPLEMENTATIONS if so.doc.note]
if not noted:
return
print('.. rubric:: Notes on "Implemented" rows\n')
for so in noted:
print(so.note + "\n")
print(so.doc.note + "\n")


def render_rst(track_b_only: bool = False) -> None:
def render_rst() -> None:
"""Print the full compliance track RST document to stdout."""
print(
".. This file is auto-generated by ``python -m security.compliance --rst``.\n"
Expand Down Expand Up @@ -922,7 +922,7 @@ def render_control_register_rst(track_b_only: bool = False) -> None:
print(f"Written: {args.component}", file=sys.stderr)

if args.rst:
render_rst(track_b_only=args.track_b_only)
render_rst()

if args.control_register:
render_control_register_rst(track_b_only=args.track_b_only)
Loading
Loading