diff --git a/enroll/manifest.py b/enroll/manifest.py index 0d97330..b607020 100644 --- a/enroll/manifest.py +++ b/enroll/manifest.py @@ -2,6 +2,7 @@ from __future__ import annotations import os import shutil +import sys import tarfile import tempfile from pathlib import Path @@ -19,6 +20,16 @@ from .sopsutil import ( from .validate import validate_harvest +_SEMANTIC_SAFETY_WARNING = ( + "This harvest is structurally valid, but Enroll cannot prove it is semantically safe.\n" + "Only apply manifests generated from harvests whose provenance you trust.\n" +) + + +def _warn_semantic_safety() -> None: + sys.stderr.write(_SEMANTIC_SAFETY_WARNING) + + def _prepare_bundle_dir( bundle: str, *, @@ -217,6 +228,8 @@ def manifest( + validation.to_text().strip() ) + _warn_semantic_safety() + if not sops_mode: manifest_ansible_from_bundle_dir( resolved_bundle_dir, diff --git a/tests/test_manifest.py b/tests/test_manifest.py index dbd7633..e72ee2e 100644 --- a/tests/test_manifest.py +++ b/tests/test_manifest.py @@ -87,6 +87,24 @@ def _write_state(bundle: Path, state: dict) -> None: write_schema_state(bundle, state) +def test_manifest_warns_that_validation_is_not_semantic_safety(tmp_path: Path, capsys): + bundle = tmp_path / "bundle" + out = tmp_path / "ansible" + _write_state(bundle, _minimal_package_state([])) + + manifest.manifest(str(bundle), str(out)) + + captured = capsys.readouterr() + assert ( + "This harvest is structurally valid, but Enroll cannot prove it is semantically safe." + in captured.err + ) + assert ( + "Only apply manifests generated from harvests whose provenance you trust." + in captured.err + ) + + def test_manifest_writes_roles_and_playbook_with_clean_when(tmp_path: Path): bundle = tmp_path / "bundle" out = tmp_path / "ansible"