WARDEN-WP-0044-T01: warden case check-type; revised oidc-rp 1.0 type conforms
Mechanical follow-through check (lane, warden_executes, value_handling, attended justification, read-free verification, rollback coverage). The revised type passes schema; lanes identity-provisioner-groups and keycape-client-secret-generate are not yet in the catalog (gate T02). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 327203@bnt-lap001 Assistant-Session: 55028ae3-4914-4bd1-8f5c-815f3527aef0
This commit is contained in:
parent
98cb0bc24a
commit
0e7ceb8c06
5 changed files with 349 additions and 1 deletions
127
tests/test_case_type.py
Normal file
127
tests/test_case_type.py
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
"""WARDEN-WP-0044-T01: SecurityCaseType follow-through check."""
|
||||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
import yaml
|
||||
from typer.testing import CliRunner
|
||||
|
||||
from warden.case_type import check_follow_through, load_type
|
||||
from warden.cli import app
|
||||
from warden.routing.catalog import load_catalog
|
||||
|
||||
LIVE_TYPE = Path.home() / "net-kingdom/security-case-types/oidc-browser-relying-party/type.yaml"
|
||||
|
||||
BASE = {
|
||||
"apiVersion": "netkingdom.io/security-case-type/v0.1",
|
||||
"kind": "SecurityCaseType",
|
||||
"spec": {
|
||||
"id": "t",
|
||||
"version": "1.0",
|
||||
"follow_through": [
|
||||
{"id": "run", "owner": "o", "entry_point": "e", "auth": "a", "lane": "key-cape-oidc-login",
|
||||
"warden_executes": True, "attended": False, "value_handling": "none"},
|
||||
{"id": "login", "owner": "o", "entry_point": "e", "auth": "a", "lane": "key-cape-oidc-login",
|
||||
"warden_executes": False, "attended": True, "value_handling": "none",
|
||||
"attended_justification": "live registry is a Secret"},
|
||||
{"id": "handover", "owner": "o", "entry_point": "e", "auth": "a",
|
||||
"warden_executes": False, "attended": False, "value_handling": "none"},
|
||||
],
|
||||
"verification": [{"id": "v", "check": "c", "expect": "present", "reads_secret_value": False}],
|
||||
"rollback": [{"reverses": "run"}, {"reverses": "login"}, {"reverses": "handover"}],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def catalog():
|
||||
return load_catalog()
|
||||
|
||||
|
||||
def _doc(**step_overrides):
|
||||
doc = copy.deepcopy(BASE)
|
||||
doc["spec"]["follow_through"][0].update(step_overrides)
|
||||
return doc
|
||||
|
||||
|
||||
def _codes(report):
|
||||
return {f.code for f in report.findings}
|
||||
|
||||
|
||||
def test_conforming_type_is_plannable(catalog):
|
||||
report = check_follow_through(_doc(), catalog)
|
||||
assert report.plannable, report.findings
|
||||
assert report.warden_steps == ["run"]
|
||||
assert report.attended_lanes == ["key-cape-oidc-login"]
|
||||
|
||||
|
||||
def test_executed_step_without_lane_refused(catalog):
|
||||
doc = _doc()
|
||||
del doc["spec"]["follow_through"][0]["lane"]
|
||||
assert "FT-LANE" in _codes(check_follow_through(doc, catalog))
|
||||
|
||||
|
||||
def test_unknown_lane_is_catalog_finding_not_schema(catalog):
|
||||
report = check_follow_through(_doc(lane="no-such-lane"), catalog)
|
||||
assert report.schema_ok and not report.plannable
|
||||
assert _codes(report) == {"LANE-MISSING"}
|
||||
|
||||
|
||||
def test_value_handling_required_and_enumerated(catalog):
|
||||
assert "FT-VALUE" in _codes(check_follow_through(_doc(value_handling="returned"), catalog))
|
||||
doc = _doc()
|
||||
del doc["spec"]["follow_through"][0]["value_handling"]
|
||||
assert "FT-FIELD" in _codes(check_follow_through(doc, catalog))
|
||||
|
||||
|
||||
def test_attended_needs_justification_and_excludes_execution(catalog):
|
||||
doc = copy.deepcopy(BASE)
|
||||
del doc["spec"]["follow_through"][1]["attended_justification"]
|
||||
doc["spec"]["follow_through"][1]["warden_executes"] = True
|
||||
msgs = [f.message for f in check_follow_through(doc, catalog).findings if f.code == "FT-ATTEND"]
|
||||
assert len(msgs) == 2
|
||||
|
||||
|
||||
def test_attended_step_needs_login_lane(catalog):
|
||||
doc = copy.deepcopy(BASE)
|
||||
secret_lane = next(e.id for e in catalog.entries if e.is_active and e.reviewed and e.lane == "secret")
|
||||
doc["spec"]["follow_through"][1]["lane"] = secret_lane
|
||||
assert "LANE-KIND" in _codes(check_follow_through(doc, catalog))
|
||||
|
||||
|
||||
def test_generated_value_never_in_attended_session(catalog):
|
||||
doc = copy.deepcopy(BASE)
|
||||
doc["spec"]["follow_through"][1]["value_handling"] = "generated_in_custody"
|
||||
assert "FT-VALUE" in _codes(check_follow_through(doc, catalog))
|
||||
|
||||
|
||||
def test_verification_must_not_read_values(catalog):
|
||||
doc = _doc()
|
||||
doc["spec"]["verification"][0]["reads_secret_value"] = True
|
||||
assert "VER-VALUE" in _codes(check_follow_through(doc, catalog))
|
||||
|
||||
|
||||
def test_every_step_reversible(catalog):
|
||||
doc = _doc()
|
||||
doc["spec"]["rollback"] = doc["spec"]["rollback"][1:]
|
||||
assert "RB-MISSING" in _codes(check_follow_through(doc, catalog))
|
||||
|
||||
|
||||
def test_cli_exit_codes(tmp_path):
|
||||
good, bad = tmp_path / "good.yaml", tmp_path / "bad.yaml"
|
||||
good.write_text(yaml.safe_dump(_doc()))
|
||||
bad.write_text(yaml.safe_dump(_doc(lane="no-such-lane")))
|
||||
runner = CliRunner()
|
||||
assert runner.invoke(app, ["case", "check-type", str(good), "--json"]).exit_code == 0
|
||||
assert runner.invoke(app, ["case", "check-type", str(bad), "--json"]).exit_code == 1
|
||||
|
||||
|
||||
@pytest.mark.skipif(not LIVE_TYPE.exists(), reason="net-kingdom checkout not present")
|
||||
def test_live_oidc_rp_type_schema_conforms(catalog):
|
||||
"""The revised 1.0 type meets R1-R3/R5-R7; only the two owner lanes are outstanding."""
|
||||
report = check_follow_through(load_type(LIVE_TYPE), catalog)
|
||||
assert report.schema_ok, report.findings
|
||||
assert report.attended_lanes == ["key-cape-oidc-login"]
|
||||
assert {f.code for f in report.findings} <= {"LANE-MISSING", "LANE-UNREVIEWED"}
|
||||
Loading…
Add table
Add a link
Reference in a new issue