net-kingdom/docs/proposals/2026-10-11-forgejo-consumer-eso/package-1.2/tests/test_source_contract.py
tegwick 584a964684
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
Prepare reviewed Forgejo type 1.2 source decision package
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a126e1-76f8-7643-8cf3-e93336539ade
2026-10-11 04:48:34 +02:00

88 lines
5.3 KiB
Python

"""Source-only contract checks against the actual owner validator and declarations."""
import copy, importlib.util, os, sys
from pathlib import Path
import pytest, yaml
PACKAGE=Path(__file__).resolve().parents[1]
OWNER=Path(os.environ.get("NET_KINGDOM_ROOT", "/home/worsch/net-kingdom"))
VP=OWNER/"tools/security-case-types/security_case_validator.py"
sys.path.insert(0,str(VP.parent))
spec=importlib.util.spec_from_file_location("security_case_validator",VP)
v=importlib.util.module_from_spec(spec);sys.modules[spec.name]=v;spec.loader.exec_module(v)
TYPE=yaml.safe_load((PACKAGE/"type.yaml").read_text())
FORGEJO=yaml.safe_load((PACKAGE/"examples/forgejo.yaml").read_text())
paths,discovery=v.discover_declarations(TYPE,OWNER.parent)
DECLS,loading=v.composer.load_declarations(paths)
def render(case):
issues,result=v.render_case(case,TYPE,DECLS,require_implemented=False)
assert not v.has_errors(discovery+loading+issues)
return result
def test_full_package_and_proposed_boundary():
assert not v.has_errors(v.validate_type(TYPE,PACKAGE))
assert TYPE["spec"]["status"]=="proposed"
assert FORGEJO["spec"]["status"]=="proposed"
assert "acceptance" not in FORGEJO["spec"]
assert v.has_errors(v.validate_case(FORGEJO,TYPE))
def test_acknowledged_is_still_not_implemented_admission():
acknowledged=copy.deepcopy(TYPE);acknowledged["spec"]["status"]="acknowledged"
assert v.has_errors(v.validate_case(FORGEJO,acknowledged))
@pytest.mark.parametrize("example",sorted((PACKAGE/"examples").glob("*.yaml")))
def test_examples_valid_deterministic_and_nonexecuting(example):
case=yaml.safe_load(example.read_text())
assert not v.has_errors(v.validate_case(case,TYPE,require_implemented=False))
first=render(case);assert first==render(case)
assert first["execution"]["permitted"] is False
assert first["composition"]["execution"]["permitted"] is False
def test_sixth_role_is_exact_consumer_identity_and_namespace():
case=copy.deepcopy(FORGEJO);case["spec"]["parameters"]["consumer_namespace"]="forgejo-preview"
r=render(case);objects={x["id"]:x for x in r["materialization"]}
assert len(objects)==6
consumer=yaml.safe_load(objects["consumer-eso-role"]["body"])
assert consumer["bound_service_account_names"]==["forgejo-keycape-forgejo-eso"]
assert consumer["bound_service_account_namespaces"]==["forgejo-preview"]
assert consumer["audience"]=="openbao"
assert consumer["token_policies"]==["workload-kv-read-keycape-forgejo-forgejo"]
assert consumer["token_no_default_policy"] is True
provider=yaml.safe_load(objects["eso-role"]["body"])
assert provider["bound_service_account_names"]!=consumer["bound_service_account_names"]
def test_original_five_structural_bodies_unchanged():
old=yaml.safe_load((OWNER/"security-case-types/oidc-browser-relying-party/type.yaml").read_text())
case=yaml.safe_load((OWNER/"security-cases/forgejo-netkingdom-oidc.yaml").read_text())
issues,before=v.render_case(case,old,DECLS,require_implemented=False)
assert not v.has_errors(issues)
after={x["id"]:x["body"] for x in render(FORGEJO)["materialization"]}
for item in before["materialization"]:assert item["body"]==after[item["id"]]
def test_public_has_no_secret_or_delivery_hooks():
r=render(yaml.safe_load((PACKAGE/"examples/example-public-portal.yaml").read_text()))
assert [x["id"] for x in r["materialization"]]==["catalog-entry"]
assert not {"mint-secret","deliver-secret","deliver-consumer-secret","bind-env"}&{x["id"] for x in r["follow_through"]}
@pytest.mark.parametrize("key,value",[("consumer_namespace",None),("consumer_namespace","default/other"),("consumer_namespace","x"*64),("consuming_service","x"*64),("redirect_uris","http://portal.invalid/callback"),("redirect_uris","https://portal.invalid/*"),("redirect_uris","https://portal.invalid/callback?x=y"),("redirect_uris","https://portal.invalid/callback#x"),("allowed_scopes","openid,email"),("consumer_reader_service_account","default"),("secret_path","other/path")])
def test_invalid_or_unsettable_parameter_refused(key,value):
case=copy.deepcopy(FORGEJO)
if value is None:case["spec"]["parameters"].pop(key)
else:case["spec"]["parameters"][key]=value
assert v.has_errors(v.validate_case(case,TYPE,require_implemented=False))
def test_created_only_role_recovery_precedes_policy_and_needs_revocation():
rollback=render(FORGEJO)["rollback"];ids=[x["reverses"] for x in rollback]
assert ids.index("consumer-eso-role")<ids.index("consumer-reader-policy")
action=next(x["action"] for x in rollback if x["reverses"]=="consumer-eso-role")
assert "case-created" in action and "revoke" in action and "expiration" in action
delivery=next(x["action"] for x in rollback if x["reverses"]=="deliver-consumer-secret")
assert "UID" in delivery and "preexisting" in delivery and "consumer secret reference" in delivery
@pytest.mark.parametrize("omitted",["README.md","tests/test_source_contract.py","examples"])
def test_full_package_validator_rejects_incomplete_supplement(tmp_path,omitted):
import shutil
trial=tmp_path/"package";shutil.copytree(PACKAGE,trial,ignore=shutil.ignore_patterns("__pycache__",".pytest_cache"))
target=trial/omitted
if target.is_dir():
for example in target.glob("*.yaml"):example.unlink()
else:target.unlink()
assert v.has_errors(v.validate_type(TYPE,trial))