Skip to content
Open
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
15 changes: 15 additions & 0 deletions src/agentrust_trace/adapters/sandbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ class SandboxAttestation:
nonce: str | None = None

def __post_init__(self) -> None:
if not isinstance(self.platform, str):
raise ValueError(
f"SandboxAttestation.platform must be a string, got {type(self.platform).__name__}"
)
if self.platform == "software-only":
raise ValueError(
"SandboxAttestation.platform must not be 'software-only'. Omit the "
Expand All @@ -108,6 +112,11 @@ def __post_init__(self) -> None:
f"SandboxAttestation.platform {self.platform!r} is not an accepted "
f"platform. Accepted: {', '.join(sorted(_PLATFORMS))}."
)
if not isinstance(self.measurement, str):
raise ValueError(
"SandboxAttestation.measurement must be a string, got "
f"{type(self.measurement).__name__}"
)
if not _DIGEST_RE.match(self.measurement):
raise ValueError(
f"SandboxAttestation.measurement {self.measurement!r} is not a sha256: or "
Expand Down Expand Up @@ -151,12 +160,18 @@ class SandboxSessionResult:
"""Issuance timestamp. Defaults to now."""

def __post_init__(self) -> None:
if not isinstance(self.sandbox_id, str):
raise ValueError(f"sandbox_id must be a string, got {type(self.sandbox_id).__name__}")
if not _SUBJECT_RE.match(self.sandbox_id):
raise ValueError(
f"sandbox_id {self.sandbox_id!r} must be a SPIFFE URI "
"('spiffe://<trust-domain>/<path>') or a DID ('did:<method>:<id>'). "
"It becomes the record subject, which is what a verifier keys on."
)
if not isinstance(self.image_digest, str):
raise ValueError(
f"image_digest must be a string, got {type(self.image_digest).__name__}"
)
if not _DIGEST_RE.match(self.image_digest):
raise ValueError(
f"image_digest {self.image_digest!r} must be a sha256: or sha384: digest."
Expand Down
59 changes: 59 additions & 0 deletions tests/test_sandbox_required_string_fields.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
"""Regression tests for sandbox adapter required-string primitive boundaries."""

from __future__ import annotations

from typing import Any

import pytest

from agentrust_trace.adapters.sandbox import SandboxAttestation, SandboxSessionResult

DIGEST = "sha256:" + "a" * 64
BAD_VALUES: tuple[Any, ...] = (True, 1, [1], {"x": 1})


@pytest.mark.parametrize("platform", BAD_VALUES, ids=repr)
def test_attestation_platform_refuses_non_string_values(platform: Any) -> None:
with pytest.raises(ValueError, match="SandboxAttestation.platform"):
SandboxAttestation(platform=platform, measurement=DIGEST)


@pytest.mark.parametrize("measurement", BAD_VALUES, ids=repr)
def test_attestation_measurement_refuses_non_string_values(measurement: Any) -> None:
with pytest.raises(ValueError, match="SandboxAttestation.measurement"):
SandboxAttestation(platform="tpm2", measurement=measurement)


@pytest.mark.parametrize("sandbox_id", BAD_VALUES, ids=repr)
def test_session_sandbox_id_refuses_non_string_values(sandbox_id: Any) -> None:
with pytest.raises(ValueError, match="sandbox_id"):
SandboxSessionResult(
sandbox_id=sandbox_id,
image_digest=DIGEST,
policy_bundle_bytes=b"policy",
decisions=[],
)


@pytest.mark.parametrize("image_digest", BAD_VALUES, ids=repr)
def test_session_image_digest_refuses_non_string_values(image_digest: Any) -> None:
with pytest.raises(ValueError, match="image_digest"):
SandboxSessionResult(
sandbox_id="spiffe://runtime.example.org/sandbox/test",
image_digest=image_digest,
policy_bundle_bytes=b"policy",
decisions=[],
)


def test_textual_controls_still_construct() -> None:
attestation = SandboxAttestation(platform="tpm2", measurement=DIGEST)
session = SandboxSessionResult(
sandbox_id="spiffe://runtime.example.org/sandbox/test",
image_digest=DIGEST,
policy_bundle_bytes=b"policy",
decisions=[],
attestation=attestation,
)
assert attestation.platform == "tpm2"
assert session.sandbox_id.startswith("spiffe://")
Loading