Include exact registration dependencies and verification tests
This commit is contained in:
parent
5d7ce0dd69
commit
e5aaa0db51
10 changed files with 1023 additions and 0 deletions
|
|
@ -0,0 +1,43 @@
|
|||
{
|
||||
"started_at": "2026-10-14T23:33:03.922823+00:00",
|
||||
"reference": "forgejo.coulomb.social/coulomb/reuse-surface:cust81-private-reader-r1",
|
||||
"expected_digest": "sha256:2364b7e7859daccf9340a063654e2b09eed1bc27e0adf7b23cbb4b30135a7db0",
|
||||
"new_tag_preflight": "not_found",
|
||||
"source_primary_commit": "d36fc463bc3d4752ec87a176ac900eb3b4be1cc8",
|
||||
"deploy_executed": false,
|
||||
"credential_provision_executed": false,
|
||||
"raw_output_discarded": true,
|
||||
"status": "published_digest_verified",
|
||||
"push_exit_code": 0,
|
||||
"registry_manifest": {
|
||||
"schemaVersion": 2,
|
||||
"mediaType": "application/vnd.oci.image.index.v1+json",
|
||||
"digest": "sha256:2364b7e7859daccf9340a063654e2b09eed1bc27e0adf7b23cbb4b30135a7db0",
|
||||
"size": 856,
|
||||
"manifests": [
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"digest": "sha256:1a69354af2bc87d4baaecc4b02e13fda468efd3746024f440cc6d138beeee2ab",
|
||||
"size": 2002,
|
||||
"platform": {
|
||||
"architecture": "amd64",
|
||||
"os": "linux"
|
||||
}
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"digest": "sha256:6bf7cd034a08da96734032b6d79f52cf5f51006480928ec2d790c9d7993024cb",
|
||||
"size": 566,
|
||||
"annotations": {
|
||||
"vnd.docker.reference.digest": "sha256:1a69354af2bc87d4baaecc4b02e13fda468efd3746024f440cc6d138beeee2ab",
|
||||
"vnd.docker.reference.type": "attestation-manifest"
|
||||
},
|
||||
"platform": {
|
||||
"architecture": "unknown",
|
||||
"os": "unknown"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"finished_at": "2026-10-14T23:33:08.596844+00:00"
|
||||
}
|
||||
|
|
@ -0,0 +1,254 @@
|
|||
{
|
||||
"id": "CUST81-PRIVATE-READER-PROVISIONING-PREPARATION-R1",
|
||||
"revision": 1,
|
||||
"prepared_at": "2026-10-14T22:35:18.623823+00:00",
|
||||
"status": "prepared; owner review and exact credential/live-change authorization pending; not executable",
|
||||
"scope": "Enable native reuse-surface hosted acquisition of exactly the seven consented private repository capability indexes, without changing repository visibility.",
|
||||
"authorization": {
|
||||
"metadata_consent": "docs/assessments/netkingdom-2026-10-14/private-source-owner-consent-r1.json",
|
||||
"metadata_consent_sha256": "4a4602ad926e3af855d6ddba4731430d33e4a7432c507ba0a5193b9f43056b52",
|
||||
"disclosure_packet": "docs/assessments/netkingdom-2026-10-14/private-source-disclosure-proposal-r1.json",
|
||||
"disclosure_packet_sha256": "46ac4fe49a08054cc215aade2fbaf3b36ee334520997857ed84f530b52da9d35",
|
||||
"credential_provisioning_approved": false,
|
||||
"live_rollout_approved": false,
|
||||
"independent_owner_reviews": "pending; not authored or inferred"
|
||||
},
|
||||
"existing_reader_search": {
|
||||
"result": "No reuse-surface private-reader CCR or construction plan found in platform CCR directory or ops-mason plans. Existing CCR0005 is runtime writer/webhook custody; CCR0044/0045 concern caller writer access.",
|
||||
"precedent": "CCR-2026-0031 core-hub source read is dedicated read:repository, but estate-wide repository membership must not be reused for this seven-repository lane."
|
||||
},
|
||||
"proposed_identity": {
|
||||
"forgejo_user": "reuse-surface-source",
|
||||
"forgejo_org": "coulomb",
|
||||
"forgejo_team": "reuse-surface-source-readers",
|
||||
"restricted": true,
|
||||
"is_admin": false,
|
||||
"visibility": "private",
|
||||
"password": "random value retained only in attended process memory; never stored in evidence",
|
||||
"team_permission": "read",
|
||||
"includes_all_repositories": false,
|
||||
"can_create_org_repo": false,
|
||||
"units_map": {
|
||||
"repo.code": "read",
|
||||
"repo.actions": "none",
|
||||
"repo.packages": "none",
|
||||
"repo.issues": "none",
|
||||
"repo.ext_issues": "none",
|
||||
"repo.wiki": "none",
|
||||
"repo.pulls": "none",
|
||||
"repo.releases": "none",
|
||||
"repo.projects": "none",
|
||||
"repo.ext_wiki": "none"
|
||||
},
|
||||
"team_repositories": [
|
||||
"coulomb/rapp-openbao",
|
||||
"coulomb/rapp-policy-nexus",
|
||||
"coulomb/rapp-postgres",
|
||||
"coulomb/rapp-sbom-nexus",
|
||||
"coulomb/rapp-telemetry",
|
||||
"coulomb/rapp-tenant-engine",
|
||||
"coulomb/rapp-user-engine"
|
||||
],
|
||||
"token_scopes": [
|
||||
"read:repository"
|
||||
],
|
||||
"token_name_prefix": "reuse-surface-source-read-",
|
||||
"reuse_other_workload_token": false,
|
||||
"fail_on_differing_existing_identity_or_team": true,
|
||||
"enforcement": "Verify complete team membership and all effective user repository grants; no other team, collaborator or organisation grants may provide extra private read access."
|
||||
},
|
||||
"proposed_delivery": {
|
||||
"kv_mount": "platform",
|
||||
"kv_path": "platform/workloads/reuse/reuse-surface/forgejo-source-read",
|
||||
"field": "FORGEJO_SOURCE_TOKEN",
|
||||
"auth_env": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN",
|
||||
"eso_role": "reuse-surface-runtime-eso",
|
||||
"eso_identity": "reuse/reuse-surface-eso",
|
||||
"audience": "openbao",
|
||||
"ttl": "15m",
|
||||
"cluster_secret_store": "openbao-reuse",
|
||||
"store_namespace_condition": [
|
||||
"reuse"
|
||||
],
|
||||
"external_secret": "reuse/reuse-surface-runtime",
|
||||
"target_secret": "reuse/reuse-surface-env",
|
||||
"existing_consumption": "Deployment reuse/reuse-surface envFrom secretRef reuse-surface-env; environment variable available only after controlled rollout. Existing image and resource requests retained.",
|
||||
"policy_delta": {
|
||||
"file": "railiance-platform/openbao/policies/workload-kv-read-reuse-surface-runtime-eso.hcl",
|
||||
"add_exact_stanza": "path \"platform/data/workloads/reuse/reuse-surface/forgejo-source-read\" { capabilities = [\"read\"] }",
|
||||
"retain_existing_paths": true,
|
||||
"do_not_change": "Caller-facing workload-kv-read-reuse-surface-runtime policy or writer OIDC role; those remain unable to read this new path."
|
||||
},
|
||||
"external_secret_data_delta": {
|
||||
"secretKey": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN",
|
||||
"remoteRef": {
|
||||
"key": "workloads/reuse/reuse-surface/forgejo-source-read",
|
||||
"property": "FORGEJO_SOURCE_TOKEN"
|
||||
}
|
||||
},
|
||||
"delivery_design_reason": "Uses the existing namespace-limited ESO store and identity plus the existing envFrom interface. Separate new KV path keeps scoped writer callers from also acquiring private-source access. No new static ESO token or new store is needed.",
|
||||
"native_source_auth": {
|
||||
"auth_env": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN",
|
||||
"auth_header": "Authorization",
|
||||
"header_behavior": "Native federation _auth_headers prefixes a bare token with Bearer; owner must verify accepted Forgejo raw API route with this actual native implementation."
|
||||
}
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"repo": "rapp-openbao",
|
||||
"capability_ids": [
|
||||
"capability.railiance.openbao-package"
|
||||
],
|
||||
"consented_commit": "35672be138036c8433a5ac5de91e068c03c2b124",
|
||||
"native_raw_url": "https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-openbao/raw/registry/indexes/capabilities.yaml?ref=35672be138036c8433a5ac5de91e068c03c2b124",
|
||||
"index_sha256": "044efa3d02b90e1872f2516c581159d388a8080a689f76fcd78bb4d048d6ee64",
|
||||
"auth_env": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN"
|
||||
},
|
||||
{
|
||||
"repo": "rapp-policy-nexus",
|
||||
"capability_ids": [
|
||||
"capability.railiance.policy-publication-package"
|
||||
],
|
||||
"consented_commit": "088b78ac03b0cebeec34a69aae3569eb7ec2f9c3",
|
||||
"native_raw_url": "https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-policy-nexus/raw/registry/indexes/capabilities.yaml?ref=088b78ac03b0cebeec34a69aae3569eb7ec2f9c3",
|
||||
"index_sha256": "e68bdedf61eee9ca2026f46a2434ce384c26234616e74f7a5d08a06c75b3bb00",
|
||||
"auth_env": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN"
|
||||
},
|
||||
{
|
||||
"repo": "rapp-postgres",
|
||||
"capability_ids": [
|
||||
"capability.railiance.postgres-isolation-recovery"
|
||||
],
|
||||
"consented_commit": "a9f569c96d74aa384ac8d3ac4aa960e4061550b5",
|
||||
"native_raw_url": "https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-postgres/raw/registry/indexes/capabilities.yaml?ref=a9f569c96d74aa384ac8d3ac4aa960e4061550b5",
|
||||
"index_sha256": "d939333eed9d85172e006f526c445fbe2602a721d53f7c0d14bc68286ae08776",
|
||||
"auth_env": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN"
|
||||
},
|
||||
{
|
||||
"repo": "rapp-sbom-nexus",
|
||||
"capability_ids": [
|
||||
"capability.railiance.sbom-runtime-package"
|
||||
],
|
||||
"consented_commit": "adbedd349e8df97be43a1f57db7095af45ff29d1",
|
||||
"native_raw_url": "https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-sbom-nexus/raw/registry/indexes/capabilities.yaml?ref=adbedd349e8df97be43a1f57db7095af45ff29d1",
|
||||
"index_sha256": "d2ee0f193d2198bf8f16ce64c90ae7664e59a2bc307ad711426af3fd47deb057",
|
||||
"auth_env": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN"
|
||||
},
|
||||
{
|
||||
"repo": "rapp-telemetry",
|
||||
"capability_ids": [
|
||||
"capability.railiance.telemetry-package"
|
||||
],
|
||||
"consented_commit": "f158307e24b86bab88a1af4830ff6b4f85afdeb5",
|
||||
"native_raw_url": "https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-telemetry/raw/registry/indexes/capabilities.yaml?ref=f158307e24b86bab88a1af4830ff6b4f85afdeb5",
|
||||
"index_sha256": "ee8801f143ed5cf064205710a61c2bb24efc6ea8585d5786d6b679426ec4c1d4",
|
||||
"auth_env": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN"
|
||||
},
|
||||
{
|
||||
"repo": "rapp-tenant-engine",
|
||||
"capability_ids": [
|
||||
"capability.railiance.tenant-engine-package"
|
||||
],
|
||||
"consented_commit": "9bdc86572728783a5ed94266f04f7b12d414c557",
|
||||
"native_raw_url": "https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-tenant-engine/raw/registry/indexes/capabilities.yaml?ref=9bdc86572728783a5ed94266f04f7b12d414c557",
|
||||
"index_sha256": "d8e9c1282350b5d2fd4f4d69712aa7a5fbcd2367bb1a7fad1b6eb16a97e5d732",
|
||||
"auth_env": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN"
|
||||
},
|
||||
{
|
||||
"repo": "rapp-user-engine",
|
||||
"capability_ids": [
|
||||
"capability.railiance.user-engine-package"
|
||||
],
|
||||
"consented_commit": "f35108f9e1b8702fb6c526fe42b2fec5ca032738",
|
||||
"native_raw_url": "https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-user-engine/raw/registry/indexes/capabilities.yaml?ref=f35108f9e1b8702fb6c526fe42b2fec5ca032738",
|
||||
"index_sha256": "95b4ed2d0b189f6847b1c0899f740d1da46933a8f40491e1a02f2d4e57c819fc",
|
||||
"auth_env": "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN"
|
||||
}
|
||||
],
|
||||
"owner_execution_route": {
|
||||
"credential_owner": "railiance-platform",
|
||||
"forgejo_owner": "railiance-forge",
|
||||
"delivery_manifest_and_rollout_owner": "railiance-apps",
|
||||
"consumer_owner": "reuse-surface",
|
||||
"construction_planner": "ops-mason",
|
||||
"routing_owner": "ops-warden (catalog review only; not secret issuer)",
|
||||
"smallest_platform_handoff": "Assign new platform CCR and an owner construction plan covering the exact seven-repo restricted identity, separate KV path, existing ESO policy/data deltas, controlled same-image rollout, and positive/negative receipts. Adapt core-hub provisioning logic with includes_all_repositories=false and exact team repository reconciliation; do not execute unchanged core-hub script. Return CCR/plan references and metadata receipts, never PAT value.",
|
||||
"attended_bootstrap_precedent": "warden access openbao-platform-admin-login --exec -- <platform-owned reviewed silent bootstrap>; bootstrap obtains Forgejo admin through existing forgejo-admin-api-token route, writes PAT to OpenBao from stdin, captures/discards raw output, and returns bounded receipt. New command is not yet implemented or approved.",
|
||||
"catalog_new_lane_proposed": "reuse-surface-forgejo-source-read (owner allocates and reviews; not present or executable yet)"
|
||||
},
|
||||
"verification": {
|
||||
"pre_apply": [
|
||||
"Pin exact CCR and plan revision/digest, owner reviews, founder disposition and rollout artifacts. Resolve existing identity/team/path conflicts before any mutation.",
|
||||
"Confirm live namespace/Deployment and its image digest, current ESO identity/binding and secret field presence through safe suppressing helper; no Secret dump.",
|
||||
"Check CPU headroom and rollout strategy/PVC constraints; retain exact prior source manifest/image and server dry-run/client diff."
|
||||
],
|
||||
"positive": [
|
||||
"PAT metadata scope exactly read:repository; exact seven team repos with no other private grants.",
|
||||
"Actual native fetch_remote_index_text from hub process with the approved auth_env fetches all seven pinned raw index URLs; validate schema, one expected capability each and exact index SHA256. No token or fetched private source output.",
|
||||
"ESO Ready and materialized key present checked without values; Deployment Ready on same pinned image after controlled rollout.",
|
||||
"Register private sources only after credential and native fetch receipts; fresh hosted federation then proves all70 reviewed IDs exactly once and matching metadata."
|
||||
],
|
||||
"negative": [
|
||||
"Reader cannot fetch a known private repository outside the seven (preselected by platform using metadata, no unauthorized body retention).",
|
||||
"Forgejo writes, admin/user operations and non-code unit access denied; probe design must avoid creating writes if an unexpected permission exists.",
|
||||
"Default identity and scoped writer OIDC caller cannot read new KV path; ESO role cannot read any other sibling path, and wrong SA/namespace/audience cannot authenticate.",
|
||||
"Native missing-auth and anonymous private-index fetch fail closed; no fallback to admin/personal/core-hub credentials."
|
||||
],
|
||||
"receipts": "Sanitized IDs, repo names, scopes, hashes, status booleans and timestamps only; no credentials, raw errors, private index bodies or Secret objects."
|
||||
},
|
||||
"rollback": {
|
||||
"immediate": "Revoke this dedicated PAT and remove exact new team/user grants. Disable only private registrations created by the associated attempt after reconciling ambiguous requests; preserve all public registrations.",
|
||||
"delivery": "Remove only new ES data item and exact new ESO policy stanza, restore prior source revision through owner path, delete only new materialized token key if needed using suppressed owner helper, then roll same-image hub. Do not delete reuse-surface-env or rotate existing writer/webhook fields.",
|
||||
"preserve_evidence": "Retain failed/partial outcomes, reconcile any ambiguous identity or token creation; never blindly remint after an observation timeout."
|
||||
},
|
||||
"cost_estimate_eur": 0,
|
||||
"risk_estimate": "read:repository plus repo.code read exposes entire source code of the seven private repos to the hub process, not just registry files; Forgejo permissions are repository-wide. The approved public output is only pinned capability metadata. New hub credential and rollout need separate owner review/authorization. Existing ESO role reads new exact path; same namespace operators can reference the store. Rollout may briefly affect single-instance SQLite service if capacity/PVC sharing prevents surge; headroom and owner rollout plan required. No new billed infrastructure proposed.",
|
||||
"unresolved": [
|
||||
"Platform allocates CCR and exact script/manifests with approved revision/digest and verifies restricted account is not implicitly granted private access elsewhere.",
|
||||
"Platform/Forgejo/reuse/railiance-apps owners perform their own reviews; credential provisioning and production rollout approval is separate from recorded metadata disclosure consent.",
|
||||
"No actual Forgejo identity/token/OpenBao path provisioning, live binding survey, native hub fetch or rollout receipt exists in this preparation."
|
||||
],
|
||||
"evidence_sources": [
|
||||
{
|
||||
"path": "/home/worsch/railiance-platform/credential-change-requests/CCR-2026-0005-reuse-surface-runtime-secrets-lane.yaml",
|
||||
"sha256": "8259abf8f044caab94414523c53bdb062d95c595b6c4d4590a4bb92ad703d22c"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/railiance-platform/credential-change-requests/CCR-2026-0031-core-hub-forgejo-source-read.yaml",
|
||||
"sha256": "fcc0b9fe75e4ac75c554967f2466cb225814f934d6f01c310b626779fa0e8b4a"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/railiance-platform/scripts/provision-core-hub-forgejo-source.py",
|
||||
"sha256": "2550e050a3c8de72d2eecf3db8bc27b3fb70ac4cb206364fdf65d6f9c8ab8f52"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/railiance-platform/openbao/policies/workload-kv-read-reuse-surface-runtime-eso.hcl",
|
||||
"sha256": "fab29572fe82fc6674837e430f616409070f97a3867a411b7e21b5141dfaba20"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/railiance-platform/openbao/policies/workload-kv-read-reuse-surface-runtime.hcl",
|
||||
"sha256": "53dd5eecc94fd8d7fc281a858f5d9fc27c8c895fd79161fd113cd3a1387a1257"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/railiance-platform/argocd/platform-addons/openbao-secretstore/openbao-reuse.clustersecretstore.yaml",
|
||||
"sha256": "b1fb08ad2f15624b8d97b797bf8152a91ea0e66457d05f9058b95281624bf378"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/railiance-apps/manifests/reuse-surface-runtime-externalsecret.yaml",
|
||||
"sha256": "e036084aec9fad3bea63cc0d591c3b40a392ab9f54d9226a2d2b3fd7194d8a47"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/railiance-apps/charts/reuse-surface/templates/deployment.yaml",
|
||||
"sha256": "c20283d6db8c93fa182ef79ada53cbf03a53fe6358c7334ebc82feb94465ad54"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/reuse-surface/reuse_surface/federation.py",
|
||||
"sha256": "b6bde1e0942189e422f0c87f226ae28027f0dff1a29887a55eb88c8eaafe58ce"
|
||||
}
|
||||
],
|
||||
"consented_revision_preflight": {
|
||||
"path": "docs/assessments/netkingdom-2026-10-14/private-source-consented-revision-preflight.json",
|
||||
"sha256": "9757a0e94aef7adab4456a7e259243e0f039f1d2a9c9826d5685bc52a7a8a19f",
|
||||
"result": "Main agent verified all seven HEAD file hashes and remote main match owner-consented commits; refresh if owner execution occurs after a source revision changes."
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
{
|
||||
"id": "CUST81-PRIVATE-METADATA-DISCLOSURE-R1",
|
||||
"revision": 1,
|
||||
"created_at": "2026-10-14T22:22:36.592716+00:00",
|
||||
"status": "prepared for source-owner review; not approved or executable",
|
||||
"disclosure": "Public discovery of the exact reviewed capability metadata and entries identified by these hashes; repository visibility stays private. No private package index, source code or credential content is included.",
|
||||
"reader_scope": "Dedicated hub-only repository-read access to exactly these seven repositories. No repository write, administrative or unrelated-repository access.",
|
||||
"sources": [
|
||||
{
|
||||
"repo": "rapp-openbao",
|
||||
"capability_ids": [
|
||||
"capability.railiance.openbao-package"
|
||||
],
|
||||
"commit": "35672be138036c8433a5ac5de91e068c03c2b124",
|
||||
"remote_main": "35672be138036c8433a5ac5de91e068c03c2b124",
|
||||
"published_revision_verified": true,
|
||||
"files": [
|
||||
{
|
||||
"path": "registry/indexes/capabilities.yaml",
|
||||
"sha256": "044efa3d02b90e1872f2516c581159d388a8080a689f76fcd78bb4d048d6ee64",
|
||||
"matches_committed": true
|
||||
},
|
||||
{
|
||||
"path": "registry/capabilities/capability.railiance.openbao-package.md",
|
||||
"sha256": "9de890da536c34ac2b50576e2c4a9c03792c8a07545d2a0566ace68ac10a293e",
|
||||
"matches_committed": true
|
||||
}
|
||||
],
|
||||
"source_owner_consent": "pending",
|
||||
"source_owner_consent_ref": null
|
||||
},
|
||||
{
|
||||
"repo": "rapp-policy-nexus",
|
||||
"capability_ids": [
|
||||
"capability.railiance.policy-publication-package"
|
||||
],
|
||||
"commit": "088b78ac03b0cebeec34a69aae3569eb7ec2f9c3",
|
||||
"remote_main": "088b78ac03b0cebeec34a69aae3569eb7ec2f9c3",
|
||||
"published_revision_verified": true,
|
||||
"files": [
|
||||
{
|
||||
"path": "registry/indexes/capabilities.yaml",
|
||||
"sha256": "e68bdedf61eee9ca2026f46a2434ce384c26234616e74f7a5d08a06c75b3bb00",
|
||||
"matches_committed": true
|
||||
},
|
||||
{
|
||||
"path": "registry/capabilities/capability.railiance.policy-publication-package.md",
|
||||
"sha256": "21415ed5e89dcf8bd2df0c8bc99b07d2c87173809c37fa2582de6d336918a126",
|
||||
"matches_committed": true
|
||||
}
|
||||
],
|
||||
"source_owner_consent": "pending",
|
||||
"source_owner_consent_ref": null
|
||||
},
|
||||
{
|
||||
"repo": "rapp-postgres",
|
||||
"capability_ids": [
|
||||
"capability.railiance.postgres-isolation-recovery"
|
||||
],
|
||||
"commit": "a9f569c96d74aa384ac8d3ac4aa960e4061550b5",
|
||||
"remote_main": "a9f569c96d74aa384ac8d3ac4aa960e4061550b5",
|
||||
"published_revision_verified": true,
|
||||
"files": [
|
||||
{
|
||||
"path": "registry/indexes/capabilities.yaml",
|
||||
"sha256": "d939333eed9d85172e006f526c445fbe2602a721d53f7c0d14bc68286ae08776",
|
||||
"matches_committed": true
|
||||
},
|
||||
{
|
||||
"path": "registry/capabilities/capability.railiance.postgres-isolation-recovery.md",
|
||||
"sha256": "64503872abb24bb28f291264e860ab094c6abb721fbfe90b47258ad13b77af44",
|
||||
"matches_committed": true
|
||||
}
|
||||
],
|
||||
"source_owner_consent": "pending",
|
||||
"source_owner_consent_ref": null
|
||||
},
|
||||
{
|
||||
"repo": "rapp-sbom-nexus",
|
||||
"capability_ids": [
|
||||
"capability.railiance.sbom-runtime-package"
|
||||
],
|
||||
"commit": "adbedd349e8df97be43a1f57db7095af45ff29d1",
|
||||
"remote_main": "adbedd349e8df97be43a1f57db7095af45ff29d1",
|
||||
"published_revision_verified": true,
|
||||
"files": [
|
||||
{
|
||||
"path": "registry/indexes/capabilities.yaml",
|
||||
"sha256": "d2ee0f193d2198bf8f16ce64c90ae7664e59a2bc307ad711426af3fd47deb057",
|
||||
"matches_committed": true
|
||||
},
|
||||
{
|
||||
"path": "registry/capabilities/capability.railiance.sbom-runtime-package.md",
|
||||
"sha256": "1a15e37bd3d19c4c89ef25803667937f549ade1cd27bedfa85132ac48ffe3f48",
|
||||
"matches_committed": true
|
||||
}
|
||||
],
|
||||
"source_owner_consent": "pending",
|
||||
"source_owner_consent_ref": null
|
||||
},
|
||||
{
|
||||
"repo": "rapp-telemetry",
|
||||
"capability_ids": [
|
||||
"capability.railiance.telemetry-package"
|
||||
],
|
||||
"commit": "f158307e24b86bab88a1af4830ff6b4f85afdeb5",
|
||||
"remote_main": "f158307e24b86bab88a1af4830ff6b4f85afdeb5",
|
||||
"published_revision_verified": true,
|
||||
"files": [
|
||||
{
|
||||
"path": "registry/indexes/capabilities.yaml",
|
||||
"sha256": "ee8801f143ed5cf064205710a61c2bb24efc6ea8585d5786d6b679426ec4c1d4",
|
||||
"matches_committed": true
|
||||
},
|
||||
{
|
||||
"path": "registry/capabilities/capability.railiance.telemetry-package.md",
|
||||
"sha256": "a67dbf6905223c04b1c003d5f967841038743c8c1479812f5cf0bb55ad6cee77",
|
||||
"matches_committed": true
|
||||
}
|
||||
],
|
||||
"source_owner_consent": "pending",
|
||||
"source_owner_consent_ref": null
|
||||
},
|
||||
{
|
||||
"repo": "rapp-tenant-engine",
|
||||
"capability_ids": [
|
||||
"capability.railiance.tenant-engine-package"
|
||||
],
|
||||
"commit": "9bdc86572728783a5ed94266f04f7b12d414c557",
|
||||
"remote_main": "9bdc86572728783a5ed94266f04f7b12d414c557",
|
||||
"published_revision_verified": true,
|
||||
"files": [
|
||||
{
|
||||
"path": "registry/indexes/capabilities.yaml",
|
||||
"sha256": "d8e9c1282350b5d2fd4f4d69712aa7a5fbcd2367bb1a7fad1b6eb16a97e5d732",
|
||||
"matches_committed": true
|
||||
},
|
||||
{
|
||||
"path": "registry/capabilities/capability.railiance.tenant-engine-package.md",
|
||||
"sha256": "e7cf0485cfdb6f497281638d81f908b5c2fe1053125c6ef28130504909d1d49e",
|
||||
"matches_committed": true
|
||||
}
|
||||
],
|
||||
"source_owner_consent": "pending",
|
||||
"source_owner_consent_ref": null
|
||||
},
|
||||
{
|
||||
"repo": "rapp-user-engine",
|
||||
"capability_ids": [
|
||||
"capability.railiance.user-engine-package"
|
||||
],
|
||||
"commit": "f35108f9e1b8702fb6c526fe42b2fec5ca032738",
|
||||
"remote_main": "f35108f9e1b8702fb6c526fe42b2fec5ca032738",
|
||||
"published_revision_verified": true,
|
||||
"files": [
|
||||
{
|
||||
"path": "registry/indexes/capabilities.yaml",
|
||||
"sha256": "95b4ed2d0b189f6847b1c0899f740d1da46933a8f40491e1a02f2d4e57c819fc",
|
||||
"matches_committed": true
|
||||
},
|
||||
{
|
||||
"path": "registry/capabilities/capability.railiance.user-engine-package.md",
|
||||
"sha256": "84ec4fdce99134c88c110104cac93bdea36cffb5ba34e046b9ad161fb860dce2",
|
||||
"matches_committed": true
|
||||
}
|
||||
],
|
||||
"source_owner_consent": "pending",
|
||||
"source_owner_consent_ref": null
|
||||
}
|
||||
],
|
||||
"runtime_auth_env": null,
|
||||
"native_hub_fetch_verification": "pending platform provisioning",
|
||||
"authority": "Owner decisions and custody provisioning must be verified before registering any of these sources. No approval is inferred from preparing this packet."
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
{
|
||||
"id": "CUST81-PRIVATE-METADATA-CONSENT-R1",
|
||||
"recorded_at": "2026-10-14T22:31:15.289746+00:00",
|
||||
"proposal_id": "CUST81-PRIVATE-METADATA-DISCLOSURE-R1",
|
||||
"proposal_revision": 1,
|
||||
"proposal_sha256": "46ac4fe49a08054cc215aade2fbaf3b36ee334520997857ed84f530b52da9d35",
|
||||
"proposal_path": "docs/assessments/netkingdom-2026-10-14/private-source-disclosure-proposal-r1.json",
|
||||
"consent_status": "approved",
|
||||
"approved_by": "Bernd Worsch, founder and owner of all seven repositories",
|
||||
"authority": "GOVERN @ estate; source-owner metadata disclosure consent",
|
||||
"user_statement": "As owner of all seven repositories, I approve public disclosure of the capability metadata specified in CUST81-PRIVATE-METADATA-DISCLOSURE-R1.",
|
||||
"repositories": [
|
||||
"rapp-openbao",
|
||||
"rapp-policy-nexus",
|
||||
"rapp-postgres",
|
||||
"rapp-sbom-nexus",
|
||||
"rapp-telemetry",
|
||||
"rapp-tenant-engine",
|
||||
"rapp-user-engine"
|
||||
],
|
||||
"approved_disclosure": "Public discovery of the exact reviewed capability metadata and entries identified by these hashes; repository visibility stays private. No private package index, source code or credential content is included.",
|
||||
"credential_provisioning_approved": false,
|
||||
"repository_visibility_change_approved": false,
|
||||
"independent_owner_reviews": "Not authored or inferred by this receipt",
|
||||
"source": "Explicit user approval in this conversation",
|
||||
"state_hub_decision_id": "d8f8f216-ac94-4295-97f3-825fd21254ea",
|
||||
"messages": [
|
||||
{
|
||||
"to_agent": "rapp-openbao",
|
||||
"id": "987e2b25-5243-4bfd-ac0d-b162c1dc0c05",
|
||||
"created_at": "2026-10-14T22:31:16.183455Z"
|
||||
},
|
||||
{
|
||||
"to_agent": "rapp-policy-nexus",
|
||||
"id": "14218fa4-d983-4700-8b9c-d31cab75b74e",
|
||||
"created_at": "2026-10-14T22:31:19.332904Z"
|
||||
},
|
||||
{
|
||||
"to_agent": "rapp-postgres",
|
||||
"id": "c603570b-3e00-4dbf-bca2-a1312ed3e175",
|
||||
"created_at": "2026-10-14T22:31:21.817440Z"
|
||||
},
|
||||
{
|
||||
"to_agent": "rapp-sbom-nexus",
|
||||
"id": "4a0a3d50-aaf9-4bf6-9aae-dc2e0e7777f1",
|
||||
"created_at": "2026-10-14T22:31:24.433679Z"
|
||||
},
|
||||
{
|
||||
"to_agent": "rapp-telemetry",
|
||||
"id": "f6935323-137f-41fe-b063-6aa52e8963fa",
|
||||
"created_at": "2026-10-14T22:31:28.201484Z"
|
||||
},
|
||||
{
|
||||
"to_agent": "rapp-tenant-engine",
|
||||
"id": "c7021880-95fc-488c-b7de-02cf149c16ad",
|
||||
"created_at": "2026-10-14T22:31:31.024224Z"
|
||||
},
|
||||
{
|
||||
"to_agent": "rapp-user-engine",
|
||||
"id": "7986ba55-61aa-4bf1-87a7-d34c2d473a60",
|
||||
"created_at": "2026-10-14T22:31:34.354641Z"
|
||||
},
|
||||
{
|
||||
"to_agent": "railiance-platform",
|
||||
"id": "72c6a148-dd75-4488-a0fb-81579d0ffb41",
|
||||
"created_at": "2026-10-14T22:31:40.359872Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
import importlib.util
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
from contextlib import redirect_stdout
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import patch
|
||||
|
||||
PATH = Path(__file__).with_name('attended_private_registration.py')
|
||||
spec = importlib.util.spec_from_file_location('attended_private', PATH)
|
||||
m = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(m)
|
||||
mason = m.module(m.MASON, 'mason_contract_test', m.MASON_SHA)
|
||||
# Synthetic shape only; never issued by a credential service.
|
||||
TOKEN = 's.' + 'synthetic_test_value_' * 3
|
||||
|
||||
class Process:
|
||||
def __init__(self, raw=TOKEN, error=''):
|
||||
self.stdout = io.StringIO(raw)
|
||||
self.stderr = io.StringIO(error)
|
||||
self.returncode = 0
|
||||
def wait(self, **kwargs): return self.returncode
|
||||
def poll(self): return self.returncode
|
||||
def kill(self): self.returncode = -9
|
||||
|
||||
class Checks(unittest.TestCase):
|
||||
def caller(self):
|
||||
return {'data': {'policies': [m.POLICY], 'identity_policies': [],
|
||||
'entity_id': m.ENTITY, 'ttl': 1800}}
|
||||
def test_exact_caller(self): m.verify_caller(json.dumps(self.caller()))
|
||||
def test_wrong_caller_boundaries(self):
|
||||
cases = [('ttl', True), ('ttl', 1801), ('ttl', 0),
|
||||
('policies', ['default', m.POLICY]),
|
||||
('policies', [m.POLICY, m.POLICY]),
|
||||
('identity_policies', ['platform-admin']), ('identity_policies', 0), ('entity_id', 'admin')]
|
||||
for key, value in cases:
|
||||
data = self.caller(); data['data'][key] = value
|
||||
with self.subTest(key=key, value=value), self.assertRaises(ValueError):
|
||||
m.verify_caller(json.dumps(data))
|
||||
def test_ambiguous_token_not_selected(self):
|
||||
self.assertEqual(m.extract_token(TOKEN, mason.TOKEN_RE), TOKEN)
|
||||
self.assertEqual(m.extract_token(TOKEN+' '+TOKEN, mason.TOKEN_RE), '')
|
||||
self.assertEqual(m.extract_token('BAD '+TOKEN, mason.TOKEN_RE), TOKEN)
|
||||
def test_missing_preapproval_stops_before_login(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
args = SimpleNamespace(approval=Path(tmp)/'missing', rollout_proof=Path(tmp)/'rollout', native_proof=Path(tmp)/'native', kubeconfig=Path(tmp)/'kube')
|
||||
with patch.object(m.subprocess, 'Popen') as login, self.assertRaises(FileNotFoundError):
|
||||
m.prepare(args)
|
||||
login.assert_not_called()
|
||||
def test_journal_refuses_symlink_and_wrong_mode(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
target = Path(tmp)/'target'; target.write_text(''); target.chmod(0o600)
|
||||
link=Path(tmp)/'link'; link.symlink_to(target)
|
||||
with self.assertRaises(OSError): m.append(link, {'safe': True})
|
||||
target.chmod(0o644)
|
||||
with self.assertRaises(ValueError): m.append(target, {'safe': True})
|
||||
self.assertEqual(target.read_text(), '')
|
||||
def exercise(self, error='', raw=TOKEN, bad_caller=False, child_exit=0, revoke_exit=0):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
approval=Path(tmp)/'approval'; approval.write_text('{}')
|
||||
args=SimpleNamespace(approval=approval, rollout_proof=Path(tmp)/'rollout', native_proof=Path(tmp)/'native', kubeconfig=Path(tmp)/'kube')
|
||||
helper=SimpleNamespace(PLAN=Path(tmp)/'plan', PUBLIC=Path(tmp)/'public', DISCLOSURE=Path(tmp)/'disclosure', CONSENT=Path(tmp)/'consent', IMAGE_PUBLICATION=Path(tmp)/'publication', load=lambda p:{}, gates=lambda *a:None)
|
||||
events=[]; calls=[]; process=Process(raw, error)
|
||||
def run(argv, **kwargs):
|
||||
calls.append((argv, kwargs))
|
||||
if argv[:3]==['bao','token','lookup']:
|
||||
data=self.caller()
|
||||
if bad_caller: data['data']['policies']=['platform-admin']
|
||||
return subprocess.CompletedProcess(argv,0,json.dumps(data),'SECRET_LOOKUP_ERROR')
|
||||
if argv[:3]==['bao','token','revoke']:
|
||||
return subprocess.CompletedProcess(argv,revoke_exit,b'SECRET_REVOKE_OUTPUT',b'SECRET_REVOKE_ERROR')
|
||||
return subprocess.CompletedProcess(argv,child_exit,b'SECRET_CHILD_OUTPUT',b'SECRET_CHILD_ERROR')
|
||||
browser=SimpleNamespace(parse_args=lambda *a: 'https://example/authorize',launch=lambda value:None)
|
||||
stdout=io.StringIO()
|
||||
with patch.object(m.yaml,'safe_load',return_value={'state_dir':'unused'}),patch.object(m.Path,'read_text',return_value='{}'),patch.object(m.subprocess,'Popen',return_value=process) as popen,patch.object(m.subprocess,'run',side_effect=run),redirect_stdout(stdout):
|
||||
result=m.execute(args,helper,mason,browser,events.append)
|
||||
text=stdout.getvalue()+json.dumps(events)
|
||||
self.assertNotIn('SECRET', text)
|
||||
argv=popen.call_args.args[0]
|
||||
self.assertIn('-token-only',argv); self.assertIn('-no-store',argv)
|
||||
self.assertIn('skip_browser=true',argv)
|
||||
env=popen.call_args.kwargs['env']
|
||||
self.assertNotIn('BAO_TOKEN',env); self.assertNotIn('VAULT_TOKEN',env)
|
||||
self.assertNotIn('REUSE_SURFACE_TOKEN',env)
|
||||
for argv,kw in calls:
|
||||
self.assertNotIn(TOKEN, ' '.join(argv))
|
||||
self.assertTrue(kw.get('capture_output'))
|
||||
return result, events, calls
|
||||
def test_success_always_revokes_and_suppresses_child_bytes(self):
|
||||
result, events, calls=self.exercise()
|
||||
self.assertEqual(result,0); self.assertTrue(events[-1]['revoked'])
|
||||
self.assertEqual(calls[-1][0],['bao','token','revoke','-self'])
|
||||
self.assertTrue(any(a[0]=='warden' for a,k in calls))
|
||||
def test_wrong_caller_revokes_without_writer(self):
|
||||
result,events,calls=self.exercise(bad_caller=True)
|
||||
self.assertEqual(result,1); self.assertTrue(events[-1]['revoked'])
|
||||
self.assertFalse(any(a[0]=='warden' for a,k in calls))
|
||||
def test_login_error_never_exposed_revokes_if_token_present(self):
|
||||
result,events,calls=self.exercise(error='SECRET_ERROR_DETAIL\n')
|
||||
self.assertEqual(result,1); self.assertTrue(events[-1]['revoked'])
|
||||
self.assertFalse(any(a[0]=='warden' for a,k in calls))
|
||||
def test_malformed_login_with_token_revokes_without_writer(self):
|
||||
result,events,calls=self.exercise(raw='BAD '+TOKEN)
|
||||
self.assertEqual(result,1); self.assertTrue(events[-1]['revoked'])
|
||||
self.assertFalse(any(a[0]=='warden' for a,k in calls))
|
||||
def test_child_failure_revokes(self):
|
||||
result,events,calls=self.exercise(child_exit=11)
|
||||
self.assertEqual(result,11); self.assertTrue(events[-1]['revoked'])
|
||||
def test_failed_revoke_overrides_success(self):
|
||||
result,events,calls=self.exercise(revoke_exit=1)
|
||||
self.assertEqual(result,1); self.assertFalse(events[-1]['revoked'])
|
||||
|
||||
if __name__=='__main__': unittest.main()
|
||||
|
|
@ -0,0 +1,205 @@
|
|||
import copy
|
||||
import importlib.util
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import sqlite3
|
||||
import tempfile
|
||||
import unittest
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from email.message import Message
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
from urllib.error import HTTPError
|
||||
from urllib.request import HTTPSHandler, build_opener
|
||||
from urllib.response import addinfourl
|
||||
|
||||
HERE = Path(__file__).resolve().parent
|
||||
spec = importlib.util.spec_from_file_location("private_admission", HERE / "register_private_hub_r1.py")
|
||||
m = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(m)
|
||||
|
||||
|
||||
class PrivateAdmissionSafety(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.tmp = tempfile.TemporaryDirectory()
|
||||
self.addCleanup(self.tmp.cleanup)
|
||||
self.plan = m.load(m.PLAN)
|
||||
self.disclosure = m.load(m.DISCLOSURE)
|
||||
self.tools = m.public_tools()
|
||||
now = datetime.now(timezone.utc).isoformat()
|
||||
self.rollout = {"checked_at": now, "status": "verified", "image_digest": m.IMAGE,
|
||||
"native_source_sha256": m.SOURCE_SHA, "native_helper_sha256": m.NATIVE_HELPER_SHA,
|
||||
"namespace": "reuse", "container": "reuse-surface", "ready": True,
|
||||
"deployment_image": m.IMAGE_REPO+"@"+m.IMAGE,
|
||||
"oci_parent_membership_verified": True, "oci_publication_sha256": m.IMAGE_PUBLICATION_SHA,
|
||||
"exact_reader_allowlist_verified": True, "pod": "reuse-surface-fixture",
|
||||
"pod_uid": "fixture-uid", "kube_api_server": "https://127.0.0.1:6443"}
|
||||
self.native = {"id": "CUST81-PRIVATE-NATIVE-FETCH-RECEIPT-R1", "checked_at": now,
|
||||
"all_verified": True, "cache_used": False, "disclosure_sha256": m.DISCLOSURE_SHA,
|
||||
"consent_sha256": m.CONSENT_SHA, "proposal_sha256": m.PREPARATION_SHA, "sources": [{"repo": row["repo"], "commit": row["commit"],
|
||||
"index_sha256": row["files"][0]["sha256"], "entry_sha256": [f["sha256"] for f in row["files"] if f["path"].endswith(".md")], "verified": True} for row in self.disclosure["sources"]]}
|
||||
self.native["hub_runtime"] = {"pod": self.rollout["pod"], "pod_uid": self.rollout["pod_uid"],
|
||||
"image_digest": m.IMAGE, "native_source_sha256": m.SOURCE_SHA, "native_helper_sha256": m.NATIVE_HELPER_SHA}
|
||||
self.rollout_path = Path(self.tmp.name) / "fake-rollout.json"
|
||||
self.native_path = Path(self.tmp.name) / "fake-native.json"
|
||||
self.rollout_path.write_text(json.dumps(self.rollout))
|
||||
self.native_path.write_text(json.dumps(self.native))
|
||||
self.paths = [Path(m.__file__), m.PLAN, m.PUBLIC, m.DISCLOSURE, m.CONSENT, m.IMAGE_PUBLICATION, self.rollout_path, self.native_path]
|
||||
# In-memory approval fixture never written as an executable approval.
|
||||
self.approval = {"id": "CUST81-PRIVATE-REGISTRATION-EXECUTION-R1", "decision": "approved",
|
||||
"decided_by": "Bernd Worsch", "decision_ref": "fixture-only-no-authority",
|
||||
"action": "register_exact_seven_private_sources", "writer_route": "reuse-surface-hub-write-token",
|
||||
"approved_artifacts": {p.name: m.digest(p) for p in self.paths}}
|
||||
|
||||
def test_exact_plan_and_bound_proofs(self):
|
||||
m.gates(self.plan, self.approval, self.rollout, self.native, self.paths)
|
||||
|
||||
def test_scope_and_fields_mutations_rejected(self):
|
||||
for key, value in [("url", "https://attacker.invalid/"), ("auth_env", "REUSE_SURFACE_TOKEN"), ("required", 0), ("domain", "other"), ("repo", "outside")]:
|
||||
changed = copy.deepcopy(self.plan)
|
||||
changed[-1]["body"][key] = value
|
||||
with self.subTest(key=key), self.assertRaises(ValueError):
|
||||
m.validate_plan(changed, self.disclosure)
|
||||
with self.assertRaises(ValueError):
|
||||
m.validate_plan(self.plan[:-1] + [self.plan[0]], self.disclosure)
|
||||
|
||||
def test_invalid_native_or_rollout_proof_prevents_writes(self):
|
||||
mutations = [("native", "cache_used", True), ("native", "all_verified", False),
|
||||
("native", "transport", "fake-only"), ("rollout", "image_digest", "sha256:wrong"),
|
||||
("rollout", "native_source_sha256", "wrong"), ("rollout", "ready", False),
|
||||
("native", "checked_at", (datetime.now(timezone.utc)-timedelta(hours=1)).isoformat())]
|
||||
for target, key, value in mutations:
|
||||
native, rollout = copy.deepcopy(self.native), copy.deepcopy(self.rollout)
|
||||
(native if target == "native" else rollout)[key] = value
|
||||
requests = []
|
||||
def authorize():
|
||||
m.gates(self.plan, self.approval, rollout, native, self.paths)
|
||||
with self.subTest(target=target, key=key), self.assertRaises(ValueError):
|
||||
m.run(self.plan, self.tools, lambda *a: (requests.append(a) or (200, {"repos": []})), lambda: {}, lambda e: None, True, "fixture", authorize)
|
||||
self.assertFalse(any(r[0] == "POST" for r in requests))
|
||||
changed = copy.deepcopy(self.native)
|
||||
changed["sources"][-1]["entry_sha256"] = ["0"*64]
|
||||
with self.assertRaises(ValueError):
|
||||
m.gates(self.plan, self.approval, self.rollout, changed, self.paths)
|
||||
|
||||
def test_artifact_change_rejects_approval(self):
|
||||
self.native_path.write_text(self.native_path.read_text() + " ")
|
||||
with self.assertRaises(ValueError):
|
||||
m.gates(self.plan, self.approval, self.rollout, self.native, self.paths)
|
||||
|
||||
def test_late_existing_conflict_prevents_entire_batch(self):
|
||||
repo = self.plan[-1]["body"]["repo"]
|
||||
wrong = dict(self.plan[-1]["body"], auth_env="WRONG_ENV")
|
||||
requests = []
|
||||
with self.assertRaises(ValueError):
|
||||
m.run(self.plan, self.tools, lambda *a: (requests.append(a) or (200, {"repos": [wrong]})), lambda: {repo: wrong}, lambda e: None, True, "fixture")
|
||||
self.assertFalse(any(r[0] == "POST" for r in requests))
|
||||
|
||||
def test_uncertain_post_stops_without_retry(self):
|
||||
calls, events = [], []
|
||||
def request(method, path, body=None):
|
||||
calls.append(method)
|
||||
if method == "POST":
|
||||
raise TimeoutError("fixture-writer-secret must not be emitted")
|
||||
return 200, {"repos": []}
|
||||
with self.assertRaises(RuntimeError):
|
||||
m.run(self.plan, self.tools, request, lambda: {}, events.append, True, "fixture")
|
||||
self.assertEqual(calls, ["GET", "POST"])
|
||||
self.assertEqual([e["outcome"] for e in events], ["post_intent", "transport_unknown"])
|
||||
self.assertNotIn("fixture-writer-secret", json.dumps(events))
|
||||
|
||||
def test_all_seven_posted_then_full_field_sql_readback(self):
|
||||
stored, events, calls = {}, [], []
|
||||
def request(method, path, body=None):
|
||||
calls.append(method)
|
||||
if method == "POST":
|
||||
stored[body["repo"]] = dict(body)
|
||||
return 201, {}
|
||||
return 200, {"repos": list(stored.values())}
|
||||
m.run(self.plan, self.tools, request, lambda: dict(stored), events.append, True, "fixture")
|
||||
self.assertEqual(calls.count("POST"), 7)
|
||||
self.assertEqual(events[-1]["outcome"], "full_sql_readback_verified")
|
||||
before = len(calls)
|
||||
m.run(self.plan, self.tools, request, lambda: dict(stored), events.append, True, "fixture")
|
||||
self.assertEqual(calls[before:], ["GET"])
|
||||
|
||||
def test_readonly_sql_projects_only_seven_fields_and_repositories(self):
|
||||
db = Path(self.tmp.name) / "fixture.db"
|
||||
with sqlite3.connect(db) as connection:
|
||||
connection.execute("CREATE TABLE registrations(repo TEXT PRIMARY KEY,payload TEXT)")
|
||||
for action in self.plan:
|
||||
body = dict(action["body"], source_token="fixture-secret-not-readable")
|
||||
connection.execute("INSERT INTO registrations VALUES(?,?)", (body["repo"], json.dumps(body)))
|
||||
connection.execute("INSERT INTO registrations VALUES(?,?)", ("outside", '{"source_token":"outside-secret"}'))
|
||||
rows = m.sqlite_rows(str(db))
|
||||
self.assertEqual(len(rows), 7)
|
||||
sanitized = m.sanitize_sql_rows(rows, self.plan)
|
||||
self.assertNotIn("secret", json.dumps(sanitized))
|
||||
self.assertEqual(set(sanitized), m.REPOS)
|
||||
changed = list(rows[0]); changed[3] = 2
|
||||
with self.assertRaises(ValueError):
|
||||
m.sanitize_sql_rows([changed], self.plan)
|
||||
|
||||
def test_pod_transport_checks_cluster_ready_uid_and_image_before_sql(self):
|
||||
rows = [[a["body"][k] if k not in ("enabled","required") else int(a["body"][k]) for k in m.FIELDS] for a in self.plan]
|
||||
values = [self.rollout["kube_api_server"].encode(), (m.IMAGE_REPO+"@"+m.IMAGE).encode(), json.dumps({"uid": "fixture-uid", "ready": "true", "specImage": m.IMAGE_REPO+"@"+m.IMAGE, "imageID": "docker-pullable://"+m.IMAGE_REPO+"@"+m.IMAGE_CHILD}).encode(), json.dumps(rows).encode()]
|
||||
calls=[]
|
||||
with patch.object(m.shutil, "which", return_value="/usr/bin/kubectl"), patch.object(m, "bounded_process", side_effect=lambda argv: (calls.append(argv) or values[len(calls)-1])):
|
||||
self.assertEqual(len(m.full_readback("fixture-kubeconfig",self.rollout,self.plan)),7)
|
||||
self.assertIn("mode=ro", calls[-1][-1])
|
||||
self.assertNotIn("SELECT payload", calls[-1][-1])
|
||||
calls=[]
|
||||
with patch.object(m.shutil,"which",return_value="/usr/bin/kubectl"), patch.object(m,"bounded_process",side_effect=lambda argv: (calls.append(argv) or b"https://wrong-cluster:6443")), self.assertRaises(ValueError):
|
||||
m.full_readback("fixture-kubeconfig",self.rollout,self.plan)
|
||||
self.assertEqual(len(calls),1)
|
||||
|
||||
def test_verified_oci_child_or_parent_runtime_allowed(self):
|
||||
for digest in [m.IMAGE, m.IMAGE_CHILD]:
|
||||
for prefix in ["", "docker-pullable://"]:
|
||||
metadata = {"uid":"fixture-uid", "ready":"true", "specImage":m.IMAGE_REPO+"@"+m.IMAGE,
|
||||
"imageID":prefix+m.IMAGE_REPO+"@"+digest}
|
||||
m.validate_pod_metadata(metadata,self.rollout)
|
||||
|
||||
def test_arbitrary_repo_unknown_child_attestation_or_tag_refused(self):
|
||||
invalid = ["attacker.invalid/repo@"+m.IMAGE, m.IMAGE_REPO+"@sha256:"+"0"*64,
|
||||
m.IMAGE_REPO+"@sha256:6bf7cd034a08da96734032b6d79f52cf5f51006480928ec2d790c9d7993024cb",
|
||||
m.IMAGE_REPO+":cust81-private-reader-r1", "containerd://"+m.IMAGE_CHILD]
|
||||
for image in invalid:
|
||||
with self.subTest(image=image), self.assertRaises(ValueError):
|
||||
m.validate_pod_metadata({"uid":"fixture-uid", "ready":"true", "specImage":m.IMAGE_REPO+"@"+m.IMAGE,
|
||||
"imageID":image},self.rollout)
|
||||
with self.assertRaises(ValueError):
|
||||
m.validate_pod_metadata({"uid":"fixture-uid", "ready":"true", "specImage":m.IMAGE_REPO+":latest",
|
||||
"imageID":m.IMAGE_REPO+"@"+m.IMAGE_CHILD},self.rollout)
|
||||
|
||||
def test_parent_membership_and_deployment_spec_proof_required(self):
|
||||
for key,value in [("oci_parent_membership_verified",False),("oci_publication_sha256","wrong"),
|
||||
("deployment_image",m.IMAGE_REPO+"@"+m.IMAGE_CHILD)]:
|
||||
rollout=dict(self.rollout);rollout[key]=value
|
||||
with self.subTest(key=key),self.assertRaises(ValueError):
|
||||
m.gates(self.plan,self.approval,rollout,self.native,self.paths)
|
||||
|
||||
def test_writer_redirect_never_replays_token(self):
|
||||
calls=[]
|
||||
class FakeHTTPS(HTTPSHandler):
|
||||
def https_open(self, request):
|
||||
calls.append(request.full_url)
|
||||
headers=Message(); headers["Location"]="https://attacker.invalid/exfil"
|
||||
response=addinfourl(io.BytesIO(b""),headers,request.full_url,302)
|
||||
response.msg="Found"
|
||||
return response
|
||||
opener=build_opener(FakeHTTPS(),self.tools.NoRedirect())
|
||||
with patch.dict(os.environ,{"REUSE_SURFACE_TOKEN":"fixture-writer-token"}), self.assertRaises(HTTPError):
|
||||
m.http_request(opener,"POST","/v1/repos",self.plan[0]["body"])
|
||||
self.assertEqual(calls,[m.BASE+"/v1/repos"])
|
||||
|
||||
def test_durable_receipt_refuses_symlink(self):
|
||||
original=Path(self.tmp.name)/"original"; original.write_text("untouched")
|
||||
link=Path(self.tmp.name)/"receipt"; link.symlink_to(original)
|
||||
with self.assertRaises(OSError):m.append(link,{"outcome":"fixture"})
|
||||
self.assertEqual(original.read_text(),"untouched")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
# CUST81 attended browser adapter R1
|
||||
|
||||
Prepared only; no GUI or login has run. This adapter changes the browser opener for one Warden invocation. It does not change the global browser or login procedure.
|
||||
|
||||
The installed OpenBao 2.5.5 OIDC CLI calls `hashicorp/cap` v0.12.0 `util.OpenURL`. On WSL that launches `cmd.exe /c start <url>`, with ampersands escaped as `^&`. This implementation does not consult `BROWSER`. On ordinary Linux it launches `xdg-open`. The two task-local entry points handle only those exact browser invocations and refuse other commands.
|
||||
|
||||
Both entry points silently launch the existing absolute Windows Firefox binary with `-no-remote`, a fresh profile in Windows user temp, `-new-instance` and `-private-window`. They suppress child output, avoid shell interpretation, and remove BAO_TOKEN, VAULT_TOKEN and WARDEN_CONFIG from the browser environment. They do not parse captured OIDC output or change caller HOME. The Warden envelope retains ownership of its private HOME, token helper, output capture, child command, self-revocation and cleanup.
|
||||
|
||||
For the founder's attended terminal, prepend only this directory to PATH on the separately approved exact owner command:
|
||||
|
||||
```bash
|
||||
env PATH="/home/worsch/the-custodian/docs/assessments/netkingdom-2026-10-14/warden-browser-opener-r1:$PATH" \
|
||||
BAO_ADDR=http://127.0.0.1:18200 VAULT_ADDR=http://127.0.0.1:18200 \
|
||||
warden access openbao-platform-admin-login --exec -- <reviewed-silent-owner-command>
|
||||
```
|
||||
|
||||
The placeholder is intentionally not executable until the parent owner binds the reviewed command and live-change approval. Complete only the KeyCape sign-in/MFA in the fresh private Firefox window. Close that window after the attended operation. The profile directory is retained in Windows temp; no recursive deletion is performed by this adapter. A new invocation creates a new profile.
|
||||
|
||||
Validation is limited to source-contract inspection and mocked subprocess checks, documented in validation.json. Actual GUI launch and OIDC callback connectivity have not been verified. An opener failure remains inside Warden's captured login and fails the attended operation; do not surface the captured authorization URL as a fallback.
|
||||
|
||||
Sources: [OpenBao 2.5.5 OIDC CLI](https://raw.githubusercontent.com/openbao/openbao/v2.5.5/builtin/credential/jwt/cli.go), [cap v0.12.0 browser helper](https://raw.githubusercontent.com/hashicorp/cap/v0.12.0/util/util.go). Warden's existing owner contract: `/home/worsch/ops-warden/wiki/playbooks/openbao-platform-admin-login.md`.
|
||||
57
docs/assessments/netkingdom-2026-10-14/warden-browser-opener-r1/cmd.exe
Executable file
57
docs/assessments/netkingdom-2026-10-14/warden-browser-opener-r1/cmd.exe
Executable file
|
|
@ -0,0 +1,57 @@
|
|||
#!/usr/bin/python3
|
||||
"""Silent, task-local OpenBao browser adapter; never performs a login."""
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
from urllib.parse import urlsplit
|
||||
|
||||
FIREFOX = "/mnt/c/Program Files/Mozilla Firefox/firefox.exe"
|
||||
|
||||
def parse_args(name, args):
|
||||
if name == "cmd.exe":
|
||||
if len(args) != 3 or args[:2] != ["/c", "start"]:
|
||||
raise ValueError("unexpected browser invocation")
|
||||
value = args[2].replace("^&", "&")
|
||||
elif name == "xdg-open" and len(args) == 1:
|
||||
value = args[0]
|
||||
else:
|
||||
raise ValueError("unexpected browser invocation")
|
||||
url = urlsplit(value)
|
||||
if (url.scheme != "https" or not url.hostname or url.username is not None
|
||||
or url.password is not None or url.fragment or any(ord(c) < 32 for c in value)):
|
||||
raise ValueError("unexpected URL")
|
||||
return value
|
||||
|
||||
def launch(value):
|
||||
result = subprocess.run(["powershell.exe", "-NoProfile", "-NonInteractive", "-Command",
|
||||
"[System.IO.Path]::GetTempPath()"],
|
||||
capture_output=True, text=True, timeout=20, check=True)
|
||||
root = result.stdout.strip()
|
||||
if len(root) < 3 or root[1:3] != ":\\" or "\n" in root or "\r" in root:
|
||||
raise ValueError("invalid profile location")
|
||||
profile = root.rstrip("\\") + "\\cust81-warden-firefox-" + uuid.uuid4().hex
|
||||
result = subprocess.run(["wslpath", "-u", profile], capture_output=True,
|
||||
text=True, timeout=10, check=True)
|
||||
local = Path(result.stdout.strip())
|
||||
if not local.is_absolute() or not str(local).startswith("/mnt/"):
|
||||
raise ValueError("invalid profile location")
|
||||
local.mkdir(mode=0o700)
|
||||
browser_env = dict(os.environ)
|
||||
for key in ("BAO_TOKEN", "VAULT_TOKEN", "WARDEN_CONFIG"):
|
||||
browser_env.pop(key, None)
|
||||
subprocess.Popen([FIREFOX, "-no-remote", "-profile", profile, "-new-instance",
|
||||
"-private-window", value], stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL,
|
||||
start_new_session=True, close_fds=True, env=browser_env)
|
||||
|
||||
def main():
|
||||
try:
|
||||
launch(parse_args(Path(sys.argv[0]).name, sys.argv[1:]))
|
||||
return 0
|
||||
except Exception:
|
||||
return 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"status": "prepared_validated_not_executed",
|
||||
"checked_at": "2026-10-14T23:53:19.197811+00:00",
|
||||
"no_gui_or_login": true,
|
||||
"validation": "two valid and six invalid invocation/URL cases; mocked Windows temp/profile and Firefox subprocess; no output or token inheritance",
|
||||
"bao_version": "2.5.5",
|
||||
"bao_revision": "028992583c693c4de6350b8aa52ff85e30375a99",
|
||||
"cap_version": "v0.12.0",
|
||||
"source_contract": "https://raw.githubusercontent.com/hashicorp/cap/v0.12.0/util/util.go",
|
||||
"oidc_source": "https://raw.githubusercontent.com/openbao/openbao/v2.5.5/builtin/credential/jwt/cli.go",
|
||||
"browser_env_not_supported": true,
|
||||
"actual_wsl_command": [
|
||||
"cmd.exe",
|
||||
"/c",
|
||||
"start",
|
||||
"<url with & escaped as ^&>"
|
||||
],
|
||||
"warden_preserves_path": true,
|
||||
"default_home_changed_by_adapter": false,
|
||||
"global_opener_changed": false,
|
||||
"profile_cleanup": "Close dedicated Firefox after attended operation. Private windows use a new task-specific profile under Windows temp. No recursive deletion is performed by this opener.",
|
||||
"files": {
|
||||
"cmd.exe": "4b91a4dc635e464143bfe1f484eccb2456ccd992326ac7a126b53d0a9a3c7f96",
|
||||
"xdg-open": "4b91a4dc635e464143bfe1f484eccb2456ccd992326ac7a126b53d0a9a3c7f96"
|
||||
}
|
||||
}
|
||||
57
docs/assessments/netkingdom-2026-10-14/warden-browser-opener-r1/xdg-open
Executable file
57
docs/assessments/netkingdom-2026-10-14/warden-browser-opener-r1/xdg-open
Executable file
|
|
@ -0,0 +1,57 @@
|
|||
#!/usr/bin/python3
|
||||
"""Silent, task-local OpenBao browser adapter; never performs a login."""
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
from urllib.parse import urlsplit
|
||||
|
||||
FIREFOX = "/mnt/c/Program Files/Mozilla Firefox/firefox.exe"
|
||||
|
||||
def parse_args(name, args):
|
||||
if name == "cmd.exe":
|
||||
if len(args) != 3 or args[:2] != ["/c", "start"]:
|
||||
raise ValueError("unexpected browser invocation")
|
||||
value = args[2].replace("^&", "&")
|
||||
elif name == "xdg-open" and len(args) == 1:
|
||||
value = args[0]
|
||||
else:
|
||||
raise ValueError("unexpected browser invocation")
|
||||
url = urlsplit(value)
|
||||
if (url.scheme != "https" or not url.hostname or url.username is not None
|
||||
or url.password is not None or url.fragment or any(ord(c) < 32 for c in value)):
|
||||
raise ValueError("unexpected URL")
|
||||
return value
|
||||
|
||||
def launch(value):
|
||||
result = subprocess.run(["powershell.exe", "-NoProfile", "-NonInteractive", "-Command",
|
||||
"[System.IO.Path]::GetTempPath()"],
|
||||
capture_output=True, text=True, timeout=20, check=True)
|
||||
root = result.stdout.strip()
|
||||
if len(root) < 3 or root[1:3] != ":\\" or "\n" in root or "\r" in root:
|
||||
raise ValueError("invalid profile location")
|
||||
profile = root.rstrip("\\") + "\\cust81-warden-firefox-" + uuid.uuid4().hex
|
||||
result = subprocess.run(["wslpath", "-u", profile], capture_output=True,
|
||||
text=True, timeout=10, check=True)
|
||||
local = Path(result.stdout.strip())
|
||||
if not local.is_absolute() or not str(local).startswith("/mnt/"):
|
||||
raise ValueError("invalid profile location")
|
||||
local.mkdir(mode=0o700)
|
||||
browser_env = dict(os.environ)
|
||||
for key in ("BAO_TOKEN", "VAULT_TOKEN", "WARDEN_CONFIG"):
|
||||
browser_env.pop(key, None)
|
||||
subprocess.Popen([FIREFOX, "-no-remote", "-profile", profile, "-new-instance",
|
||||
"-private-window", value], stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL,
|
||||
start_new_session=True, close_fds=True, env=browser_env)
|
||||
|
||||
def main():
|
||||
try:
|
||||
launch(parse_args(Path(sys.argv[0]).name, sys.argv[1:]))
|
||||
return 0
|
||||
except Exception:
|
||||
return 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Loading…
Add table
Add a link
Reference in a new issue