Skip to content

Commit c8f0da1

Browse files
Pigbibicodex
andcommitted
replace trusted identity with raw byte validation
Co-Authored-By: Codex <noreply@openai.com>
1 parent d20719f commit c8f0da1

3 files changed

Lines changed: 329 additions & 0 deletions

File tree

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Trusted workflow identity raw-byte contract
2+
3+
This fresh A0 replacement is pure and does not use Python object identity,
4+
singleton state, capability objects, registries, workflows, permissions,
5+
Actions, bundles, or artifact access as a trust boundary.
6+
7+
## Fixed identity
8+
9+
The only accepted constants are:
10+
11+
- repository: `QuantStrategyLab/PoliticalEventTrackingResearch`
12+
- workflow path: `.github/workflows/pert_weekly_period_lock_harness.yml`
13+
- workflow ref: `QuantStrategyLab/PoliticalEventTrackingResearch/.github/workflows/pert_weekly_period_lock_harness.yml@refs/heads/main`
14+
15+
`build_trusted_workflow_identity_bytes(reviewed_workflow_sha)` builds canonical
16+
bytes from those module-private constants and an explicitly validated lowercase
17+
40-hex runtime SHA. `validate_trusted_workflow_ref()` accepts only the fixed
18+
workflow ref.
19+
20+
## Operation boundary
21+
22+
`validate_trusted_workflow_identity_bytes(raw)` bounds, parses, shape-checks,
23+
reconstructs, canonicalizes, and compares the complete wire object on every
24+
call. It returns only a plain reviewed-SHA value; the value is not authority
25+
and must not be cached as a capability. Any later operation must retain and
26+
revalidate the original canonical bytes.
27+
28+
The serializer accepts only a `Mapping`, snapshots all entries, validates exact
29+
keys and string types before canonical JSON serialization, and never reads
30+
attributes from arbitrary objects. Duplicate, unknown, missing, noncanonical,
31+
oversized, Unicode/control, wrong identity, and malformed SHA inputs fail with
32+
sanitized `TrustedWorkflowIdentityError` codes. Expected contract exceptions are
33+
sanitized; programming/system exceptions are not broadly caught.
34+
35+
The later A1 bundle integration may consume this raw contract and bind it to
36+
the period lock/snapshot/manifest. It remains blocked until this foundation is
37+
accepted and merged; no workflow or privileged permission is part of A0.
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
"""Canonical raw-byte validation for the future trusted weekly harness.
2+
3+
No returned Python value is an authority token. Each caller must retain and
4+
revalidate the canonical bytes at its operation boundary.
5+
"""
6+
7+
from __future__ import annotations
8+
9+
import json
10+
import re
11+
from collections.abc import Mapping
12+
13+
IDENTITY_VERSION = "pert.trusted_workflow_identity.v1"
14+
TRUSTED_REPOSITORY = "QuantStrategyLab/PoliticalEventTrackingResearch"
15+
TRUSTED_WORKFLOW_PATH = ".github/workflows/pert_weekly_period_lock_harness.yml"
16+
TRUSTED_WORKFLOW_REF = f"{TRUSTED_REPOSITORY}/{TRUSTED_WORKFLOW_PATH}@refs/heads/main"
17+
MAX_IDENTITY_BYTES = 4096
18+
_SHA_RE = re.compile(r"^[0-9a-f]{40}$")
19+
_KEYS = frozenset({"identity_version", "repository", "workflow_path", "workflow_ref", "reviewed_workflow_sha"})
20+
21+
22+
class TrustedWorkflowIdentityError(ValueError):
23+
"""Stable, sanitized raw identity contract error."""
24+
25+
def __init__(self, code: str) -> None:
26+
self.code = code
27+
super().__init__(code)
28+
29+
30+
def _error(code: str) -> TrustedWorkflowIdentityError:
31+
return TrustedWorkflowIdentityError(code)
32+
33+
34+
def _reviewed_sha(value: object) -> str:
35+
if type(value) is not str or not _SHA_RE.fullmatch(value):
36+
raise _error("reviewed_workflow_sha_invalid")
37+
return value
38+
39+
40+
def validate_trusted_workflow_ref(value: object) -> str:
41+
if type(value) is not str or value != TRUSTED_WORKFLOW_REF:
42+
raise _error("trusted_workflow_identity_mismatch")
43+
return TRUSTED_WORKFLOW_REF
44+
45+
46+
def _wire_snapshot(value: object) -> dict[str, str]:
47+
if not isinstance(value, Mapping):
48+
raise _error("identity_wire_invalid")
49+
try:
50+
items = list(value.items())
51+
except (AttributeError, TypeError, ValueError, UnicodeError, OverflowError, RecursionError):
52+
raise _error("identity_wire_invalid") from None
53+
if len(items) != len(_KEYS):
54+
raise _error("identity_shape_invalid")
55+
result: dict[str, str] = {}
56+
for key, item in items:
57+
if type(key) is not str or key in result or type(item) is not str:
58+
raise _error("identity_wire_invalid")
59+
result[key] = item
60+
if set(result) != _KEYS:
61+
raise _error("identity_shape_invalid")
62+
if result["identity_version"] != IDENTITY_VERSION:
63+
raise _error("identity_version_invalid")
64+
if result["repository"] != TRUSTED_REPOSITORY or any(
65+
char in result["repository"] for char in "\x00\n\r\t"
66+
):
67+
raise _error("identity_mismatch")
68+
if result["workflow_path"] != TRUSTED_WORKFLOW_PATH:
69+
raise _error("identity_mismatch")
70+
validate_trusted_workflow_ref(result["workflow_ref"])
71+
_reviewed_sha(result["reviewed_workflow_sha"])
72+
return result
73+
74+
75+
def _canonical_bytes(value: Mapping[str, str]) -> bytes:
76+
try:
77+
raw = json.dumps(value, ensure_ascii=True, sort_keys=True, separators=(",", ":"), allow_nan=False).encode(
78+
"ascii"
79+
)
80+
except (TypeError, ValueError, UnicodeError, OverflowError, RecursionError):
81+
raise _error("identity_serialization_invalid") from None
82+
if len(raw) > MAX_IDENTITY_BYTES:
83+
raise _error("identity_wire_oversized")
84+
return raw
85+
86+
87+
def serialize_trusted_workflow_identity(value: object) -> bytes:
88+
"""Validate a plain mapping completely, then emit canonical bytes."""
89+
90+
return _canonical_bytes(_wire_snapshot(value))
91+
92+
93+
def build_trusted_workflow_identity_bytes(reviewed_workflow_sha: object) -> bytes:
94+
"""Build canonical bytes from fixed identity constants and explicit SHA."""
95+
96+
sha = _reviewed_sha(reviewed_workflow_sha)
97+
return serialize_trusted_workflow_identity(
98+
{
99+
"identity_version": IDENTITY_VERSION,
100+
"repository": TRUSTED_REPOSITORY,
101+
"workflow_path": TRUSTED_WORKFLOW_PATH,
102+
"workflow_ref": TRUSTED_WORKFLOW_REF,
103+
"reviewed_workflow_sha": sha,
104+
}
105+
)
106+
107+
108+
def parse_trusted_workflow_identity_bytes(raw: object) -> dict[str, str]:
109+
"""Parse and reconstruct a plain value; never return an authority object."""
110+
111+
if type(raw) is not bytes:
112+
raise _error("identity_wire_invalid")
113+
if len(raw) > MAX_IDENTITY_BYTES:
114+
raise _error("identity_wire_oversized")
115+
116+
def pairs(items: list[tuple[str, object]]) -> dict[str, object]:
117+
result: dict[str, object] = {}
118+
for key, item in items:
119+
if key in result:
120+
raise _error("identity_duplicate_key")
121+
result[key] = item
122+
return result
123+
124+
def reject_constant(_: str) -> None:
125+
raise _error("identity_wire_invalid")
126+
127+
try:
128+
value = json.loads(raw.decode("ascii"), object_pairs_hook=pairs, parse_constant=reject_constant)
129+
except TrustedWorkflowIdentityError:
130+
raise
131+
except (UnicodeError, json.JSONDecodeError, TypeError, ValueError, RecursionError):
132+
raise _error("identity_wire_invalid") from None
133+
snapshot = _wire_snapshot(value)
134+
canonical = _canonical_bytes(snapshot)
135+
if canonical != raw:
136+
raise _error("identity_noncanonical")
137+
return dict(snapshot)
138+
139+
140+
def validate_trusted_workflow_identity_bytes(raw: object) -> str:
141+
"""Reparse canonical bytes and return only the reviewed SHA value."""
142+
143+
return parse_trusted_workflow_identity_bytes(raw)["reviewed_workflow_sha"]
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
from __future__ import annotations
2+
3+
import json
4+
from dataclasses import dataclass
5+
6+
import pytest
7+
8+
import political_event_tracking_research.trusted_workflow_identity_raw as module
9+
from political_event_tracking_research.trusted_workflow_identity_raw import (
10+
IDENTITY_VERSION,
11+
MAX_IDENTITY_BYTES,
12+
TRUSTED_REPOSITORY,
13+
TRUSTED_WORKFLOW_PATH,
14+
TRUSTED_WORKFLOW_REF,
15+
TrustedWorkflowIdentityError,
16+
build_trusted_workflow_identity_bytes,
17+
serialize_trusted_workflow_identity,
18+
validate_trusted_workflow_identity_bytes,
19+
validate_trusted_workflow_ref,
20+
)
21+
22+
23+
SHA = "a" * 40
24+
25+
26+
def wire() -> dict[str, str]:
27+
return {
28+
"identity_version": IDENTITY_VERSION,
29+
"repository": TRUSTED_REPOSITORY,
30+
"workflow_path": TRUSTED_WORKFLOW_PATH,
31+
"workflow_ref": TRUSTED_WORKFLOW_REF,
32+
"reviewed_workflow_sha": SHA,
33+
}
34+
35+
36+
def test_fixed_identity_bytes_are_deterministic_and_revalidated() -> None:
37+
raw = build_trusted_workflow_identity_bytes(SHA)
38+
assert validate_trusted_workflow_identity_bytes(raw) == SHA
39+
assert serialize_trusted_workflow_identity(dict(reversed(list(wire().items())))) == raw
40+
assert validate_trusted_workflow_ref(TRUSTED_WORKFLOW_REF) == TRUSTED_WORKFLOW_REF
41+
42+
43+
def test_returned_plain_values_are_not_trust_objects() -> None:
44+
value = module.parse_trusted_workflow_identity_bytes(build_trusted_workflow_identity_bytes(SHA))
45+
value["repository"] = "QuantStrategyLab/Other"
46+
with pytest.raises(TrustedWorkflowIdentityError, match="identity_mismatch"):
47+
serialize_trusted_workflow_identity(value)
48+
49+
50+
@pytest.mark.parametrize("field", ["repository", "workflow_path", "workflow_ref"])
51+
def test_each_identity_field_is_rechecked(field: str) -> None:
52+
value = wire()
53+
value[field] = "wrong"
54+
with pytest.raises(TrustedWorkflowIdentityError, match="identity_mismatch"):
55+
serialize_trusted_workflow_identity(value)
56+
57+
58+
def test_reviewed_sha_is_explicit_runtime_evidence() -> None:
59+
value = wire()
60+
value["reviewed_workflow_sha"] = "b" * 40
61+
assert serialize_trusted_workflow_identity(value) != serialize_trusted_workflow_identity(wire())
62+
63+
64+
@pytest.mark.parametrize("mutation", ["unknown", "missing", "duplicate", "noncanonical", "wrong_version"])
65+
def test_wire_shape_and_canonical_bytes_fail_closed(mutation: str) -> None:
66+
value = wire()
67+
if mutation == "unknown":
68+
value["debug"] = "unexpected" # type: ignore[typeddict-unknown-key]
69+
raw = json.dumps(value, sort_keys=True, separators=(",", ":")).encode()
70+
elif mutation == "missing":
71+
del value["workflow_path"]
72+
raw = json.dumps(value, sort_keys=True, separators=(",", ":")).encode()
73+
elif mutation == "duplicate":
74+
raw = json.dumps(value, sort_keys=True, separators=(",", ":")).replace(
75+
'"workflow_path":"' + TRUSTED_WORKFLOW_PATH + '"',
76+
'"workflow_path":"' + TRUSTED_WORKFLOW_PATH + '","workflow_path":"' + TRUSTED_WORKFLOW_PATH + '"',
77+
).encode()
78+
elif mutation == "noncanonical":
79+
raw = json.dumps(value).encode()
80+
else:
81+
value["identity_version"] = "future"
82+
raw = json.dumps(value, sort_keys=True, separators=(",", ":")).encode()
83+
with pytest.raises(TrustedWorkflowIdentityError):
84+
validate_trusted_workflow_identity_bytes(raw)
85+
86+
87+
@pytest.mark.parametrize(
88+
"workflow_ref",
89+
[
90+
"QuantStrategyLab/Other/.github/workflows/pert_weekly_period_lock_harness.yml@refs/heads/main",
91+
TRUSTED_WORKFLOW_REF.replace(".github/workflows/", ".github//workflows/"),
92+
TRUSTED_WORKFLOW_REF.replace("refs/heads/main", "refs/tags/main"),
93+
TRUSTED_WORKFLOW_REF + "\n",
94+
"QuantStrategyLab/PoliticalEventTrackingResearch/.github/workflows/other.yml@refs/heads/main",
95+
],
96+
)
97+
def test_wrong_workflow_refs_are_sanitized(workflow_ref: str) -> None:
98+
with pytest.raises(TrustedWorkflowIdentityError, match="identity_mismatch"):
99+
validate_trusted_workflow_ref(workflow_ref)
100+
101+
102+
@pytest.mark.parametrize("sha", ["a" * 39, "A" * 40, "g" * 40, True, 1, "refs/heads/main"])
103+
def test_reviewed_sha_requires_lowercase_full_hex(sha: object) -> None:
104+
with pytest.raises(TrustedWorkflowIdentityError, match="reviewed_workflow_sha_invalid"):
105+
build_trusted_workflow_identity_bytes(sha)
106+
107+
108+
def test_oversized_and_non_ascii_wire_fail_closed() -> None:
109+
with pytest.raises(TrustedWorkflowIdentityError, match="identity_wire_oversized"):
110+
validate_trusted_workflow_identity_bytes(b"{" + b"a" * MAX_IDENTITY_BYTES)
111+
value = wire()
112+
value["repository"] = "QuantStrategyLab/PoliticalEventTrackingResearch\N{SNOWMAN}"
113+
with pytest.raises(TrustedWorkflowIdentityError, match="identity_mismatch"):
114+
serialize_trusted_workflow_identity(value)
115+
116+
117+
@dataclass
118+
class ForgedObject:
119+
repository: str = TRUSTED_REPOSITORY
120+
workflow_path: str = TRUSTED_WORKFLOW_PATH
121+
workflow_ref: str = TRUSTED_WORKFLOW_REF
122+
reviewed_workflow_sha: str = SHA
123+
124+
125+
def test_forged_objects_and_attribute_failures_are_sanitized() -> None:
126+
with pytest.raises(TrustedWorkflowIdentityError, match="identity_wire_invalid"):
127+
serialize_trusted_workflow_identity(ForgedObject())
128+
129+
class BrokenMapping(dict[str, str]):
130+
def items(self): # type: ignore[no-untyped-def]
131+
raise AttributeError("untrusted attribute")
132+
133+
with pytest.raises(TrustedWorkflowIdentityError, match="identity_wire_invalid"):
134+
serialize_trusted_workflow_identity(BrokenMapping())
135+
136+
137+
def test_unexpected_runtime_error_is_not_broad_caught(monkeypatch: pytest.MonkeyPatch) -> None:
138+
class BrokenMapping(dict[str, str]):
139+
def items(self): # type: ignore[no-untyped-def]
140+
raise RuntimeError("programming failure")
141+
142+
with pytest.raises(RuntimeError, match="programming failure"):
143+
serialize_trusted_workflow_identity(BrokenMapping())
144+
def fail_dumps(*_: object, **__: object) -> str:
145+
raise RuntimeError("programming failure")
146+
147+
monkeypatch.setattr(module.json, "dumps", fail_dumps)
148+
with pytest.raises(RuntimeError, match="programming failure"):
149+
serialize_trusted_workflow_identity(wire())

0 commit comments

Comments
 (0)