42 lines
1.6 KiB
Python
42 lines
1.6 KiB
Python
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from aios_autonomous_release_candidate.artifact import ArtifactBuilder, ArtifactFile, BuiltArtifact
|
|
from aios_autonomous_release_candidate.errors import EvidenceIncomplete, ReleaseBlocked
|
|
|
|
|
|
def test_artifact_is_deterministic_and_reverified() -> None:
|
|
builder = ArtifactBuilder()
|
|
files = (
|
|
ArtifactFile("bin/run", b"#!/bin/sh\nexit 0\n", 0o755),
|
|
ArtifactFile("config/candidate.json", b'{"production":false}\n'),
|
|
)
|
|
first = builder.build("a" * 40, "b" * 40, files)
|
|
second = builder.build("a" * 40, "b" * 40, tuple(reversed(files)))
|
|
assert first.content == second.content
|
|
assert first.manifest.manifest_sha256 == second.manifest.manifest_sha256
|
|
assert first.manifest.secrets_included is False
|
|
builder.verify(first)
|
|
with pytest.raises(EvidenceIncomplete, match="ARTIFACT_DIGEST_MISMATCH"):
|
|
builder.verify(BuiltArtifact(first.content + b"tampered", first.manifest))
|
|
|
|
|
|
def test_source_tree_rejects_symlink_and_hardlink(tmp_path: Path) -> None:
|
|
root = tmp_path / "source"
|
|
root.mkdir()
|
|
target = root / "file.txt"
|
|
target.write_text("safe", encoding="utf-8")
|
|
(root / "link.txt").symlink_to(target)
|
|
with pytest.raises(ReleaseBlocked, match="ARTIFACT_SOURCE_LINK_REJECTED"):
|
|
ArtifactBuilder().build_from_directory("a" * 40, "b" * 40, root)
|
|
|
|
|
|
def test_artifact_rejects_secret_markers() -> None:
|
|
with pytest.raises(ReleaseBlocked, match="ARTIFACT_SECRET_MARKER"):
|
|
ArtifactBuilder().build(
|
|
"a" * 40,
|
|
"b" * 40,
|
|
(ArtifactFile("unsafe.env", b"password" + b"=" + b"x" * 16),),
|
|
)
|