From 001a38ca2aa85155ce4840c4da22aca6e2346171 Mon Sep 17 00:00:00 2001 From: tegwick Date: Sat, 17 Oct 2026 12:01:18 +0200 Subject: [PATCH] NK-WP-0048-T16 r5: regenerate the 1.3 map and both compositions; tests follow the r5 lanes 1.3 composes completely with the new lanes and an all-human actor set. 1.2 is correctly refused on the three moved lanes and stays plan-only. The 1.2 admission test's unique checks move into test_admit_1_3_type.py. Tests run against ops-warden 117fec9. Co-Authored-By: Claude Opus 5.5 Assistant: claude-code Assistant-Model: opus Assistant-Process: 903097@bnt-lap001 Assistant-Session: aa320c8f-c3fa-46e7-8541-0b58e1e45d23 --- .../1.3/oidc-browser-relying-party/type.yaml | 10 +- .../descriptor-composition-1.2.candidate.json | 54 +++++----- .../descriptor-composition-1.3.candidate.json | 50 +++++----- .../test_admit_1_2_adapter_descriptors.py | 98 ------------------- sso-mfa/k8s/keycape/test_admit_1_3_type.py | 21 +++- .../keycape/test_compose_1_2_descriptors.py | 46 ++++++--- 6 files changed, 106 insertions(+), 173 deletions(-) delete mode 100644 sso-mfa/k8s/keycape/test_admit_1_2_adapter_descriptors.py diff --git a/security-case-type-revisions/1.3/oidc-browser-relying-party/type.yaml b/security-case-type-revisions/1.3/oidc-browser-relying-party/type.yaml index de1c772..6ec7560 100644 --- a/security-case-type-revisions/1.3/oidc-browser-relying-party/type.yaml +++ b/security-case-type-revisions/1.3/oidc-browser-relying-party/type.yaml @@ -486,10 +486,10 @@ spec: # Reviewed machine descriptors (NK-WP-0048-T16 successor candidate). Digests are # generated by sso-mfa/k8s/keycape/compose-1.2-descriptors.py --version 1.3. execution_descriptors: - bind-env: sha256:d2511dce3d2ff1d380f845920aa168e6fe09c0f0c382482a796a5bf13619905d - deliver-consumer-secret: sha256:9c289f646516923a4c448a2d3cdd45b33115516e6a171b7955924d8b5d852d2b + bind-env: sha256:d5fcb20cf15bf7454890d2529180c582218e3df12584a99162e4b664b899caa0 + deliver-consumer-secret: sha256:36281df8139fb1ec64b226d94f80eb592de5addab7063ff54c691c641e275fc7 deliver-secret: sha256:75aaa7346aebdea2a0eb0ba993995515b5248ffefc34caf8e713525e9b58da85 - ensure-groups: sha256:4411b5cb8944ee76ca1ade62c0c246f061981fb18f0b9afd05934a8935598dc3 - hand-over: sha256:84f0fac401ea2f1e7c305fb1753a34838ac062655199fcfbbd998f39268c3000 + ensure-groups: sha256:ebc9d5b9167d18e6015f4bbeb9699e8d396d64fbfaf195142781f84f4f47a70a + hand-over: sha256:1bce07a3229b7d0c7c730f35ce5b40fe471009a82de8dd53788ed970a9be1224 mint-secret: sha256:c92957227235db04953809d92b260d32c8b947472a8e8fd17a258ade3ae8da70 - register-client: sha256:3ccc8eed545cf31f8a48feb8afa0c1355e126d9549eef9737d92d0c1b87708a8 + register-client: sha256:4ecc769f75433b66c7161f13c0fe7e5599633cf466d46cb6d8031f0456f426e7 diff --git a/sso-mfa/k8s/keycape/descriptor-composition-1.2.candidate.json b/sso-mfa/k8s/keycape/descriptor-composition-1.2.candidate.json index a20b500..f98010b 100644 --- a/sso-mfa/k8s/keycape/descriptor-composition-1.2.candidate.json +++ b/sso-mfa/k8s/keycape/descriptor-composition-1.2.candidate.json @@ -6,39 +6,31 @@ "unchanged": true }, "check_config_digest": "sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5", - "complete_executable_descriptor_set": true, + "complete_executable_descriptor_set": false, "dispatch_permitted": false, - "execution_descriptors": { - "bind-env": "sha256:d2511dce3d2ff1d380f845920aa168e6fe09c0f0c382482a796a5bf13619905d", - "deliver-consumer-secret": "sha256:9c289f646516923a4c448a2d3cdd45b33115516e6a171b7955924d8b5d852d2b", - "deliver-secret": "sha256:75aaa7346aebdea2a0eb0ba993995515b5248ffefc34caf8e713525e9b58da85", - "ensure-groups": "sha256:4411b5cb8944ee76ca1ade62c0c246f061981fb18f0b9afd05934a8935598dc3", - "hand-over": "sha256:84f0fac401ea2f1e7c305fb1753a34838ac062655199fcfbbd998f39268c3000", - "mint-secret": "sha256:c92957227235db04953809d92b260d32c8b947472a8e8fd17a258ade3ae8da70", - "register-client": "sha256:3ccc8eed545cf31f8a48feb8afa0c1355e126d9549eef9737d92d0c1b87708a8" - }, + "execution_descriptors": null, "owner_review": false, "params": { "consumer_owner_repo": "railiance-apps" }, - "refusal": null, + "refusal": "ensure-groups: lane 'keycape-attended-case-change' does not match the 1.2 type", "review_requirement": "successor candidate only: new descriptor bytes need a reviewed successor revision under canon section 6 and owner review (NK-WP-0048-T16) before pinning", "schema": "netkingdom.descriptor-composition-candidate.v1", "selection": "explicit version root 1.2=security-case-type-revisions/1.2; default root unchanged", "status": "not_an_admission", "steps": [ { - "candidate_descriptor_digest": "sha256:4411b5cb8944ee76ca1ade62c0c246f061981fb18f0b9afd05934a8935598dc3", + "candidate_descriptor_digest": "sha256:ebc9d5b9167d18e6015f4bbeb9699e8d396d64fbfaf195142781f84f4f47a70a", "descriptor": { "actor": "human", "adapter": "net-kingdom/ensure-groups@v1", - "adapter_source": "sha256:87cc12ada32c4d1296070dd1e8586f35151fcb181ecd89d5d1fbb20c78b50a6f", + "adapter_source": "sha256:f93e68e1932c7ca293c9e69e2d0e1843d58d06a8d4892158fe7d1e0393a21ba0", "allowed_created": [ "service-group", "service-group-membership" ], "effect": "change", - "lane": "key-cape-oidc-login", + "lane": "keycape-attended-case-change", "owner": "net-kingdom", "retry_safe": false, "revocation_scopes": [ @@ -48,7 +40,9 @@ "target_parameter": "client_id", "version": "warden.case.execution.v1" }, - "findings": [], + "findings": [ + "lane 'keycape-attended-case-change' != type lane 'key-cape-oidc-login'" + ], "names_confirmed": null, "owner_confirmation_required": null, "step": "ensure-groups", @@ -122,16 +116,16 @@ "type_owner": "railiance-platform" }, { - "candidate_descriptor_digest": "sha256:d2511dce3d2ff1d380f845920aa168e6fe09c0f0c382482a796a5bf13619905d", + "candidate_descriptor_digest": "sha256:d5fcb20cf15bf7454890d2529180c582218e3df12584a99162e4b664b899caa0", "descriptor": { "actor": "human", "adapter": "net-kingdom/keycape-bind-env@v1", - "adapter_source": "sha256:cfc8a7f52494869ead07f9878526a27206c99b0f468673e2e7b111d29d175ddf", + "adapter_source": "sha256:dcd43f5ce1c4e829e1198faffc6bfb9cc610509f5176658f42c133accc0a7305", "allowed_created": [ "keycape-env-binding" ], "effect": "change", - "lane": "key-cape-oidc-login", + "lane": "keycape-attended-case-change", "owner": "net-kingdom", "retry_safe": false, "revocation_scopes": [ @@ -141,7 +135,9 @@ "target_parameter": "client_id", "version": "warden.case.execution.v1" }, - "findings": [], + "findings": [ + "lane 'keycape-attended-case-change' != type lane 'key-cape-oidc-login'" + ], "names_confirmed": { "by": "founder", "on": "2026-10-17", @@ -154,16 +150,16 @@ "type_owner": "net-kingdom" }, { - "candidate_descriptor_digest": "sha256:3ccc8eed545cf31f8a48feb8afa0c1355e126d9549eef9737d92d0c1b87708a8", + "candidate_descriptor_digest": "sha256:4ecc769f75433b66c7161f13c0fe7e5599633cf466d46cb6d8031f0456f426e7", "descriptor": { "actor": "human", "adapter": "net-kingdom/keycape-registration@v1", - "adapter_source": "sha256:8af23cb5d5fff963d2eff65d8b16dcfd395931ad860357846151019a70b2ed34", + "adapter_source": "sha256:b30f7abfbb5bf439c5ce067325b01c8b711076a38a6fb8484f55d5c8912921f3", "allowed_created": [ "keycape-client" ], "effect": "change", - "lane": "key-cape-oidc-login", + "lane": "keycape-attended-case-change", "owner": "net-kingdom", "retry_safe": false, "revocation_scopes": [ @@ -173,7 +169,9 @@ "target_parameter": "client_id", "version": "warden.case.execution.v1" }, - "findings": [], + "findings": [ + "lane 'keycape-attended-case-change' != type lane 'key-cape-oidc-login'" + ], "names_confirmed": null, "owner_confirmation_required": null, "step": "register-client", @@ -181,11 +179,11 @@ "type_owner": "net-kingdom" }, { - "candidate_descriptor_digest": "sha256:9c289f646516923a4c448a2d3cdd45b33115516e6a171b7955924d8b5d852d2b", + "candidate_descriptor_digest": "sha256:36281df8139fb1ec64b226d94f80eb592de5addab7063ff54c691c641e275fc7", "descriptor": { - "actor": "agent", + "actor": "human", "adapter": "net-kingdom/deliver-consumer-secret@v1", - "adapter_source": "sha256:8ec63233218f2f9571e1f0605ae29b4378f3126d0ff7f5192e35efa4291f72cd", + "adapter_source": "sha256:66f16dcb665216d09ef63a5ca654283a0292005ba4fb4d79c27eeb54b886a385", "allowed_created": [ "consumer-eso-sa", "consumer-secretstore", @@ -216,11 +214,11 @@ "type_owner": "{consumer_owner_repo}" }, { - "candidate_descriptor_digest": "sha256:84f0fac401ea2f1e7c305fb1753a34838ac062655199fcfbbd998f39268c3000", + "candidate_descriptor_digest": "sha256:1bce07a3229b7d0c7c730f35ce5b40fe471009a82de8dd53788ed970a9be1224", "descriptor": { "actor": "human", "adapter": "net-kingdom/apps-handover@v1", - "adapter_source": "sha256:ceaefd44168fa49d4ab1bb0f4870fe43ffd3b6a963b59d53970c97b1ed871155", + "adapter_source": "sha256:4439305b3edcd5d51c73e6438744d8895d69c1ccb58854aac79bb489d868cf48", "allowed_created": [ "handover-receipt" ], diff --git a/sso-mfa/k8s/keycape/descriptor-composition-1.3.candidate.json b/sso-mfa/k8s/keycape/descriptor-composition-1.3.candidate.json index 2aae558..d18b649 100644 --- a/sso-mfa/k8s/keycape/descriptor-composition-1.3.candidate.json +++ b/sso-mfa/k8s/keycape/descriptor-composition-1.3.candidate.json @@ -9,13 +9,13 @@ "complete_executable_descriptor_set": true, "dispatch_permitted": false, "execution_descriptors": { - "bind-env": "sha256:d2511dce3d2ff1d380f845920aa168e6fe09c0f0c382482a796a5bf13619905d", - "deliver-consumer-secret": "sha256:9c289f646516923a4c448a2d3cdd45b33115516e6a171b7955924d8b5d852d2b", + "bind-env": "sha256:d5fcb20cf15bf7454890d2529180c582218e3df12584a99162e4b664b899caa0", + "deliver-consumer-secret": "sha256:36281df8139fb1ec64b226d94f80eb592de5addab7063ff54c691c641e275fc7", "deliver-secret": "sha256:75aaa7346aebdea2a0eb0ba993995515b5248ffefc34caf8e713525e9b58da85", - "ensure-groups": "sha256:4411b5cb8944ee76ca1ade62c0c246f061981fb18f0b9afd05934a8935598dc3", - "hand-over": "sha256:84f0fac401ea2f1e7c305fb1753a34838ac062655199fcfbbd998f39268c3000", + "ensure-groups": "sha256:ebc9d5b9167d18e6015f4bbeb9699e8d396d64fbfaf195142781f84f4f47a70a", + "hand-over": "sha256:1bce07a3229b7d0c7c730f35ce5b40fe471009a82de8dd53788ed970a9be1224", "mint-secret": "sha256:c92957227235db04953809d92b260d32c8b947472a8e8fd17a258ade3ae8da70", - "register-client": "sha256:3ccc8eed545cf31f8a48feb8afa0c1355e126d9549eef9737d92d0c1b87708a8" + "register-client": "sha256:4ecc769f75433b66c7161f13c0fe7e5599633cf466d46cb6d8031f0456f426e7" }, "owner_review": false, "params": { @@ -28,17 +28,17 @@ "status": "not_an_admission", "steps": [ { - "candidate_descriptor_digest": "sha256:4411b5cb8944ee76ca1ade62c0c246f061981fb18f0b9afd05934a8935598dc3", + "candidate_descriptor_digest": "sha256:ebc9d5b9167d18e6015f4bbeb9699e8d396d64fbfaf195142781f84f4f47a70a", "descriptor": { "actor": "human", "adapter": "net-kingdom/ensure-groups@v1", - "adapter_source": "sha256:87cc12ada32c4d1296070dd1e8586f35151fcb181ecd89d5d1fbb20c78b50a6f", + "adapter_source": "sha256:f93e68e1932c7ca293c9e69e2d0e1843d58d06a8d4892158fe7d1e0393a21ba0", "allowed_created": [ "service-group", "service-group-membership" ], "effect": "change", - "lane": "key-cape-oidc-login", + "lane": "keycape-attended-case-change", "owner": "net-kingdom", "retry_safe": false, "revocation_scopes": [ @@ -52,7 +52,7 @@ "names_confirmed": null, "owner_confirmation_required": null, "step": "ensure-groups", - "type_lane": "key-cape-oidc-login", + "type_lane": "keycape-attended-case-change", "type_owner": "net-kingdom" }, { @@ -118,20 +118,20 @@ }, "owner_confirmation_required": null, "step": "deliver-secret", - "type_lane": null, + "type_lane": "platform-secret-delivery", "type_owner": "railiance-platform" }, { - "candidate_descriptor_digest": "sha256:d2511dce3d2ff1d380f845920aa168e6fe09c0f0c382482a796a5bf13619905d", + "candidate_descriptor_digest": "sha256:d5fcb20cf15bf7454890d2529180c582218e3df12584a99162e4b664b899caa0", "descriptor": { "actor": "human", "adapter": "net-kingdom/keycape-bind-env@v1", - "adapter_source": "sha256:cfc8a7f52494869ead07f9878526a27206c99b0f468673e2e7b111d29d175ddf", + "adapter_source": "sha256:dcd43f5ce1c4e829e1198faffc6bfb9cc610509f5176658f42c133accc0a7305", "allowed_created": [ "keycape-env-binding" ], "effect": "change", - "lane": "key-cape-oidc-login", + "lane": "keycape-attended-case-change", "owner": "net-kingdom", "retry_safe": false, "revocation_scopes": [ @@ -150,20 +150,20 @@ }, "owner_confirmation_required": null, "step": "bind-env", - "type_lane": "key-cape-oidc-login", + "type_lane": "keycape-attended-case-change", "type_owner": "net-kingdom" }, { - "candidate_descriptor_digest": "sha256:3ccc8eed545cf31f8a48feb8afa0c1355e126d9549eef9737d92d0c1b87708a8", + "candidate_descriptor_digest": "sha256:4ecc769f75433b66c7161f13c0fe7e5599633cf466d46cb6d8031f0456f426e7", "descriptor": { "actor": "human", "adapter": "net-kingdom/keycape-registration@v1", - "adapter_source": "sha256:8af23cb5d5fff963d2eff65d8b16dcfd395931ad860357846151019a70b2ed34", + "adapter_source": "sha256:b30f7abfbb5bf439c5ce067325b01c8b711076a38a6fb8484f55d5c8912921f3", "allowed_created": [ "keycape-client" ], "effect": "change", - "lane": "key-cape-oidc-login", + "lane": "keycape-attended-case-change", "owner": "net-kingdom", "retry_safe": false, "revocation_scopes": [ @@ -177,15 +177,15 @@ "names_confirmed": null, "owner_confirmation_required": null, "step": "register-client", - "type_lane": "key-cape-oidc-login", + "type_lane": "keycape-attended-case-change", "type_owner": "net-kingdom" }, { - "candidate_descriptor_digest": "sha256:9c289f646516923a4c448a2d3cdd45b33115516e6a171b7955924d8b5d852d2b", + "candidate_descriptor_digest": "sha256:36281df8139fb1ec64b226d94f80eb592de5addab7063ff54c691c641e275fc7", "descriptor": { - "actor": "agent", + "actor": "human", "adapter": "net-kingdom/deliver-consumer-secret@v1", - "adapter_source": "sha256:8ec63233218f2f9571e1f0605ae29b4378f3126d0ff7f5192e35efa4291f72cd", + "adapter_source": "sha256:66f16dcb665216d09ef63a5ca654283a0292005ba4fb4d79c27eeb54b886a385", "allowed_created": [ "consumer-eso-sa", "consumer-secretstore", @@ -212,15 +212,15 @@ }, "owner_confirmation_required": null, "step": "deliver-consumer-secret", - "type_lane": null, + "type_lane": "consumer-owner-deployment", "type_owner": "{consumer_owner_repo}" }, { - "candidate_descriptor_digest": "sha256:84f0fac401ea2f1e7c305fb1753a34838ac062655199fcfbbd998f39268c3000", + "candidate_descriptor_digest": "sha256:1bce07a3229b7d0c7c730f35ce5b40fe471009a82de8dd53788ed970a9be1224", "descriptor": { "actor": "human", "adapter": "net-kingdom/apps-handover@v1", - "adapter_source": "sha256:ceaefd44168fa49d4ab1bb0f4870fe43ffd3b6a963b59d53970c97b1ed871155", + "adapter_source": "sha256:4439305b3edcd5d51c73e6438744d8895d69c1ccb58854aac79bb489d868cf48", "allowed_created": [ "handover-receipt" ], @@ -245,7 +245,7 @@ }, "owner_confirmation_required": null, "step": "hand-over", - "type_lane": null, + "type_lane": "apps-consumer-handover", "type_owner": "{consumer_owner_repo}" } ], diff --git a/sso-mfa/k8s/keycape/test_admit_1_2_adapter_descriptors.py b/sso-mfa/k8s/keycape/test_admit_1_2_adapter_descriptors.py deleted file mode 100644 index a3d5d63..0000000 --- a/sso-mfa/k8s/keycape/test_admit_1_2_adapter_descriptors.py +++ /dev/null @@ -1,98 +0,0 @@ -"""Kernel admission of every 1.2 descriptor with fixture-only trust (NK48-R1-F1). - -Descriptor.decode alone missed that the kernel refuses an agent mint. Each of the -seven composed descriptors goes through Warden's own Executor.admit here, with the -real 1.2 type, the 1.2 forgejo example case and the reviewed browser validator. -Adapters are stand-ins carrying only the pinned reference and source; nothing is -dispatched. Admission here is a fixture, not owner admission or a pin. -""" -from dataclasses import replace -import importlib.util -import json -import os -from pathlib import Path -import tempfile -import unittest -from unittest import mock - -import yaml - -from warden.case_run import Adapter, Executor, Journal, digest -from warden.case_receipt_state import Refused -import warden.case_inputs as inputs - -ROOT = Path(__file__).parent -REPO = ROOT.parents[2] -spec = importlib.util.spec_from_file_location('compose_12_admit', ROOT / 'compose-1.2-descriptors.py') -c = importlib.util.module_from_spec(spec) -spec.loader.exec_module(c) - -TYPE_DIR = REPO / 'security-case-type-revisions/1.2/oidc-browser-relying-party' -ARTIFACT = json.loads((ROOT / 'descriptor-composition-1.2.candidate.json').read_text()) -EVIDENCE = {'fixture-acceptance', 'fixture-materialization', 'fixture-descriptor', 'fixture-session'} - - -def unused(*_): - raise AssertionError('admission must not dispatch') - - -class AdmitDescriptorSetTests(unittest.TestCase): - def setUp(self): - self.temp = tempfile.TemporaryDirectory() - self.addCleanup(self.temp.cleanup) - case = yaml.safe_load((TYPE_DIR / 'examples/forgejo.yaml').read_bytes()) - case['spec'].update(status='materialized', acceptance={ - 'at': '2026-10-17', 'record': 'fixture-only acceptance, not an owner record', - 'by': [{'name': 'fixture', 'role': 'owner'}, {'name': 'fixture', 'role': 'founder'}]}) - self.case_source = yaml.safe_dump(case).encode() - self.descriptors = dict(c.adapter_descriptors(ARTIFACT['check_config_digest'], - ARTIFACT['params']['consumer_owner_repo'])) - - def admit(self, desc, *, actor=None, lane=None): - desc_bytes = c.encode(desc) - typ = yaml.safe_load((TYPE_DIR / 'type.yaml').read_bytes()) - typ['spec']['execution_descriptors'] = {desc.step: digest(desc_bytes)} - adapter = Adapter(desc.adapter, desc.adapter_source, unused, unused) - executor = Executor(Journal(Path(self.temp.name) / desc.step), - {adapter.reference: adapter}, - lambda purpose, source, evidence: evidence in EVIDENCE, - browser_inputs=inputs.BrowserInputs(REPO)) - with mock.patch.dict(os.environ): - os.environ.pop('WARDEN_AGENT_ID', None) - return executor.admit(self.case_source, yaml.safe_dump(typ).encode(), desc_bytes, - b'fixture-only-catalog', principal='founder', actor=actor or desc.actor, - lane=lane or desc.lane, acceptance_evidence='fixture-acceptance', - materialization_evidence='fixture-materialization', - descriptor_evidence='fixture-descriptor', session_evidence='fixture-session') - - def test_composed_descriptors_are_the_committed_artifact(self): - self.assertEqual({s: digest(c.encode(d)) for s, d in self.descriptors.items()}, - ARTIFACT['execution_descriptors']) - - def test_every_1_2_descriptor_passes_kernel_admission(self): - self.assertEqual(len(self.descriptors), 7) - for step, desc in self.descriptors.items(): - with self.subTest(step=step): - admitted_desc, invocation, _ = self.admit(desc) - self.assertEqual(admitted_desc, desc) - self.assertEqual((invocation.step, invocation.owner, invocation.lane, - invocation.actor, invocation.target), - (desc.step, desc.owner, desc.lane, desc.actor, 'forgejo')) - - def test_mint_secret_admits_only_a_human_actor(self): - mint = self.descriptors['mint-secret'] - self.assertEqual((mint.effect, mint.actor), ('mint', 'human')) - with self.assertRaisesRegex(Refused, 'agent mint is forbidden'): - self.admit(replace(mint, actor='agent')) - with self.assertRaisesRegex(Refused, 'principal actor or lane mismatch'): - self.admit(mint, actor='agent') - - def test_wrong_lane_is_refused(self): - for step, desc in self.descriptors.items(): - with self.subTest(step=step), self.assertRaisesRegex( - Refused, 'principal actor or lane mismatch'): - self.admit(desc, lane='wrong-lane') - - -if __name__ == '__main__': - unittest.main() diff --git a/sso-mfa/k8s/keycape/test_admit_1_3_type.py b/sso-mfa/k8s/keycape/test_admit_1_3_type.py index d4ffb8b..084df94 100644 --- a/sso-mfa/k8s/keycape/test_admit_1_3_type.py +++ b/sso-mfa/k8s/keycape/test_admit_1_3_type.py @@ -74,7 +74,7 @@ class Admit13TypeTests(unittest.TestCase): ARTIFACT['params']['consumer_owner_repo'], version='1.3')) - def admit(self, desc, type_source): + def admit(self, desc, type_source, *, actor=None, lane=None): desc_bytes = c.encode(desc) adapter = Adapter(desc.adapter, desc.adapter_source, unused, unused) executor = Executor(Journal(Path(self.temp.name) / desc.step), @@ -84,7 +84,8 @@ class Admit13TypeTests(unittest.TestCase): with mock.patch.dict(os.environ): os.environ.pop('WARDEN_AGENT_ID', None) return executor.admit(self.case_source, type_source, desc_bytes, - b'fixture-only-catalog', principal='founder', actor=desc.actor, lane=desc.lane, + b'fixture-only-catalog', principal='founder', actor=actor or desc.actor, + lane=lane or desc.lane, acceptance_evidence='fixture-acceptance', materialization_evidence='fixture-materialization', descriptor_evidence='fixture-descriptor', session_evidence='fixture-session') @@ -129,6 +130,22 @@ class Admit13TypeTests(unittest.TestCase): Refused, 'no exact acknowledged machine descriptor'): self.admit(changed, acknowledged) + def test_mint_secret_admits_only_a_human_actor(self): + # Moved from test_admit_1_2_adapter_descriptors.py (NK48-R1-F1) in r5. + acknowledged = with_status('acknowledged') + mint = self.descriptors['mint-secret'] + self.assertEqual((mint.effect, mint.actor), ('mint', 'human')) + with pinned_13(), self.assertRaisesRegex(Refused, 'principal actor or lane mismatch'): + self.admit(mint, acknowledged, actor='agent') + + def test_wrong_lane_is_refused(self): + # Moved from test_admit_1_2_adapter_descriptors.py in r5. + acknowledged = with_status('acknowledged') + for step, desc in self.descriptors.items(): + with self.subTest(step=step), pinned_13(), self.assertRaisesRegex( + Refused, 'principal actor or lane mismatch'): + self.admit(desc, acknowledged, lane='wrong-lane') + def test_earlier_versions_carry_no_execution_map(self): for version in ('1.2',): path = REPO / f'security-case-type-revisions/{version}/oidc-browser-relying-party/type.yaml' diff --git a/sso-mfa/k8s/keycape/test_compose_1_2_descriptors.py b/sso-mfa/k8s/keycape/test_compose_1_2_descriptors.py index 0e194ed..bc15e8f 100644 --- a/sso-mfa/k8s/keycape/test_compose_1_2_descriptors.py +++ b/sso-mfa/k8s/keycape/test_compose_1_2_descriptors.py @@ -109,27 +109,43 @@ class ComposeTests(unittest.TestCase): c.artifact(CHECKER, PARAMS) self.assertEqual(path.read_bytes(), before) - def test_current_adapters_do_not_compose(self): - # Pins the present source findings; this test must change when they are fixed. + def test_1_2_composition_is_refused_on_the_r5_lanes(self): + # r5 (OW-1.3-C1) moved three net-kingdom steps to keycape-attended-case-change, + # which the acknowledged 1.2 type does not declare. 1.2 stays plan-only. doc = c.artifact(CHECKER, PARAMS) - # Complete in source with mint-secret's actor 'human'; still not an admission. + self.assertFalse(doc['complete_executable_descriptor_set']) + self.assertIsNone(doc['execution_descriptors']) + findings = {r['step']: r['findings'] for r in doc['steps'] if r['findings']} + self.assertEqual(findings, {step: ["lane 'keycape-attended-case-change' != type lane " + "'key-cape-oidc-login'"] + for step in ('ensure-groups', 'bind-env', 'register-client')}) + + def test_current_adapters_compose_for_1_3(self): + doc = c.artifact(CHECKER, PARAMS, version='1.3') self.assertTrue(doc['complete_executable_descriptor_set']) self.assertEqual(len(doc['execution_descriptors']), 7) self.assertIsNone(doc['refusal']) self.assertFalse(doc['admitted'] or doc['owner_review'] or doc['dispatch_permitted']) - findings = {r['step']: r['findings'] for r in doc['steps']} - self.assertEqual({s for s, f in findings.items() if f}, set()) - mint = next(r for r in doc['steps'] if r['step'] == 'mint-secret') - self.assertEqual(mint['descriptor']['actor'], 'human') - hand_over = next(r for r in doc['steps'] if r['step'] == 'hand-over') - self.assertEqual(hand_over['descriptor']['owner'], PARAMS['consumer_owner_repo']) - self.assertNotIn('owner', hand_over['names_confirmed']) + self.assertEqual({r['step'] for r in doc['steps'] if r['findings']}, set()) + by_step = {r['step']: r['descriptor'] for r in doc['steps']} + self.assertEqual({s: d['actor'] for s, d in by_step.items()}, + {s: 'human' for s in by_step}) # RAPPS-1.3-C1: no agent step remains + self.assertEqual({s: d['lane'] for s, d in by_step.items()}, { + 'ensure-groups': 'keycape-attended-case-change', + 'bind-env': 'keycape-attended-case-change', + 'register-client': 'keycape-attended-case-change', + 'mint-secret': 'keycape-client-secret-generate', + 'deliver-secret': 'platform-secret-delivery', + 'deliver-consumer-secret': 'consumer-owner-deployment', + 'hand-over': 'apps-consumer-handover'}) + self.assertEqual(by_step['hand-over']['owner'], PARAMS['consumer_owner_repo']) self.assertFalse(any(r['owner_confirmation_required'] for r in doc['steps'])) - confirmed = {r['step'] for r in doc['steps'] if r['names_confirmed']} - self.assertEqual(confirmed, {'mint-secret', 'deliver-secret', 'bind-env', - 'deliver-consumer-secret', 'hand-over'}) - self.assertTrue(all(r['names_confirmed']['by'] == 'founder' - for r in doc['steps'] if r['names_confirmed'])) + + def test_committed_1_3_artifact_matches_regeneration(self): + import json + committed = json.loads((ROOT / 'descriptor-composition-1.3.candidate.json').read_text()) + regenerated = c.artifact(committed['check_config_digest'], committed['params'], version='1.3') + self.assertEqual(json.loads(json.dumps(regenerated)), committed) def test_committed_artifact_matches_regeneration(self): import json