correct exact source case-type pin

Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a126e1-76f8-7643-8cf3-e93336539ade
This commit is contained in:
codex 2026-10-11 18:46:21 +02:00
parent 93ac156adf
commit 62bbdee811
3 changed files with 53 additions and 14 deletions

View file

@ -0,0 +1,30 @@
# Exact case-type pin correction — 2026-10-10
The broker's `CASE_TYPE` still required browser-RP @1.0 after the founder
authorized @1.1 and net-kingdom re-pinned the accepted Forgejo case. That made
the real case fail `case_type_mismatch` before any backend call.
`keycape_client_secret_generate.py` now accepts exactly
`oidc-browser-relying-party@1.1`; @1.0, arbitrary later versions and other
types remain refused. Existing exact-byte case-ref, materialized status,
client match, confidential/reserved identity, caller/root/agent boundary,
scoped grant, CAS0 create-once, silent receipt and revocation checks are unchanged.
Authority evidence: NK-WP-0048, 2026-10-10 founder go for 1.1; source type
`security-case-types/oidc-browser-relying-party/type.yaml` version 1.1,
acknowledged; accepted Forgejo case carries @1.1. No case status, live backend
or credential changed during this correction.
Regression uses the actual accepted Forgejo file shape with materialized
status solely in the offline fake test fixture. Real accepted (unmaterialized)
case remains refused. Ten subprocess CLI tests pass against a fake bao in an
isolated temporary tree, including silence, exact mismatch before any backend
call, reserved/agent/root identity guards, scoped token delivery, CAS0/race and
revocation. No live broker execution is claimed.
The materialization/run circularity remains an owner contract question:
MASON-WP-0010 describes mint-secret as needed before materialized, while
WARDEN-WP-0044 and this broker require materialized before mint-secret.
Owner review should define structural materialization with exact applied reader
receipts, then follow-through creates custody and verifies delivery. This
correction does not silently relax status gating or assert those receipts.

View file

@ -16,7 +16,7 @@ Steps (every one fails closed):
the broker issuer policy for this grant.
2. Re-derive the case reference from ``--case-file`` (sha256 over its exact
bytes) and require: it equals ``--case-ref``; type
``oidc-browser-relying-party@1.0``; status ``materialized``; client_type
``oidc-browser-relying-party@1.1``; status ``materialized``; client_type
``confidential``; ``parameters.client_id`` equals ``--client-id``, which
matches the type's pattern and is not a reserved entry.
3. Mint a 5-minute grant token through token role
@ -57,8 +57,8 @@ ISSUER_POLICY = 'credential-broker-keycape-client-secret-generate-issuer'
PREFIX = 'workloads/keycape'
RESERVED = {'secrets-engine-openbao'}
FIELD = 'CLIENT_SECRET'
CASE_TYPE = 'oidc-browser-relying-party@1.0'
# oidc-browser-relying-party@1.0 parameter client_id.
CASE_TYPE = 'oidc-browser-relying-party@1.1'
# oidc-browser-relying-party@1.1 parameter client_id.
CLIENT_ID_RE = re.compile(r'^[a-z0-9][a-z0-9-]{1,62}$')
CASE_REF_RE = re.compile(r'^[a-z0-9][a-z0-9-]*@sha256:[0-9a-f]{64}$')
# 48 random bytes -> 64 URL-safe characters, confirmed by key-cape 2026-10-10

View file

@ -53,17 +53,8 @@ FAKE_BAO = textwrap.dedent(f'''\
save(); sys.exit(9)
''')
CASE = textwrap.dedent('''\
apiVersion: netkingdom.io/security-case/v0.1
kind: SecurityCase
spec:
id: forgejo-netkingdom-oidc
type: oidc-browser-relying-party@1.0
status: materialized
parameters:
client_id: forgejo
client_type: confidential
''')
# Exact accepted Forgejo @1.1 shape; materialized only in this fake test.
CASE = '# First KnownSecurityCase of oidc-browser-relying-party 1.1 (NK-WP-0048-T06).\n# Parameters are the request the founder accepted on 2026-10-08\n# (key-cape docs/forgejo-oidc-client-request.yaml, KEY-WP-0038-T01); that\n# acceptance carries over as this case\'s acceptance. One difference: custody is\n# derived by the type (platform/workloads/keycape/forgejo), not the request\'s\n# proposed platform/workloads/forgejo/netkingdom-oidc-client. The founder\n# acknowledged that derivation with the type (memo r4, 2026-10-09).\n# Re-pinned from 1.0 to 1.1 on 2026-10-10 on the founder\'s go, before any\n# apply, so no 1.0 run overlaps (minor change: consumer-enforces-groups).\napiVersion: netkingdom.io/security-case/v0.1\nkind: SecurityCase\nspec:\n id: forgejo-netkingdom-oidc\n type: oidc-browser-relying-party@1.1\n status: materialized\n acceptance:\n at: "2026-10-08"\n record: "key-cape docs/forgejo-oidc-client-request.yaml (status accepted-2026-10-08, KEY-WP-0038-T01); type acknowledged via informed-decision memo nk-wp-0048-t05-oidc-browser-relying-party-r4, hub decision e418263e"\n by:\n - {role: owner, name: "founder (for railiance-apps)"}\n - {role: founder, name: founder}\n owner:\n repo: railiance-apps\n person: founder\n parameters:\n client_id: forgejo\n display_name: Forgejo source forge\n redirect_uris: https://forgejo.coulomb.social/user/oauth2/netkingdom/callback\n allowed_scopes: openid,profile,email,groups\n client_type: confidential\n mfa_required: "true"\n access_groups: forge-admins,forge-coulomb-owners,forge-coulomb-readers\n initial_members: forge-admins=tegwick;forge-coulomb-owners=tegwick\n consuming_service: forgejo\n consumer_owner_repo: railiance-apps\n'
class Generate(unittest.TestCase):
@ -155,6 +146,24 @@ class Generate(unittest.TestCase):
code, receipt = self.run_cli()
self.assertEqual((code, receipt['outcome']), (2, outcome))
def test_only_exact_acknowledged_type_version_is_admitted(self):
for type_ref in ('oidc-browser-relying-party@1.0',
'oidc-browser-relying-party@1.2',
'oidc-browser-relying-party@99.0',
'confidential-service-client@1.1'):
with self.subTest(type_ref=type_ref):
self.write_case(CASE.replace('oidc-browser-relying-party@1.1', type_ref))
self.state['calls'] = []
code, receipt = self.run_cli()
self.assertEqual((code, receipt['outcome']), (2, 'case_type_mismatch'))
self.assertEqual(self.state['calls'], [])
def test_exact_accepted_case_still_requires_materialization(self):
self.write_case(CASE.replace('status: materialized', 'status: accepted'))
code, receipt = self.run_cli()
self.assertEqual((code, receipt['outcome']), (2, 'case_not_materialized'))
self.assertEqual(self.state['calls'], [])
def test_agents_root_and_unbound_callers_refused(self):
code, receipt = self.run_cli(env_extra={'WARDEN_AGENT_ID': 'codex'})
self.assertEqual((code, receipt['outcome']), (2, 'agent_callers_refused'))