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 6ec7560..7479022 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 @@ -269,8 +269,11 @@ spec: when: client_type == confidential entry_point: reviewed adapter railiance-platform/keycape-deliver-secret, dispatched by Warden's case kernel; Platform's deliver capability applies ExternalSecret - sso/keycape-{client_id}-client (key client-secret) through the ESO store for - role external-secrets-keycape-{client_id}, from bao:platform/workloads/keycape/{client_id} + sso/keycape-{client_id}-client (key client-secret) through fixed ClusterSecretStore + openbao-keycape-{client_id}-issuer (cluster-scoped, restricted to namespace sso), + defined by Platform-controlled manifests/templates/keycape-issuer-eso.yaml + (sha256:e17498bd8a4c820041a4105e4efe9c60c6a50953db8f621ff54887080065c743), + using role external-secrets-keycape-{client_id}, from bao:platform/workloads/keycape/{client_id} auth: Platform's own delivery path on lane platform-secret-delivery, inside the human aal2 session; ESO reads custody through role external-secrets-keycape-{client_id}; Warden holds no Platform credential, and no value leaves custody diff --git a/sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml b/sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml new file mode 100644 index 0000000..d9f8d06 --- /dev/null +++ b/sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml @@ -0,0 +1,128 @@ +# TEST ONLY: synthetic subset shaped after ops-warden 5abde817 catalog. +# Four draft entries are hypothetically active; Apps session_lane remains unresolved. +# Not an authenticated owner catalog, promotion, login choice or native authority. +entries: +- id: key-cape-oidc-login + risk: standard + owner_repo: key-cape + warden_executes: false + reviewed: '2026-06-27' + status: active + delegation: + mode: interim + intended_owner: key-cape + reviewed: '2026-09-08' + blocked_on: fixture-only; no native authority + lane: login + title: Synthetic key-cape-oidc-login + need_keywords: + - key-cape-oidc-login + subsystem: synthetic source test + wiki_ref: fixture-only + canon_ref: fixture-only + workload_ref: + applicability: not-applicable + reason: Synthetic source test; no workload or native authority. +- id: keycape-client-secret-generate + risk: high + owner_repo: railiance-platform + warden_executes: false + reviewed: '2026-10-10' + status: active + delegation: + mode: native + intended_owner: railiance-platform + reviewed: '2026-10-10' + lane: secret + title: Synthetic keycape-client-secret-generate + need_keywords: + - keycape-client-secret-generate + subsystem: synthetic source test + wiki_ref: fixture-only + canon_ref: fixture-only + workload_ref: + applicability: not-applicable + reason: Synthetic source test; no workload or native authority. +- id: keycape-attended-case-change + lane: attended-change + session_lane: key-cape-oidc-login + risk: high + owner_repo: key-cape/net-kingdom + warden_executes: false + reviewed: '2026-10-17' + status: active + delegation: + mode: interim + intended_owner: key-cape + reviewed: '2026-10-17' + blocked_on: fixture-only; no native authority + title: Synthetic keycape-attended-case-change + need_keywords: + - keycape-attended-case-change + subsystem: synthetic source test + wiki_ref: fixture-only + canon_ref: fixture-only + workload_ref: + applicability: not-applicable + reason: Synthetic source test; no workload or native authority. +- id: platform-secret-delivery + risk: high + owner_repo: railiance-platform + warden_executes: false + reviewed: '2026-10-17' + status: active + delegation: + mode: native + intended_owner: railiance-platform + reviewed: '2026-10-17' + title: Synthetic platform-secret-delivery + need_keywords: + - platform-secret-delivery + subsystem: synthetic source test + wiki_ref: fixture-only + canon_ref: fixture-only + workload_ref: + applicability: not-applicable + reason: Synthetic source test; no workload or native authority. +- id: consumer-owner-deployment + lane: attended-change + session_lane: pending-owner-confirmation + risk: high + owner_repo: railiance-apps + warden_executes: false + reviewed: '2026-10-17' + status: active + delegation: + mode: native + intended_owner: railiance-apps + reviewed: '2026-10-17' + title: Synthetic consumer-owner-deployment + need_keywords: + - consumer-owner-deployment + subsystem: synthetic source test + wiki_ref: fixture-only + canon_ref: fixture-only + workload_ref: + applicability: not-applicable + reason: Synthetic source test; no workload or native authority. +- id: apps-consumer-handover + lane: attended-change + session_lane: pending-owner-confirmation + risk: high + owner_repo: railiance-apps + warden_executes: false + reviewed: '2026-10-17' + status: active + delegation: + mode: native + intended_owner: railiance-apps + reviewed: '2026-10-17' + title: Synthetic apps-consumer-handover + need_keywords: + - apps-consumer-handover + subsystem: synthetic source test + wiki_ref: fixture-only + canon_ref: fixture-only + workload_ref: + applicability: not-applicable + reason: Synthetic source test; no workload or native authority. 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 1b3e537..e0ca44b 100644 --- a/sso-mfa/k8s/keycape/test_admit_1_3_type.py +++ b/sso-mfa/k8s/keycape/test_admit_1_3_type.py @@ -8,7 +8,9 @@ fingerprint and knows only 1.1 and 1.2 (ops-warden case_inputs.PARAMETER_PINS), acknowledged 1.3 is still refused until ops-warden adds a 1.3 pin. That is an ops-warden owner change, and it is not made here. The tests show that 1.3's parameters carry 1.2's pinned fingerprint, and that with only that one pin added in -memory the committed map admits exactly the composed descriptors and nothing else. +memory the five resolvable steps admit. The two Apps attended steps remain refused +because their establishing login is unresolved in the synthetic catalog. Four real +draft lane shapes are hypothetically active in that fixture; no catalog is promoted. Trust is fixture-only, nothing is dispatched, and none of this is owner admission or an acknowledgment. """ @@ -37,6 +39,8 @@ spec.loader.exec_module(c) TYPE_DIR = REPO / 'security-case-type-revisions/1.3/oidc-browser-relying-party' TYPE_BYTES = (TYPE_DIR / 'type.yaml').read_bytes() ARTIFACT = json.loads((ROOT / 'descriptor-composition-1.3.candidate.json').read_text()) +CATALOG = (ROOT / 'kernel-catalog.fixture.yaml').read_bytes() +UNRESOLVED_APPS = {'deliver-consumer-secret', 'hand-over'} EVIDENCE = {'fixture-acceptance', 'fixture-materialization', 'fixture-descriptor', 'fixture-session'} @@ -74,12 +78,13 @@ class Admit13TypeTests(unittest.TestCase): ARTIFACT['params']['consumer_owner_repo'], version='1.3')) - def admit(self, desc, type_source, *, actor=None, lane=None, agent_id=None): + def admit(self, desc, type_source, *, actor=None, lane=None, agent_id=None, catalog=CATALOG): desc_bytes = c.encode(desc) 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, + lambda purpose, source, evidence: (evidence in EVIDENCE and + (purpose != 'catalog-routing-acceptance' or source == digest(CATALOG))), browser_inputs=inputs.BrowserInputs(REPO)) with mock.patch.dict(os.environ): if agent_id is None: @@ -87,7 +92,7 @@ class Admit13TypeTests(unittest.TestCase): else: os.environ['WARDEN_AGENT_ID'] = agent_id return executor.admit(self.case_source, type_source, desc_bytes, - b'fixture-only-catalog', principal='founder', actor=actor or desc.actor, + catalog, principal='founder', actor=actor or desc.actor, lane=lane or desc.lane, acceptance_evidence='fixture-acceptance', materialization_evidence='fixture-materialization', @@ -112,19 +117,34 @@ class Admit13TypeTests(unittest.TestCase): self.assertEqual(parameter_fingerprint(TYPE_BYTES), inputs.PARAMETER_PINS['1.2']) acknowledged = with_status('acknowledged') for step, desc in self.descriptors.items(): + if step in UNRESOLVED_APPS: + continue # The attended session gate correctly precedes parameter validation. with self.subTest(step=step), self.assertRaisesRegex( Refused, 'failed exact-version validation'): self.admit(desc, acknowledged) - def test_committed_map_admits_every_descriptor_once_acknowledged_and_pinned(self): + def test_committed_map_conditionally_admits_five_resolvable_steps(self): acknowledged = with_status('acknowledged') for step, desc in self.descriptors.items(): + if step in UNRESOLVED_APPS: + continue with self.subTest(step=step), pinned_13(): admitted, invocation, _ = self.admit(desc, acknowledged) self.assertEqual(admitted, desc) self.assertEqual((invocation.step, invocation.actor, invocation.target), (desc.step, desc.actor, 'forgejo')) + def test_unresolved_apps_sessions_refused_even_once_acknowledged_and_pinned(self): + for step in sorted(UNRESOLVED_APPS): + with self.subTest(step=step), pinned_13(), self.assertRaisesRegex( + Refused, 'does not resolve to an active, reviewed login lane'): + self.admit(self.descriptors[step], with_status('acknowledged')) + + def test_catalog_evidence_is_bound_to_exact_fixture_bytes(self): + mint = self.descriptors['mint-secret'] + with pinned_13(), self.assertRaisesRegex(Refused, 'independent owner evidence not verified'): + self.admit(mint, with_status('acknowledged'), catalog=CATALOG + b'\n# changed source\n') + def test_descriptor_outside_the_map_is_refused(self): acknowledged = with_status('acknowledged') mint = self.descriptors['mint-secret'] diff --git a/sso-mfa/k8s/keycape/test_keycape_registration_adapter.py b/sso-mfa/k8s/keycape/test_keycape_registration_adapter.py index 7f9786c..c403916 100644 --- a/sso-mfa/k8s/keycape/test_keycape_registration_adapter.py +++ b/sso-mfa/k8s/keycape/test_keycape_registration_adapter.py @@ -259,8 +259,13 @@ class RegistrationTests(unittest.TestCase): case_source = yaml.safe_dump(case).encode() typ = yaml.safe_load((root / 'security-case-types/oidc-browser-relying-party/type.yaml').read_bytes()) typ['spec']['execution_descriptors'] = {'register-client': digest(self.desc_bytes)} + # Fixture-only successor lane for the adapter's current descriptor. + # Canonical 1.1 still names the earlier login lane and is not edited. + typ['spec']['follow_through'] = [ + dict(step, lane=self.admitted.lane) if step['id'] == 'register-client' else step + for step in typ['spec']['follow_through']] type_source = yaml.safe_dump(typ).encode() - catalog = b'fixture-only-catalog' + catalog = (ROOT / 'kernel-catalog.fixture.yaml').read_bytes() sheet = json.dumps(['register-client', 'net-kingdom', 'forgejo', 'human', 'founder', 'keycape-attended-case-change', 'fixture-acceptance', 'fixture-materialization', 'fixture-descriptor', 'fixture-session'], separators=(',', ':')).encode() @@ -270,6 +275,8 @@ class RegistrationTests(unittest.TestCase): adapter = a.build(**dict(self.kw, admitted=invocation, case_source=case_source, case_acceptance='fixture-acceptance')) def fixture_verifier(purpose, source, evidence): + if purpose == 'catalog-routing-acceptance': + return source == digest(catalog) and evidence == 'fixture-descriptor' if purpose.startswith('session-revocation:'): _, lane, operation, attempt = purpose.split(':') return source == digest(case_source) and lane == invocation.lane and evidence == ( diff --git a/workplans/NK-WP-0048-security-case-types.md b/workplans/NK-WP-0048-security-case-types.md index 16914fc..2b3bb8e 100644 --- a/workplans/NK-WP-0048-security-case-types.md +++ b/workplans/NK-WP-0048-security-case-types.md @@ -1559,7 +1559,7 @@ T16 still owns actual composition and admission; F2/F3 follow below. ```task id: NK-WP-0048-T33 -status: todo +status: done priority: high state_hub_task_id: "5ace28ff-a4cb-5647-a8f6-3cd54bb9d0a5" ``` @@ -1584,7 +1584,7 @@ RPF-1.3-T4 and T16 admission/native requirements. ```task id: NK-WP-0048-T34 -status: todo +status: progress priority: high state_hub_task_id: "37f43c40-d014-544e-aa0b-5219dd4a6b93" ``` @@ -1607,3 +1607,44 @@ Custodian `docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r and `nk48-r5-f3-review-r1/`. Source review, owner acknowledgment and native proof remain distinct. T16's final composition/admission and T06's native first case are not completed by this refinement. + +## T35 — Align source admission fixtures with the authenticated catalog and attended-session API + +```task +id: NK-WP-0048-T35 +status: done +priority: high +state_hub_task_id: "cf1d6ab3-846e-53ea-8575-d6347b6f4769" +``` + +Completed 2026-10-17, source fixture compatibility only. Distinct from T32's +independent agent-mint regression repair: both remaining opaque catalog byte +placeholders now use one well-formed synthetic catalog whose exact bytes are +bound by the fixture evidence verifier. The fixture mirrors the six relevant +Warden lane shapes; four draft statuses are hypothetically active, while both +Apps establishing login references remain unresolved. No real catalog entry, +owner login choice or production parameter pin changes. + +Against the reviewed mode-aware Warden source now integrated at +`ae7bc73b4f4bac22454144959b870d161498e0af`, scratch validation passes 31 tests +and 53 subtests. Five resolvable 1.3 descriptors conditionally admit under +fixture-only acknowledgment and pinning; both Apps attended steps refuse before +execution. Changed catalog bytes refuse, all independent mint-guard tests still +reach their intended guard, and the registration fixture's actual kernel +run/rollback and uncertain-result paths pass with synthetic owner callbacks. +The registration fixture explicitly aligns its in-memory proposed type lane +with its current descriptor; canonical 1.1 is unchanged. + +The same tests against predecessor `117fec9241e7b985dfb187b992e173d0a5dc3700` +pass 31 tests and 51 subtests, with only the two new Apps refusal assertions +failing because that predecessor lacks the attended-session catalog check. +Tests target the reviewed current API; the parser or Apps gate is not weakened +for predecessor compatibility. Evidence and root source review: +`the-custodian/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-update-r1/` +and `nk48-catalog-fixture-publication-r2/`. Root canonical validation passes +31 tests / 53 subtests against Warden `922c26d` (the same reviewed code plus +one normal allocator-only source UUID commit). Publication receipts belong +in that packet. This records source +fixture work, not native T03, owner assent, exact-type acknowledgment, PIN-1, +composition/admission completion under T16 or the native first case under T06. +T33 and T34 retain their existing issuer-store and consumer-inverse work.