Fix for remote harvest tmp dir
This commit is contained in:
parent
21a3ef3447
commit
d93de8a8a2
7 changed files with 596 additions and 9 deletions
126
tests/test_manifest_safety.py
Normal file
126
tests/test_manifest_safety.py
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from enroll.manifest_safety import (
|
||||
ArtifactSafetyError,
|
||||
ManifestOutputError,
|
||||
copy_safe_artifact_file,
|
||||
iter_safe_artifact_files,
|
||||
prepare_manifest_output_dir,
|
||||
safe_artifact_file,
|
||||
validate_site_fqdn,
|
||||
)
|
||||
|
||||
|
||||
def test_validate_site_fqdn_accepts_and_normalises_simple_values():
|
||||
assert validate_site_fqdn(None) is None
|
||||
assert validate_site_fqdn(" ") is None
|
||||
assert validate_site_fqdn("host_1.example") == "host_1.example"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value", ["../host", "host/name", "host\\name", "host\nname", "-bad", ".", ".."]
|
||||
)
|
||||
def test_validate_site_fqdn_rejects_path_or_inventory_injection(value: str):
|
||||
with pytest.raises(ManifestOutputError):
|
||||
validate_site_fqdn(value)
|
||||
|
||||
|
||||
def test_prepare_manifest_output_dir_allows_existing_clean_tree_in_site_mode(
|
||||
tmp_path: Path,
|
||||
):
|
||||
out = tmp_path / "site"
|
||||
out.mkdir()
|
||||
(out / ".git").mkdir()
|
||||
(out / ".git" / "ignored-link").symlink_to(tmp_path, target_is_directory=True)
|
||||
|
||||
assert prepare_manifest_output_dir(out, allow_existing=True) == out
|
||||
|
||||
|
||||
def test_prepare_manifest_output_dir_rejects_existing_tree_symlink(tmp_path: Path):
|
||||
out = tmp_path / "site"
|
||||
out.mkdir()
|
||||
(out / "bad-link").symlink_to(tmp_path, target_is_directory=True)
|
||||
|
||||
with pytest.raises(ManifestOutputError, match="contains a symlink"):
|
||||
prepare_manifest_output_dir(out, allow_existing=True)
|
||||
|
||||
|
||||
def test_safe_artifact_file_accepts_regular_file_and_copy(tmp_path: Path):
|
||||
bundle = tmp_path / "bundle"
|
||||
artifact = bundle / "artifacts" / "role" / "etc" / "app.conf"
|
||||
artifact.parent.mkdir(parents=True)
|
||||
artifact.write_text("managed=true\n", encoding="utf-8")
|
||||
|
||||
assert safe_artifact_file(bundle, "role", "etc/app.conf") == artifact
|
||||
|
||||
dst = tmp_path / "copy.conf"
|
||||
copy_safe_artifact_file(artifact, dst)
|
||||
assert dst.read_text(encoding="utf-8") == "managed=true\n"
|
||||
|
||||
|
||||
def test_safe_artifact_file_rejects_unsafe_role_and_src(tmp_path: Path):
|
||||
bundle = tmp_path / "bundle"
|
||||
with pytest.raises(ArtifactSafetyError, match="must be relative"):
|
||||
safe_artifact_file(bundle, "/role", "file")
|
||||
with pytest.raises(ArtifactSafetyError, match="unsafe path component"):
|
||||
safe_artifact_file(bundle, "role", "../file")
|
||||
with pytest.raises(ArtifactSafetyError, match="NUL"):
|
||||
safe_artifact_file(bundle, "role", "bad\x00file")
|
||||
|
||||
|
||||
def test_safe_artifact_file_rejects_artifacts_symlink(tmp_path: Path):
|
||||
bundle = tmp_path / "bundle"
|
||||
bundle.mkdir()
|
||||
(bundle / "artifacts").symlink_to(tmp_path, target_is_directory=True)
|
||||
|
||||
with pytest.raises(ArtifactSafetyError, match="artifacts directory is a symlink"):
|
||||
safe_artifact_file(bundle, "role", "file")
|
||||
|
||||
|
||||
def test_safe_artifact_file_rejects_bad_artifact_kinds(tmp_path: Path):
|
||||
bundle = tmp_path / "bundle"
|
||||
role_dir = bundle / "artifacts" / "role"
|
||||
role_dir.mkdir(parents=True)
|
||||
|
||||
target = role_dir / "target"
|
||||
target.write_text("x", encoding="utf-8")
|
||||
(role_dir / "link").symlink_to(target)
|
||||
with pytest.raises(ArtifactSafetyError, match="symlink"):
|
||||
safe_artifact_file(bundle, "role", "link")
|
||||
|
||||
(role_dir / "dir-artifact").mkdir()
|
||||
with pytest.raises(ArtifactSafetyError, match="not a regular file"):
|
||||
safe_artifact_file(bundle, "role", "dir-artifact")
|
||||
|
||||
hardlink = role_dir / "hardlink"
|
||||
os.link(target, hardlink)
|
||||
with pytest.raises(ArtifactSafetyError, match="hardlinked"):
|
||||
safe_artifact_file(bundle, "role", "target")
|
||||
|
||||
|
||||
def test_iter_safe_artifact_files_handles_missing_and_bad_role_dirs(tmp_path: Path):
|
||||
bundle = tmp_path / "bundle"
|
||||
assert list(iter_safe_artifact_files(bundle, "missing")) == []
|
||||
|
||||
role_file = bundle / "artifacts" / "role"
|
||||
role_file.parent.mkdir(parents=True)
|
||||
role_file.write_text("not a dir", encoding="utf-8")
|
||||
with pytest.raises(ArtifactSafetyError, match="not a directory"):
|
||||
list(iter_safe_artifact_files(bundle, "role"))
|
||||
|
||||
|
||||
def test_iter_safe_artifact_files_rejects_symlink_subdir(tmp_path: Path):
|
||||
bundle = tmp_path / "bundle"
|
||||
role_dir = bundle / "artifacts" / "role"
|
||||
role_dir.mkdir(parents=True)
|
||||
real = tmp_path / "real"
|
||||
real.mkdir()
|
||||
(role_dir / "linkdir").symlink_to(real, target_is_directory=True)
|
||||
|
||||
with pytest.raises(ArtifactSafetyError, match="directory is a symlink"):
|
||||
list(iter_safe_artifact_files(bundle, "role"))
|
||||
Reference in a new issue