Skip to content

Commit 51ab5d1

Browse files
Pigbibicodex
andcommitted
feat: add field-specific typed identity grammar
Co-Authored-By: Codex <noreply@openai.com>
1 parent 6cc545a commit 51ab5d1

2 files changed

Lines changed: 251 additions & 0 deletions

File tree

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
from __future__ import annotations
2+
import hashlib
3+
import json
4+
import re
5+
import unicodedata
6+
from dataclasses import dataclass
7+
from typing import Any
8+
SCHEMA = "contract_identity.v2"
9+
VERSION = "structured_tokens.v2"
10+
OPS = frozenset({">=", "<=", ">", "<", "==", "!=", "===", "!==", "->", "=>", "::"})
11+
PREDICATE_OPS = frozenset({">=", "<=", ">", "<", "==", "!=", "===", "!=="})
12+
BEHAVIOR_OPS = frozenset({"==", "!=", "->", "=>"})
13+
ORDERING_OPS = frozenset({"->", "=>"})
14+
POLICY = frozenset({"required", "forbidden", "present", "absent", "enabled", "disabled", "valid", "invalid", "missing", "optional"})
15+
KINDS = frozenset({"identifier", "operator", "policy_state", "secret_ref"})
16+
OPERANDS = frozenset({"identifier", "policy_state", "secret_ref"})
17+
CATEGORIES = frozenset({"bug", "contract", "logic", "performance", "reliability", "security"})
18+
SECRET_TYPES = frozenset({"credential", "authorization", "api_key", "session_token", "private_key"})
19+
SECRET_ROLES = frozenset({"auth", "header", "query", "environment", "body"})
20+
OWNER = re.compile(r"^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$")
21+
REPO = re.compile(r"^[A-Za-z0-9._-]{1,100}$")
22+
IDENTIFIER = re.compile(r"^[A-Za-z_][A-Za-z0-9_.-]*(?:\(\))?$")
23+
DIGEST = re.compile(r"^[0-9a-f]{64}$")
24+
MAX_ITEMS = 32
25+
MAX_BYTES = 64 * 1024
26+
class IdentityError(ValueError):
27+
pass
28+
@dataclass(frozen=True)
29+
class CanonicalIdentity:
30+
_payload_json: str
31+
contract_key: str
32+
behavior_digest: str
33+
fingerprint_v2: str
34+
@property
35+
def payload(self) -> dict[str, Any]:
36+
return json.loads(self._payload_json)
37+
def as_record(self) -> dict[str, Any]:
38+
record = self.payload
39+
record.update(contract_key=self.contract_key, behavior_digest=self.behavior_digest, fingerprint_v2=self.fingerprint_v2)
40+
return record
41+
def _obj(value: Any, required: set[str], optional: set[str] = set()) -> dict[str, Any]:
42+
keys = set(value) if isinstance(value, dict) else set()
43+
if not isinstance(value, dict) or len(value) > len(required) + len(optional) or not required <= keys or not keys <= required | optional:
44+
raise IdentityError("invalid object fields")
45+
return value
46+
def _text(value: Any, limit: int = 512) -> str:
47+
if not isinstance(value, str) or len(value) > limit:
48+
raise IdentityError("invalid bounded text")
49+
try:
50+
raw_size = len(value.encode())
51+
except UnicodeEncodeError as exc:
52+
raise IdentityError("invalid unicode text") from exc
53+
if raw_size > limit:
54+
raise IdentityError("invalid bounded text")
55+
value = unicodedata.normalize("NFC", value)
56+
try:
57+
normalized_size = len(value.encode())
58+
except UnicodeEncodeError as exc:
59+
raise IdentityError("invalid unicode text") from exc
60+
if any(unicodedata.category(char).startswith("C") for char in value) or normalized_size > limit:
61+
raise IdentityError("invalid text")
62+
return value
63+
def _scope(value: Any) -> dict[str, str]:
64+
scope = _obj(value, {"repo", "file", "category"})
65+
repo = _text(scope["repo"], 140)
66+
parts = repo.split("/")
67+
if len(parts) != 2 or not OWNER.fullmatch(parts[0]) or "--" in parts[0] or not REPO.fullmatch(parts[1]) or parts[1] in {".", ".."}:
68+
raise IdentityError("invalid repo")
69+
path = _text(scope["file"], 1024)
70+
if path.startswith("/") or "\\" in path or any(part in {"", ".", ".."} for part in path.split("/")):
71+
raise IdentityError("invalid path")
72+
category = _text(scope["category"], 32)
73+
if category not in CATEGORIES:
74+
raise IdentityError("invalid category")
75+
return {"repo": f"{parts[0].lower()}/{parts[1].lower()}", "file": path, "category": category}
76+
def _token(value: Any) -> dict[str, Any]:
77+
token = _obj(value, {"kind", "value"})
78+
kind = _text(token["kind"], 32)
79+
if kind not in KINDS:
80+
raise IdentityError("unknown token kind")
81+
if kind == "secret_ref":
82+
ref = _obj(token["value"], {"type", "role", "position"})
83+
typ, role, position = ref["type"], ref["role"], ref["position"]
84+
if not isinstance(typ, str) or not isinstance(role, str):
85+
raise IdentityError("invalid secret reference")
86+
typ, role = _text(typ, 32), _text(role, 32)
87+
if typ not in SECRET_TYPES or role not in SECRET_ROLES or isinstance(position, bool) or not isinstance(position, int) or not 0 <= position <= 1024:
88+
raise IdentityError("invalid secret reference")
89+
return {"kind": kind, "value": {"type": typ, "role": role, "position": position}}
90+
item = _text(token["value"])
91+
if kind == "operator" and item not in OPS or kind == "policy_state" and item not in POLICY or kind == "identifier" and not IDENTIFIER.fullmatch(item):
92+
raise IdentityError("invalid typed token")
93+
return {"kind": kind, "value": item}
94+
def _anchors(value: Any) -> list[dict[str, Any]]:
95+
if not isinstance(value, list) or not value or len(value) > MAX_ITEMS:
96+
raise IdentityError("invalid anchors")
97+
result = [_token(item) for item in value]
98+
for index, token in enumerate(result):
99+
if token["kind"] != ("identifier" if index % 2 == 0 else "operator") or token["kind"] == "operator" and token["value"] != "::":
100+
raise IdentityError("invalid anchor grammar")
101+
if result[-1]["kind"] != "identifier":
102+
raise IdentityError("anchor must end with identifier")
103+
return result
104+
def _clause(value: Any, operators: frozenset[str], name: str) -> list[dict[str, Any]]:
105+
if not isinstance(value, list) or not value or len(value) > MAX_ITEMS:
106+
raise IdentityError(f"invalid {name} clause")
107+
result = [_token(item) for item in value]
108+
for index, token in enumerate(result):
109+
if index % 2 == 0 and token["kind"] not in OPERANDS or index % 2 == 1 and (token["kind"] != "operator" or token["value"] not in operators):
110+
raise IdentityError(f"invalid {name} grammar")
111+
if result[-1]["kind"] == "operator":
112+
raise IdentityError(f"invalid {name} ending")
113+
secret_indexes = [index for index, token in enumerate(result) if token["kind"] == "secret_ref"]
114+
if secret_indexes and (secret_indexes != [len(result) - 1] or len(result) < 3 or result[-2]["kind"] != "operator" or result[-2]["value"] not in {"==", "!="}):
115+
raise IdentityError(f"invalid {name} secret_ref placement")
116+
return result
117+
def _clauses(value: Any, operators: frozenset[str], name: str, required: bool) -> list[list[dict[str, Any]]]:
118+
if not isinstance(value, list) or len(value) > MAX_ITEMS or required and not value:
119+
raise IdentityError(f"invalid {name} clauses")
120+
return [_clause(clause, operators, name) for clause in value]
121+
def _ordering(value: Any) -> list[list[dict[str, Any]]]:
122+
if not isinstance(value, list) or len(value) > MAX_ITEMS:
123+
raise IdentityError("invalid ordering constraints")
124+
result = []
125+
for clause in value:
126+
if not isinstance(clause, list) or len(clause) != 3:
127+
raise IdentityError("ordering must be one relation")
128+
tokens = [_token(item) for item in clause]
129+
if tokens[0]["kind"] != "identifier" or tokens[1]["kind"] != "operator" or tokens[1]["value"] not in ORDERING_OPS or tokens[2]["kind"] != "identifier":
130+
raise IdentityError("invalid ordering relation")
131+
result.append(tokens)
132+
return result
133+
def _json(value: Any) -> str:
134+
return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
135+
def _hash(value: Any) -> str:
136+
return hashlib.sha256(_json(value).encode()).hexdigest()
137+
def validate_identity(payload: Any) -> CanonicalIdentity:
138+
fields = {"schema", "canonicalizer_version", "scope", "anchors", "predicates", "required_behavior", "forbidden_behavior", "ordering_constraints"}
139+
value = _obj(payload, fields)
140+
if value["schema"] != SCHEMA or value["canonicalizer_version"] != VERSION:
141+
raise IdentityError("unsupported schema")
142+
canonical = {"schema": SCHEMA, "canonicalizer_version": VERSION, "scope": _scope(value["scope"]), "anchors": _anchors(value["anchors"]), "predicates": _clauses(value["predicates"], PREDICATE_OPS, "predicate", True), "required_behavior": _clauses(value["required_behavior"], BEHAVIOR_OPS, "required_behavior", True), "forbidden_behavior": _clauses(value["forbidden_behavior"], BEHAVIOR_OPS, "forbidden_behavior", False), "ordering_constraints": _ordering(value["ordering_constraints"])}
143+
key = _hash({name: canonical[name] for name in ("schema", "canonicalizer_version", "scope", "anchors", "predicates")})
144+
behavior = _hash({"contract_key": key, "required_behavior": canonical["required_behavior"], "forbidden_behavior": canonical["forbidden_behavior"], "ordering_constraints": canonical["ordering_constraints"]})
145+
identity = CanonicalIdentity(_json(canonical), key, behavior, _hash({"contract_key": key, "behavior_digest": behavior}))
146+
if len(_json(identity.as_record()).encode()) > MAX_BYTES:
147+
raise IdentityError("identity too large")
148+
return identity
149+
def verify_identity_record(record: Any) -> CanonicalIdentity:
150+
payload_fields = {"schema", "canonicalizer_version", "scope", "anchors", "predicates", "required_behavior", "forbidden_behavior", "ordering_constraints"}
151+
digest_fields = {"contract_key", "behavior_digest", "fingerprint_v2"}
152+
if not isinstance(record, dict) or set(record) != payload_fields | digest_fields or any(not isinstance(record[name], str) or not DIGEST.fullmatch(record[name]) for name in digest_fields):
153+
raise IdentityError("invalid verified record")
154+
identity = validate_identity({name: record[name] for name in payload_fields})
155+
if (identity.contract_key, identity.behavior_digest, identity.fingerprint_v2) != tuple(record[name] for name in ("contract_key", "behavior_digest", "fingerprint_v2")) or identity.as_record() != record:
156+
raise IdentityError("record mismatch")
157+
return identity
158+
def canonical_json(identity: CanonicalIdentity) -> str:
159+
if not isinstance(identity, CanonicalIdentity):
160+
raise IdentityError("expected identity")
161+
return _json(identity.as_record())
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import copy
2+
import json
3+
import unittest
4+
from scripts.canonical_typed_identity import IdentityError, canonical_json, validate_identity, verify_identity_record
5+
class R1dGrammarTests(unittest.TestCase):
6+
def tok(self, kind, value):
7+
return {"kind": kind, "value": value}
8+
def secret(self):
9+
return self.tok("secret_ref", {"type": "credential", "role": "auth", "position": 0})
10+
def payload(self):
11+
return {
12+
"schema": "contract_identity.v2",
13+
"canonicalizer_version": "structured_tokens.v2",
14+
"scope": {"repo": "AcMe/Audit-Bridge", "file": "service/review.py", "category": "contract"},
15+
"anchors": [self.tok("identifier", "Namespace"), self.tok("operator", "::"), self.tok("identifier", "validate()")],
16+
"predicates": [[self.tok("identifier", "score"), self.tok("operator", ">="), self.tok("identifier", "threshold")]],
17+
"required_behavior": [[self.tok("policy_state", "required")]],
18+
"forbidden_behavior": [[self.tok("identifier", "mode"), self.tok("operator", "=="), self.tok("policy_state", "forbidden")]],
19+
"ordering_constraints": [[self.tok("identifier", "validate"), self.tok("operator", "->"), self.tok("identifier", "persist")]],
20+
}
21+
def invalid(self, value):
22+
with self.assertRaises(IdentityError):
23+
validate_identity(value)
24+
def test_valid_field_grammars(self):
25+
for operator in (">=", "<=", ">", "<", "==", "!=", "===", "!=="):
26+
value = self.payload()
27+
value["predicates"][0][1]["value"] = operator
28+
validate_identity(value)
29+
for behavior in ([[self.tok("policy_state", "required")]], [[self.tok("identifier", "mode"), self.tok("operator", "=="), self.tok("policy_state", "required")]], [[self.tok("identifier", "token"), self.tok("operator", "=="), self.secret()]]):
30+
value = self.payload()
31+
value["required_behavior"] = behavior
32+
validate_identity(value)
33+
for operator in ("->", "=>"):
34+
value = self.payload()
35+
value["ordering_constraints"][0][1]["value"] = operator
36+
validate_identity(value)
37+
def test_predicate_and_behavior_operator_boundaries(self):
38+
bad_clauses = ([[self.tok("operator", ">=")]], [[self.tok("operator", ">="), self.tok("identifier", "x")]], [[self.tok("identifier", "x"), self.tok("operator", ">=")]], [[self.tok("identifier", "x"), self.tok("operator", "::"), self.tok("identifier", "y")]], [[self.tok("identifier", "x"), self.tok("operator", "=="), self.tok("operator", "!=")]])
39+
for clause in bad_clauses:
40+
value = self.payload()
41+
value["predicates"] = [clause]
42+
self.invalid(value)
43+
for clause in ([[self.tok("operator", "==")]], [[self.secret()]], [[self.tok("secret_ref", {"type": "credential", "role": "auth", "position": 0}), self.tok("operator", "=="), self.tok("identifier", "x")]], [[self.tok("identifier", "x"), self.tok("operator", "->"), self.secret()]], [[self.tok("identifier", "x"), self.tok("operator", "=="), self.secret(), self.tok("operator", "=="), self.tok("identifier", "y")]]):
44+
value = self.payload()
45+
value["required_behavior"] = [clause]
46+
self.invalid(value)
47+
def test_ordering_is_exactly_one_explicit_relation(self):
48+
for clause in ([[self.tok("operator", "->")]], [[self.tok("identifier", "a"), self.tok("operator", "=="), self.tok("identifier", "b")]], [[self.tok("identifier", "a"), self.tok("operator", "->")]], [[self.tok("identifier", "a"), self.tok("operator", "->"), self.tok("identifier", "b"), self.tok("operator", "->"), self.tok("identifier", "c")]]):
49+
value = self.payload()
50+
value["ordering_constraints"] = [clause]
51+
self.invalid(value)
52+
def test_near_miss_identifiers_and_strict_input(self):
53+
for name in ("ghs_database", "github_pat_validator", "Eurasia", "keyJson", "secret_manager", "secret_ref_validator"):
54+
value = self.payload()
55+
value["anchors"] = [self.tok("identifier", name)]
56+
validate_identity(value)
57+
for field, item in (("canonicalizer_version", "structured_tokens.v1"), ("evidence", {}), ("description", "raw prose"), ("severity", "high")):
58+
value = self.payload()
59+
if field == "canonicalizer_version":
60+
value[field] = item
61+
else:
62+
value[field] = item
63+
self.invalid(value)
64+
for text in ("raw prose", "password=secret", "bad\x00name", "bad\ud800name"):
65+
value = self.payload()
66+
value["anchors"] = [self.tok("identifier", text)]
67+
self.invalid(value)
68+
value = self.payload()
69+
value["anchors"] = [self.tok("mystery", "x")]
70+
self.invalid(value)
71+
def test_secret_ref_and_wire_tamper_fail_closed(self):
72+
for ref in ({"type": "credential", "role": "auth", "position": 0, "raw": "x"}, {"type": "unknown", "role": "auth", "position": 0}, {"type": "credential", "role": "other", "position": 0}, {"type": "credential", "role": "auth", "position": 1025}):
73+
value = self.payload()
74+
value["required_behavior"] = [[self.tok("identifier", "token"), self.tok("operator", "=="), self.tok("secret_ref", ref)]]
75+
self.invalid(value)
76+
identity = validate_identity(self.payload())
77+
record = identity.as_record()
78+
self.assertEqual(verify_identity_record(record), identity)
79+
self.assertEqual(canonical_json(identity), canonical_json(verify_identity_record(json.loads(canonical_json(identity)))))
80+
for field in ("contract_key", "behavior_digest", "fingerprint_v2"):
81+
bad = copy.deepcopy(record)
82+
bad[field] = "0" * 64
83+
with self.assertRaises(IdentityError):
84+
verify_identity_record(bad)
85+
bad = copy.deepcopy(record)
86+
bad["scope"]["repo"] = "AcMe/Audit-Bridge"
87+
with self.assertRaises(IdentityError):
88+
verify_identity_record(bad)
89+
if __name__ == "__main__":
90+
unittest.main()

0 commit comments

Comments
 (0)