fix(identity): isolate checker environment and executable selection
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a140c4-354a-7823-8f0d-8f717397f786
This commit is contained in:
tegwick 2026-10-16 22:08:28 +02:00
parent 0394484b92
commit 916ba4b33e
4 changed files with 147 additions and 8 deletions

View file

@ -200,10 +200,10 @@ def deployment_bound(deployment, client):
def check_config(binary, config_text, env_names, policy_store):
"""KeyCape's startup checks on one registry: names and messages only, no values."""
command = [binary, 'check-config', '--config', '/dev/stdin', '--env-names', ','.join(env_names)]
command = [str(Path(binary).resolve()), 'check-config', '--config', '/dev/stdin', '--env-names', ','.join(env_names)]
if policy_store:
command.append('--policy-store')
result = subprocess.run(command, input=config_text.encode(), capture_output=True, timeout=60)
result = subprocess.run(command, input=config_text.encode(), capture_output=True, timeout=60, env={})
report = json.loads(result.stdout)
require(isinstance(report, dict) and isinstance(report.get('valid'), bool)
and report['valid'] == (result.returncode == 0), 'check_config_output_shape')

View file

@ -13,7 +13,12 @@ catalog/acceptance/run-sheet digests and target. The descriptor pins this adapte
and its complete local helper import closure together with the exact reviewed
`check-config` executable digest. Changing that digest changes the descriptor
source pin and descriptor bytes, requiring independently reviewed admission.
The fixed executable is rehashed before each attempt. The caller constructs the
The fixed executable is rehashed before each attempt. The helper resolves the
selected file to an absolute executable path and supplies an empty checker
environment, so ambient PATH and KeyCape URL overrides cannot select another
executable or alter validation of supplied bytes. Path resolution does not
establish immutable storage or eliminate replacement races; executable custody
and native runtime admission remain separate requirements. The caller constructs the
proposed invocation from exact source inputs, installs the fixed adapter once,
and calls normal `Executor.admit`; independently derived admission must match.
No callable replacement after admission is required or supported by this recipe.

View file

@ -0,0 +1,66 @@
"""Opt-in real-checker boundary regressions; no production inputs or checker stub.
Set KEYCAPE_TEST_CHECKER_BINARY and KEYCAPE_TEST_CHECKER_SHA256 to an independently
built artifact. Ordinary helper-only runs skip this class when none is supplied.
"""
from contextlib import chdir
import hashlib
import os
from pathlib import Path
import tempfile
import unittest
from unittest.mock import patch
from test_keycape_client import kc
VALID = '''issuer: https://issuer.example.invalid
port: 8080
privateKeyPem: /nonexistent/synthetic-key.pem
clients:
- clientId: synthetic-client
displayName: Synthetic client
redirectUris: [https://client.example.invalid/cb]
allowedScopes: [openid]
grantTypes: [authorization_code]
clientType: public
'''
class RealCheckerEnvironmentTests(unittest.TestCase):
@classmethod
def setUpClass(cls):
source = os.environ.get('KEYCAPE_TEST_CHECKER_BINARY')
if not source:
raise unittest.SkipTest('exact real checker artifact not supplied')
data = Path(source).read_bytes()
expected = os.environ.get('KEYCAPE_TEST_CHECKER_SHA256')
if expected != 'sha256:' + hashlib.sha256(data).hexdigest():
raise ValueError('real checker artifact digest differs')
cls.directory = tempfile.TemporaryDirectory()
cls.addClassCleanup(cls.directory.cleanup)
cls.binary = Path(cls.directory.name) / 'keycape'
cls.binary.write_bytes(data)
cls.binary.chmod(0o700)
def check(self, raw, binary=None):
return kc.check_config(str(binary or self.binary), raw, [], False)
def test_ambient_overrides_cannot_reject_supplied_valid_config(self):
with patch.dict(os.environ, {'KEYCAPE_ACCOUNT_PORTAL_URL': 'http://polluted.example.invalid',
'KEYCAPE_BROWSER_LOGOUT_URL': 'http://polluted.example.invalid/logout'}):
self.assertTrue(self.check(VALID)['valid'])
def test_ambient_overrides_cannot_admit_supplied_invalid_config(self):
invalid = VALID + 'accountPortalURL: http://invalid.example.invalid\nbrowserLogoutURL: http://invalid.example.invalid/logout\n'
with patch.dict(os.environ, {'KEYCAPE_ACCOUNT_PORTAL_URL': 'https://polluted.example.invalid',
'KEYCAPE_BROWSER_LOGOUT_URL': 'https://polluted.example.invalid/logout'}):
self.assertFalse(self.check(invalid)['valid'])
def test_relative_selected_artifact_is_not_replaced_by_path_decoy(self):
decoy = Path(self.directory.name) / 'decoy'
decoy.mkdir()
trap = decoy / 'keycape'
trap.write_text('#!/bin/sh\nexit 27\n')
trap.chmod(0o700)
with chdir(self.directory.name), patch.dict(os.environ, {'PATH': str(decoy)}):
self.assertTrue(self.check(VALID, binary=Path('keycape'))['valid'])

View file

@ -323,7 +323,7 @@ the rationale. The type is `acknowledged`; it becomes `implemented` with T06.
```task
id: NK-WP-0048-T06
status: wait
depends_on: [NK-WP-0048-T05, NK-WP-0048-T08, NK-WP-0048-T09, NK-WP-0048-T12, NK-WP-0048-T13, NK-WP-0048-T14, NK-WP-0048-T15, NK-WP-0048-T16, MASON-WP-0010-T03, WARDEN-WP-0044-T03]
depends_on: [NK-WP-0048-T05, NK-WP-0048-T08, NK-WP-0048-T09, NK-WP-0048-T12, NK-WP-0048-T13, NK-WP-0048-T14, NK-WP-0048-T15, NK-WP-0048-T16, NK-WP-0048-T17, NK-WP-0048-T18, MASON-WP-0010-T03, WARDEN-WP-0044-T03]
blocking_reason: "Type1.2 architecture has a confirmed source-only founder acknowledgment; canonical1.1 remains unchanged/default and the1.2 reference case stays proposed. Needs explicit version selection/reference-source integration without normal admission bypass, closed owner adapters plus global/native receipt verifier (WARDEN-WP-0044-T03), independently verified six-object structural materialization and CCR42 claims/created-only provenance, exact broker/case/source reconciliation, then separately authorized native mint/delivery and attended registry/consumer verification. Source acknowledgment, a bundle or local tests do not prove implemented/materialized/live status."
priority: high
state_hub_task_id: "d9b26bcf-a7c6-59a2-92a0-f913d76a203c"
@ -734,10 +734,11 @@ state_hub_task_id: "200bf500-5084-5ae7-be26-3033fe1f296e"
```
Owner: NetKingdom for descriptor/type source and exact checker artifact; Warden
independently owns global provenance/admission. Next action: identify the exact
reviewed checker executable artifact and native owner evidence/session callback
contracts, then assemble the complete step descriptor set and a proposed explicit
type version selection. T13's retained descriptor pins a synthetic checker only.
independently owns global provenance/admission. Next action: obtain review of the
exact checker source/artifact proposed by T17 and identify native owner evidence/
session callback contracts, then assemble the complete step descriptor set and
a proposed explicit type version selection. T13's historical descriptor retains
its synthetic checker; T17 supplies an artifact-bound proposal, not admission.
Completion requires separate owner source review of exact machine descriptors,
artifact/source/config pins, normal type/version admission and independent
evidence-verification boundaries. Warden d97652a refreshes producer validator
@ -746,3 +747,70 @@ machine descriptor into an acknowledged type is a separately reviewed source
change. Platform owns structural/mint/broker/delivery evidence; Apps owns actual
consumer login/group enforcement. Neither their assent nor native acceptance is
supplied by this task.
## T17 — Build and exercise the exact offline config-checker artifact
```task
id: NK-WP-0048-T17
status: done
priority: high
```
Completed 2026-10-16, isolated artifact/source fixture proof only. Built the
existing `key-cape/src/cmd/keycape` command from published clean source revision
`3c71226a1a7b8a2bdb710e9c497e1a1f7f8a3987`, source tree
`f0010b9cab17dbc3a787628a29657d98b900320f`, with cached Go 1.23.0,
CGO disabled, Linux/amd64 v1, trimmed paths, disabled VCS stamping and offline
read-only module selection. The full retained closure pins 108 source files,
9876 toolchain files and ten used module source directories. Three isolated
build locations produced identical 13633409-byte artifacts:
`sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5`.
Eight disposable checks use the actual executable and maintained helper/kernel,
with complete synthetic configs and a minimal environment: add/created-only
removal, normal pinned producer admission, rollback, lost-readback uncertainty,
stale revisions, substituted checker refusal, invalid startup config and missing
secret-env refusal. No checker stub or producer-pin override remains. The
artifact-bound descriptor proposal is
`sha256:76b1153d188062ece53fcd5e8d35290fdbfd02a69dd79d4b001fc42781afcdc3`.
Evidence: Custodian
`docs/changes/netkingdom-target-2026-10-16/browser-checker-artifact-next/`.
The first two build attempts succeeded but their closure collection failed;
both failures are retained, as is refusal of the older incomplete synthetic
fixture. No production config, credentials or cluster mutation was used.
This extracts completed build/fixture scope from T16 without claiming KeyCape
owner endorsement, native checker selection/storage custody, acknowledged type
integration, native evidence/session callbacks or attended browser acceptance.
T16 retains exact owner review and composition/admission scope. The attended
registration procedure remains unexercised.
## T18 — Make checker input and executable selection deterministic
```task
id: NK-WP-0048-T18
status: done
priority: high
```
Completed 2026-10-16, isolated source correction and real-artifact fixture proof
only; independent integration/publication remain separately recorded. KeyCape
source review identified non-secret `KEYCAPE_ACCOUNT_PORTAL_URL` and
`KEYCAPE_BROWSER_LOGOUT_URL` overrides in config.Load. The original helper
inherited ambient environment, allowing valid supplied configuration to be
rejected or invalid supplied URL configuration to be admitted. A bare relative
checker filename could also be replaced by PATH lookup after the adapter had
hashed the selected local file. All three counterexamples are retained against
the unchanged predecessor.
The existing helper now resolves the selected checker file to an absolute path
and passes an explicit empty subprocess environment. Three opt-in real-checker
regressions verify both URL-verdict directions and relative-file selection under
a non-checker PATH decoy. All 39 affected checks pass with the exact supplied
artifact, as do 11 real-artifact helper/kernel scenarios. Source evidence:
Custodian `docs/changes/netkingdom-target-2026-10-16/browser-checker-environment-r2/`.
Historical descriptors remain retained; the helper correction changes successor
source-composition pins and requires their separate review/admission. An absolute
path is not immutable custody and does not eliminate file replacement races;
T16 retains native executable storage/custody and callback/type admission.
No production configuration, credentials or cluster mutation was used.