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
2 changes: 1 addition & 1 deletion src/trace_tests/modules/tr_pol.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ def check(
))

enforcement = policy.get("enforcement_mode")
if enforcement in _VALID_ENFORCEMENT:
if isinstance(enforcement, str) and enforcement in _VALID_ENFORCEMENT:
findings.append(Finding("TR-POL-002", Status.PASS, f"policy.enforcement_mode is valid ({enforcement!r})"))
else:
findings.append(Finding(
Expand Down
4 changes: 2 additions & 2 deletions src/trace_tests/modules/tr_rte.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def check(
]

platform = runtime.get("platform")
if platform in _DEV_PLATFORMS:
if isinstance(platform, str) and platform in _DEV_PLATFORMS:
if level == 0:
findings.append(
Finding("TR-RTE-001", Status.PASS, f"runtime.platform is registered ({platform!r})")
Expand All @@ -63,7 +63,7 @@ def check(
f"hardware-attested levels (Level {level} requires a hardware TEE platform)",
)
)
elif platform in _VALID_PLATFORMS:
elif isinstance(platform, str) and platform in _VALID_PLATFORMS:
findings.append(
Finding("TR-RTE-001", Status.PASS, f"runtime.platform is registered ({platform!r})")
)
Expand Down
9 changes: 8 additions & 1 deletion src/trace_tests/modules/tr_sca.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,14 @@ def check(trace: dict[str, Any]) -> list[Finding]:
return [Finding("TR-SCA-001", Status.FAIL, "TR-SCA-001: build_provenance must be an object")]

slsa_level = prov.get("slsa_level")
if slsa_level in _SLSA_LEVELS:
# JSON booleans are not integers, although Python makes bool an int subclass.
# JSON Schema does admit 1.0 as an integer, so numeric membership remains valid
# after booleans are excluded explicitly.
if (
isinstance(slsa_level, (int, float))
and not isinstance(slsa_level, bool)
and slsa_level in _SLSA_LEVELS
):
findings.append(Finding("TR-SCA-001", Status.PASS, f"build_provenance.slsa_level is valid ({slsa_level})"))
else:
findings.append(Finding(
Expand Down
2 changes: 1 addition & 1 deletion src/trace_tests/modules/tr_sig.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ def check(trace: dict[str, Any], record: dict[str, Any], fmt: str, level: int =
))
return findings

if kty in _SUPPORTED_KTY:
if isinstance(kty, str) and kty in _SUPPORTED_KTY:
label = f"kty={kty!r}" + (f", crv={crv!r}" if crv else "")
findings.append(Finding("TR-SIG-004", Status.PASS, f"cnf.jwk key type is supported ({label})"))
elif kty is None:
Expand Down
66 changes: 59 additions & 7 deletions tests/test_modules_never_raise.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@
a record it does not understand ends the whole run: the caller gets a traceback where
a verdict belongs, and the record is neither passed nor failed.

``tr_sig`` did exactly that in five ways. ``Finding(rule=...)`` raised ``TypeError`` on
the one check meant to catch a record that embeds its own private key, and reading
``cnf`` or ``cnf.jwk`` raised ``AttributeError`` whenever either was not an object. The
packaged schema does not forbid a ``d`` member in ``cnf.jwk``, so nothing rejected such
a record before the module saw it. The other six modules already guarded their inputs
with ``isinstance``; this pins that for all seven.
The original regression covered malformed top-level fields but not nested discriminator
fields. Four modules then performed set membership directly on values from the record:
an array or object at ``policy.enforcement_mode``, ``runtime.platform``,
``build_provenance.slsa_level`` or ``cnf.jwk.kty`` raised ``TypeError`` instead of
returning a finding. This matrix pins both levels of the record shape.

Scope: the record is a dict throughout and its *fields* are malformed. The cmcp case
below hands ``check`` an envelope whose ``trace`` is junk, but it does so directly.
Expand Down Expand Up @@ -51,12 +50,27 @@
"transparency", "appraisal", "signature", "model", "subject", "iat",
)

NESTED_DISCRIMINANTS = (
(("policy", "enforcement_mode"), "TR-POL-002"),
(("runtime", "platform"), "TR-RTE-001"),
(("build_provenance", "slsa_level"), "TR-SCA-001"),
(("cnf", "jwk", "kty"), "TR-SIG-004"),
)


def _record() -> dict[str, Any]:
raw = json.loads((VECTORS / "signed_root.json").read_text(encoding="utf-8"))
return dict(raw.get("record", raw))


def _replace(record: dict[str, Any], path: tuple[str, ...], value: Any) -> None:
"""Replace the value at *path* in a copied fixture."""
node = record
for key in path[:-1]:
node = node[key]
node[path[-1]] = value


def _call(module: Any, record: dict[str, Any]) -> list[Finding]:
"""Invoke a module's ``check`` whatever its parameter list happens to be.

Expand All @@ -83,6 +97,11 @@ def _mutations() -> list[tuple[str, dict[str, Any]]]:
if isinstance(record.get("cnf"), dict):
record["cnf"]["jwk"] = junk
cases.append((f"cnf.jwk={junk!r}", record))
for path, _ in NESTED_DISCRIMINANTS:
for junk in JUNK:
record = _record()
_replace(record, path, junk)
cases.append((f"{'.'.join(path)}={junk!r}", record))
record = _record()
record["cnf"]["jwk"]["d"] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
cases.append(("cnf.jwk carries d", record))
Expand Down Expand Up @@ -119,6 +138,40 @@ def test_no_module_raises_on_a_record_whose_fields_are_malformed(name: str) -> N
)


@pytest.mark.parametrize(
("path", "expected_code"),
NESTED_DISCRIMINANTS,
ids=["policy", "runtime", "provenance", "signing-key"],
)
@pytest.mark.parametrize("junk", (["unexpected"], {"unexpected": True}), ids=("array", "object"))
def test_runner_fails_nested_discriminators_instead_of_raising(
path: tuple[str, ...], expected_code: str, junk: Any
) -> None:
record = _record()
_replace(record, path, junk)

results = run(record, "trace", level=2)

findings = [finding for module in results.values() for finding in module]
assert any(finding.code == expected_code and finding.failed() for finding in findings), (
f"{'.'.join(path)}={junk!r} produced no {expected_code} failure: {findings}"
)


@pytest.mark.parametrize("slsa_level", [True, False])
def test_runner_rejects_boolean_slsa_levels(slsa_level: bool) -> None:
"""JSON booleans must not inherit Python's integer membership semantics."""
record = _record()
record["build_provenance"]["slsa_level"] = slsa_level

results = run(record, "trace", level=1)

assert any(
finding.code == "TR-SCA-001" and finding.failed()
for finding in results["TR-SCA"]
), results["TR-SCA"]


def test_a_record_embedding_its_own_private_key_fails_rather_than_raising() -> None:
"""The condition the check exists for, named as its own case.

Expand Down Expand Up @@ -243,4 +296,3 @@ def test_the_cmcp_path_does_not_raise_on_a_malformed_envelope() -> None:
assert not raised, (
"tr_sig.check on a cmcp envelope raised or returned nothing:\n " + "\n ".join(raised)
)

13 changes: 13 additions & 0 deletions tests/unit/test_tr_sca.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
margin 0, meaning it could have been deleted silently.
"""

import pytest

from trace_tests.modules.tr_sca import check

VALID_DIGEST = "sha256:" + "a" * 64
Expand All @@ -20,6 +22,17 @@ def test_valid_provenance_passes():
assert not failed, failed


@pytest.mark.parametrize("slsa_level", [0, 1, 2, 3, 0.0, 1.0, 2.0, 3.0])
def test_every_schema_integer_slsa_level_passes(slsa_level):
"""JSON Schema treats numbers with a zero fractional part as integers."""
failed = [
finding
for finding in check(_prov(slsa_level=slsa_level))
if finding.code == "TR-SCA-001" and finding.failed()
]
assert not failed, failed


def test_missing_provenance_fails():
failed = [f for f in check({}) if f.failed()]
assert any(f.code == "TR-SCA-001" for f in failed)
Expand Down
Loading