Protect authenticated federation sources with exact runtime URL bindings
Assistant: codex Assistant-Model: gpt-6.1-sol Assistant-Session: 01a137ca-5c77-7a71-b9ae-23ad9b0b5684
This commit is contained in:
parent
03cd049773
commit
b181240b35
10 changed files with 1388 additions and 3 deletions
|
|
@ -0,0 +1,55 @@
|
|||
{
|
||||
"id": "REUSE-WP-0025-CONSUMER-CANDIDATE-REVIEW-R1",
|
||||
"reviewed_at": "2026-10-14T23:08:33.016513+00:00",
|
||||
"review_scope": "Consumer code, receipt-only verification helper, exact metadata URL configuration and local image preparation integration only; not credential provisioning, production rollout, registry admission or runtime approval.",
|
||||
"owner_review": {
|
||||
"reviewer": "reuse-surface-owner (agent reuse-surface, codex)",
|
||||
"decision": "approved",
|
||||
"comment": "REUSE-WP-0025-T02. INTENT.md Registry First, Consumer Experience Matters, and Internal and External Evidence Must Stay Separate: the reviewed native patch makes source credentials usable only for operator-approved exact HTTPS URLs, before any caller-selected credential lookup; redirects are refused. The helper verifies native acquisition of the consented seven indexes and seven entries by exact hashes/schema without stale-cache fallback and returns bounded receipts. Exact candidate source/helper/config/test and preparation patch digests are bound below. 227 tests pass in explicit fixture environment and source hashes match the locally smoke-verified image. This approves consumer preparation integration only; baseline proposal unchanged-image reader delivery remains changes_requested. No credential execution, founder approval or production success is inferred."
|
||||
},
|
||||
"workplan_id": "REUSE-WP-0025",
|
||||
"task_id": "REUSE-WP-0025-T02",
|
||||
"intent_sha256": "511d1029a7aefc67b68a6363ed3ce8f803f2516c00062ff805263dffe26c771b",
|
||||
"candidate_artifacts": {
|
||||
"reuse_surface/federation.py": "dbaa40f25cc0f090143578cd1c7068cb2cb00960fafd17d5239bf8a5bac514dd",
|
||||
"reuse_surface/private_source_verify.py": "c67a284091cc4dd62dca56658a14b8ea1357e2b3df1518120da4c51e0ee8d85c",
|
||||
"tests/test_private_source_verify.py": "5f0c0b30b98b4db96d695bdda952b25707916e297d566744fadb6dde7bea18b2",
|
||||
"docs/assessments/cust81-private-reader/runtime-auth-source-allowlist-r1.json": "e1948c537e206b30fc72b9e4732a3f600d92e131813e6f7ecfd4bfbb18c27286",
|
||||
"docs/assessments/cust81-private-reader/suite-environment-validation.json": "84f9c410f09e0dd5c0c90987744bc9f6e85292d2e28e4db88859ee0b24e68bd2",
|
||||
"docs/assessments/cust81-private-reader/reuse-owner-preparation-r1.patch": "da9634481ce80658dfce815d3539ba023234938dc76163dfd93d7677be95ed51"
|
||||
},
|
||||
"local_image": {
|
||||
"manifest_digest": "sha256:2364b7e7859daccf9340a063654e2b09eed1bc27e0adf7b23cbb4b30135a7db0",
|
||||
"receipt_path": "/home/worsch/the-custodian/docs/assessments/netkingdom-2026-10-14/hardened-reader-local-image-r1.json",
|
||||
"receipt_sha256": "ed7c2516925d7f16b573a70b5e48d5656a4875e9eb94bcc8760a8bdcbe07f59c",
|
||||
"published": false,
|
||||
"deployed": false,
|
||||
"credentials_provisioned": false
|
||||
},
|
||||
"existing_auth_source_inventory": {
|
||||
"receipt_path": "/home/worsch/the-custodian/docs/assessments/netkingdom-2026-10-14/live-authenticated-source-inventory-r1.json",
|
||||
"receipt_sha256": "ba669d6231c8c611191c7ad28f5914d6c47332032b23b0ea40a13f753e883073",
|
||||
"checked_at": "2026-10-14T23:01:13.254261+00:00",
|
||||
"authenticated_source_count": 0,
|
||||
"must_recheck_before_rollout": true
|
||||
},
|
||||
"preserved_baseline_review": {
|
||||
"path": "docs/assessments/cust81-private-reader/owner-review-r1.md",
|
||||
"decision": "changes_requested",
|
||||
"scope": "Original R1 same-image credential delivery proposal; preserved, not rewritten."
|
||||
},
|
||||
"remaining_execution_gates": [
|
||||
"Platform/Forgejo/apps/mason owner reviews of exact current CCR and implementation.",
|
||||
"Exact founder credential and live rollout decision remains separate.",
|
||||
"Publish and independently verify registry image digest before rendering actual release values.",
|
||||
"Recheck metadata-only authenticated source inventory and materialized exact14 reader allowlist before approved rollout.",
|
||||
"Run receipt helper in actual hub environment with native authenticated fresh acquisition, then exact70 hosted audit."
|
||||
],
|
||||
"does_not_approve": [
|
||||
"Credential provision or access grant",
|
||||
"Live production mutation or rollout",
|
||||
"Other owner roles",
|
||||
"Registration/admission status change",
|
||||
"Entire objective completion"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
"id": "REUSE-WP-0025-CCR0046-CONSUMER-REVIEW-R2",
|
||||
"reviewed_at": "2026-10-14T23:19:37.233425+00:00",
|
||||
"workplan_id": "REUSE-WP-0025",
|
||||
"task_id": "REUSE-WP-0025-T01",
|
||||
"ccr_id": "CCR-2026-0046",
|
||||
"revision": 2,
|
||||
"review": {
|
||||
"reviewer": "reuse-surface-owner (agent reuse-surface, codex)",
|
||||
"decision": "approved",
|
||||
"comment": "REUSE-WP-0025-T01; INTENT.md Registry First, Consumer Experience Matters and Internal and External Evidence Must Stay Separate. Reviewed exact CCR-2026-0046 revision2, provisioner, exact-token recovery, bootstrap contract and ESO policy SHA256 listed below solely for consumer delivery boundary. The exact seven consented sources/commits/hash rows match original pinned disclosure preparation. Separate reader KV and env protect scoped writer callers; contract prohibits delivery to baseline/current image and requires full reviewed HTTPS/redirect/credential-before-use allowlist enforcement plus exact fourteen metadata URLs. Final actual hub helper must prove native index AND entry hashes without stale cache; 70-ID hosted audit remains independent. Recovery confines revocation to separately approved exact dedicated PAT, preserves shared runtime KV/Secret/public sources, and does not remint after ambiguity. Full native code/helper/config preparation integration review is separately approved and pinned below. This owner judgement approves preparation integration of this consumer boundary, not human credential/rollout go, provisioner execution, status/admission changes, other owners or deployment success."
|
||||
},
|
||||
"artifacts": {
|
||||
"credential-change-requests/CCR-2026-0046-reuse-surface-forgejo-source-read.yaml": "90522912fb78fed72e625382fb7114a0e9aafe76d59e36bffecab83c5bb7d643",
|
||||
"scripts/provision-reuse-surface-forgejo-source.py": "abf60a754596baa1610d23b1fd367385b0222ac8aeaebba0f2cfb7ec4c62055b",
|
||||
"scripts/revoke-reuse-surface-forgejo-source.py": "f78d1b26fbff93c6cc481a896d9fb9b9ce93049b8b3267fa41a64bc57dadb162",
|
||||
"docs/proposals/2026-10-15-reuse-private-reader/bootstrap-contract.json": "2bb72671e0a5f230497ab8ef2967a20ee68d1f2cf081f07c84f6db3442e18d8b",
|
||||
"openbao/policies/workload-kv-read-reuse-surface-runtime-eso.hcl": "6320f6c2d262d924ea16291d301d5f9eeb7efdb6ce7ea141c1a41d277d86b01b"
|
||||
},
|
||||
"consumer_preparation_review": {
|
||||
"path": "docs/assessments/cust81-private-reader/candidate-owner-review-r1.json",
|
||||
"sha256": "853cb3b721d83ba5aeb0847111cc47f8094b733f870059cca9834b2c1a5a6219",
|
||||
"decision": "approved"
|
||||
},
|
||||
"preserved_baseline": "Original same-image reader delivery review remains changes_requested; this R2 consumer boundary resolves that design defect without altering earlier evidence.",
|
||||
"execution_authorized": false,
|
||||
"required_before_execution": [
|
||||
"Human exact credential and separately scoped rollout decisions, with all required independent owner reviews.",
|
||||
"Published independently verified image digest and reviewed release config for the full security patch.",
|
||||
"Recheck metadata-only live authenticated-source inventory (last snapshot0) and validate exact14 runtime reader URLs.",
|
||||
"Post-rollout native authenticated helper receipt plus separate registration and exact70 hosted completion audit."
|
||||
],
|
||||
"not_proven": [
|
||||
"Real Forgejo reader scope",
|
||||
"Live OpenBao/ESO materialization",
|
||||
"Production image/config rollout",
|
||||
"Actual authenticated metadata fetch",
|
||||
"Hosted private capability admission"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
{
|
||||
"id": "CUST81-PRIVATE-NATIVE-FETCH-RECEIPT-R1",
|
||||
"checked_at": "2026-10-14T22:43:42.958330+00:00",
|
||||
"proposal_sha256": "7819eb51d249f5c7aff4df9fb520afcfb76603b05a4104439499102b6c20af99",
|
||||
"disclosure_sha256": "46ac4fe49a08054cc215aade2fbaf3b36ee334520997857ed84f530b52da9d35",
|
||||
"consent_sha256": "4a4602ad926e3af855d6ddba4731430d33e4a7432c507ba0a5193b9f43056b52",
|
||||
"cache_used": false,
|
||||
"all_verified": true,
|
||||
"sources": [
|
||||
{
|
||||
"repo": "rapp-openbao",
|
||||
"commit": "35672be138036c8433a5ac5de91e068c03c2b124",
|
||||
"index_sha256": "044efa3d02b90e1872f2516c581159d388a8080a689f76fcd78bb4d048d6ee64",
|
||||
"verified": true,
|
||||
"entry_sha256": [
|
||||
"9de890da536c34ac2b50576e2c4a9c03792c8a07545d2a0566ace68ac10a293e"
|
||||
]
|
||||
},
|
||||
{
|
||||
"repo": "rapp-policy-nexus",
|
||||
"commit": "088b78ac03b0cebeec34a69aae3569eb7ec2f9c3",
|
||||
"index_sha256": "e68bdedf61eee9ca2026f46a2434ce384c26234616e74f7a5d08a06c75b3bb00",
|
||||
"verified": true,
|
||||
"entry_sha256": [
|
||||
"21415ed5e89dcf8bd2df0c8bc99b07d2c87173809c37fa2582de6d336918a126"
|
||||
]
|
||||
},
|
||||
{
|
||||
"repo": "rapp-postgres",
|
||||
"commit": "a9f569c96d74aa384ac8d3ac4aa960e4061550b5",
|
||||
"index_sha256": "d939333eed9d85172e006f526c445fbe2602a721d53f7c0d14bc68286ae08776",
|
||||
"verified": true,
|
||||
"entry_sha256": [
|
||||
"64503872abb24bb28f291264e860ab094c6abb721fbfe90b47258ad13b77af44"
|
||||
]
|
||||
},
|
||||
{
|
||||
"repo": "rapp-sbom-nexus",
|
||||
"commit": "adbedd349e8df97be43a1f57db7095af45ff29d1",
|
||||
"index_sha256": "d2ee0f193d2198bf8f16ce64c90ae7664e59a2bc307ad711426af3fd47deb057",
|
||||
"verified": true,
|
||||
"entry_sha256": [
|
||||
"1a15e37bd3d19c4c89ef25803667937f549ade1cd27bedfa85132ac48ffe3f48"
|
||||
]
|
||||
},
|
||||
{
|
||||
"repo": "rapp-telemetry",
|
||||
"commit": "f158307e24b86bab88a1af4830ff6b4f85afdeb5",
|
||||
"index_sha256": "ee8801f143ed5cf064205710a61c2bb24efc6ea8585d5786d6b679426ec4c1d4",
|
||||
"verified": true,
|
||||
"entry_sha256": [
|
||||
"a67dbf6905223c04b1c003d5f967841038743c8c1479812f5cf0bb55ad6cee77"
|
||||
]
|
||||
},
|
||||
{
|
||||
"repo": "rapp-tenant-engine",
|
||||
"commit": "9bdc86572728783a5ed94266f04f7b12d414c557",
|
||||
"index_sha256": "d8e9c1282350b5d2fd4f4d69712aa7a5fbcd2367bb1a7fad1b6eb16a97e5d732",
|
||||
"verified": true,
|
||||
"entry_sha256": [
|
||||
"e7cf0485cfdb6f497281638d81f908b5c2fe1053125c6ef28130504909d1d49e"
|
||||
]
|
||||
},
|
||||
{
|
||||
"repo": "rapp-user-engine",
|
||||
"commit": "f35108f9e1b8702fb6c526fe42b2fec5ca032738",
|
||||
"index_sha256": "95b4ed2d0b189f6847b1c0899f740d1da46933a8f40491e1a02f2d4e57c819fc",
|
||||
"verified": true,
|
||||
"entry_sha256": [
|
||||
"84ec4fdce99134c88c110104cac93bdea36cffb5ba34e046b9ad161fb860dce2"
|
||||
]
|
||||
}
|
||||
],
|
||||
"transport": "fake-only local committed metadata; no authenticated network fetch or runtime evidence"
|
||||
}
|
||||
102
docs/assessments/cust81-private-reader/owner-review-r1.md
Normal file
102
docs/assessments/cust81-private-reader/owner-review-r1.md
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
# Reuse-surface independent consumer preparation review
|
||||
|
||||
Disposition: **changes_requested** for reader provisioning proposal
|
||||
`CUST81-PRIVATE-READER-PROVISIONING-PREPARATION-R1`, SHA256
|
||||
`7819eb51d249f5c7aff4df9fb520afcfb76603b05a4104439499102b6c20af99`.
|
||||
Reviewer: delegated reuse-surface owner preparation worker, supervised local scope.
|
||||
This review does not authorize credentials, approve another owner's implementation,
|
||||
claim deployment success, or grant permanent repo authority.
|
||||
|
||||
INTENT requires inspectable registry membership and evidence-driven consumer
|
||||
reliability. Acquisition must protect source custody while making the explicitly
|
||||
consented metadata discoverable. The proposal's seven-repository reader, separate
|
||||
KV path and exact commit pins fit that purpose, but its unchanged-image rollout
|
||||
assumption fails consumer credential isolation.
|
||||
|
||||
## Findings and prepared correction
|
||||
|
||||
1. Native `fetch_remote_index_text` originally supplies Authorization to urllib's
|
||||
default opener. Redirect handling copies headers to the redirect target. A
|
||||
source redirect can disclose the PAT to another origin or unreviewed path.
|
||||
Prepared patch rejects **all authenticated redirects**, even same-origin ones.
|
||||
Anonymous federation keeps its previous behavior.
|
||||
2. Hub registration accepts caller-selected `auth_env`, `auth_header` and `url`.
|
||||
Composition passes these to `_auth_headers`, which reads the named runtime env
|
||||
and sends its value to that URL. A holder of the writer token can therefore
|
||||
induce credential exfiltration without a redirect. Prepared patch validates
|
||||
operator-owned `REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST` JSON before looking up the
|
||||
credential. Exact allowed env name and URL are mandatory; missing/malformed
|
||||
configuration fails closed. Authenticated HTTP is rejected.
|
||||
3. `resolve_source_index_path(refresh=True)` may return stale cache after remote
|
||||
failure. A fresh composition timestamp alone cannot prove private acquisition.
|
||||
Prepared helper calls native fetch directly, never the resolver/cache path,
|
||||
and verifies exact consented index **and Markdown entry** SHA256 plus capability
|
||||
identity, owner and entry schema. Failed receipts contain bounded categories,
|
||||
never raw exception text, bodies, headers, or secrets.
|
||||
|
||||
## Exact prepared artifacts
|
||||
|
||||
- `reuse_surface/federation.py` SHA256
|
||||
`dbaa40f25cc0f090143578cd1c7068cb2cb00960fafd17d5239bf8a5bac514dd`
|
||||
- `reuse_surface/private_source_verify.py` SHA256
|
||||
`c67a284091cc4dd62dca56658a14b8ea1357e2b3df1518120da4c51e0ee8d85c`
|
||||
- `tests/test_private_source_verify.py` SHA256
|
||||
`5f0c0b30b98b4db96d695bdda952b25707916e297d566744fadb6dde7bea18b2`
|
||||
- `runtime-auth-source-allowlist-r1.json` SHA256
|
||||
`e1948c537e206b30fc72b9e4732a3f600d92e131813e6f7ecfd4bfbb18c27286`
|
||||
is the exact fourteen commit-pinned index/entry URLs for reader env
|
||||
`REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN`. Serialize this object as the runtime
|
||||
value of `REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST`; do not use host wildcards.
|
||||
|
||||
## Validation and limits
|
||||
|
||||
Focused fake transport tests cover native Bearer header handling, all fourteen
|
||||
fetches, no cache path, mismatched index/entry hashes, tampered consent packet,
|
||||
missing authentication, sanitized errors, same/cross-origin redirects without
|
||||
second requests, HTTP rejection, missing/malformed/unapproved allowlists, and
|
||||
broadened reader URL scope. Focused tests: **30 passed**. Whole test suite initially
|
||||
reported **226 passed, 1 failed**: existing `test_federation_compose` resolved a
|
||||
required sibling index to missing `/tmp/access-engine/registry/indexes/capabilities.yaml`.
|
||||
The first sibling fixture preparation also stopped at maturity-engine's untracked
|
||||
registry index, and that partial fixture run reported **226 passed, 1 failed**.
|
||||
Both outcomes remain in `suite-environment-validation.json`; no consumer test
|
||||
failure or failed preparation is hidden.
|
||||
|
||||
With the complete task-owned fixture environment, the unchanged source patch
|
||||
passes the **entire suite: 227 passed, 1 existing dependency warning**. Command:
|
||||
`/home/worsch/reuse-surface/.venv/bin/python -m pytest -q tests`, cwd
|
||||
`/tmp/cust81-reuse-suite-fixtures-r1/reuse-surface`. That fixture contains sixty
|
||||
required local registry indexes and thirty-nine seeded anonymous caches to avoid
|
||||
live network dependencies. Fifty-nine local indexes are copied from committed
|
||||
HEAD; maturity-engine's existing untracked index is copied read-only and labeled
|
||||
as a test fixture, never publication evidence. Member indexes are only read;
|
||||
composition writes its own copied registry/cache. No foreign checkout is written.
|
||||
Tests rewrite generated catalog/graph files in their own fixture checkout; earlier
|
||||
generated changes in the integration checkout were restored to its HEAD.
|
||||
|
||||
`local-consented-metadata-validation.json` proves schema and exact hashes for all
|
||||
seven repositories' consented committed metadata using a fake transport and a
|
||||
fixture-only token. It is explicitly **not** a network, credential, rollout or
|
||||
live federation verification receipt.
|
||||
|
||||
Static `registry/federation/sources.yaml` contains no `auth_env`, but public hub
|
||||
registration responses intentionally omit it and do not prove live SQLite dynamic
|
||||
source inventory. This patch changes authenticated-source behavior: every existing
|
||||
authenticated lane needs a reviewed exact-URL allowlist entry. Before rollout,
|
||||
platform/apps must safely inventory only auth env names and registered URLs,
|
||||
preserve approved existing lanes in runtime config, and prove no unrelated runtime
|
||||
secret name can be routed. No raw env dump or Secret read is required for that
|
||||
inventory. Until this migration and the reviewed new consumer image are complete,
|
||||
do not deliver the new PAT to the existing hub image.
|
||||
|
||||
After separate exact credential/rollout approval, invoke the helper from inside
|
||||
the hub process environment with the pinned proposal/disclosure/consent files:
|
||||
|
||||
```sh
|
||||
python -m reuse_surface.private_source_verify --proposal /review/private-reader-provisioning-preparation-r1.json --disclosure /review/private-source-disclosure-proposal-r1.json --consent /review/private-source-owner-consent-r1.json
|
||||
```
|
||||
|
||||
It accepts no credential argument and returns only bounded metadata receipts.
|
||||
Exit zero means all seven native index/entry fetches matched pinned consent; it
|
||||
does not prove registration/federation completion. Final admission still needs
|
||||
separate live source registrations and exact 70-ID fresh hosted audit.
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN": [
|
||||
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-openbao/raw/registry/capabilities/capability.railiance.openbao-package.md?ref=35672be138036c8433a5ac5de91e068c03c2b124",
|
||||
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-openbao/raw/registry/indexes/capabilities.yaml?ref=35672be138036c8433a5ac5de91e068c03c2b124",
|
||||
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-policy-nexus/raw/registry/capabilities/capability.railiance.policy-publication-package.md?ref=088b78ac03b0cebeec34a69aae3569eb7ec2f9c3",
|
||||
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-policy-nexus/raw/registry/indexes/capabilities.yaml?ref=088b78ac03b0cebeec34a69aae3569eb7ec2f9c3",
|
||||
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-postgres/raw/registry/capabilities/capability.railiance.postgres-isolation-recovery.md?ref=a9f569c96d74aa384ac8d3ac4aa960e4061550b5",
|
||||
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-postgres/raw/registry/indexes/capabilities.yaml?ref=a9f569c96d74aa384ac8d3ac4aa960e4061550b5",
|
||||
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-sbom-nexus/raw/registry/capabilities/capability.railiance.sbom-runtime-package.md?ref=adbedd349e8df97be43a1f57db7095af45ff29d1",
|
||||
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-sbom-nexus/raw/registry/indexes/capabilities.yaml?ref=adbedd349e8df97be43a1f57db7095af45ff29d1",
|
||||
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-telemetry/raw/registry/capabilities/capability.railiance.telemetry-package.md?ref=f158307e24b86bab88a1af4830ff6b4f85afdeb5",
|
||||
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-telemetry/raw/registry/indexes/capabilities.yaml?ref=f158307e24b86bab88a1af4830ff6b4f85afdeb5",
|
||||
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-tenant-engine/raw/registry/capabilities/capability.railiance.tenant-engine-package.md?ref=9bdc86572728783a5ed94266f04f7b12d414c557",
|
||||
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-tenant-engine/raw/registry/indexes/capabilities.yaml?ref=9bdc86572728783a5ed94266f04f7b12d414c557",
|
||||
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-user-engine/raw/registry/capabilities/capability.railiance.user-engine-package.md?ref=f35108f9e1b8702fb6c526fe42b2fec5ca032738",
|
||||
"https://forgejo.coulomb.social/api/v1/repos/coulomb/rapp-user-engine/raw/registry/indexes/capabilities.yaml?ref=f35108f9e1b8702fb6c526fe42b2fec5ca032738"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,628 @@
|
|||
{
|
||||
"id": "REUSE-WP-0025-SUITE-ENVIRONMENT-R1",
|
||||
"fixture_checkout": "/tmp/cust81-reuse-suite-fixtures-r1/reuse-surface",
|
||||
"fixture_only": true,
|
||||
"sources": [
|
||||
{
|
||||
"repo": "access-engine",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "7d59022dac9da4d8dcbecab936d3cea9b154c8c51478b378932fd9ebe5ec7e02"
|
||||
},
|
||||
{
|
||||
"repo": "activity-core",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "536d359366ccf3db3b2b0d390635bc2c5311bd37ac7ff89dae11d809ef15b848"
|
||||
},
|
||||
{
|
||||
"repo": "agentic-resources",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "42986b7a274ef510166e566a9756d594a6f88706189bc97ac889bab86b20240d"
|
||||
},
|
||||
{
|
||||
"repo": "approval-engine",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "b6812fd12dc530270fb66bae592c828f56f17f2fc86759cb04d750162fc619a4"
|
||||
},
|
||||
{
|
||||
"repo": "artifact-store",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "200802ac737195721a52d6d345104f2302fbdc35b4122b431bd2255f4e5552bc"
|
||||
},
|
||||
{
|
||||
"repo": "audit-core",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "6a1d7dbb442f0fd0e481d54fb0acbee37ef2e693198b6f2108e154d3c3795e0e"
|
||||
},
|
||||
{
|
||||
"repo": "can-you-assist",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "ce106e5c106453635075dd1b5906762d516e84601af4d5620c9a7faab444be46"
|
||||
},
|
||||
{
|
||||
"repo": "citation-engine",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "e02914ffe937c8342022d92b5bfa6906aef560edeb6e90f80826cbf8e0620ada"
|
||||
},
|
||||
{
|
||||
"repo": "citation-evidence",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "628e338df6da2f0966675e8facf91f52cc3ac4a0c81417b33e1284045db1dfad"
|
||||
},
|
||||
{
|
||||
"repo": "citation-work",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "c20b96c226bb9465d7154c127ee19a94396c7b03307e7581ff827eba42651d86"
|
||||
},
|
||||
{
|
||||
"repo": "commerce-canon",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "6e1186aad49634b9725bc2cfa051c42b2ac13e5c4abe4bade7b39c0483f4242d"
|
||||
},
|
||||
{
|
||||
"repo": "config-atlas",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "d0325d71a5de1e756c080dc9abc3162536b9700cc37942a028ad2f65bad9bfef"
|
||||
},
|
||||
{
|
||||
"repo": "coordination-engine",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "f7a2c140ac1a7628a2106b1047b59e208879257b23ffed6dceef8ecf6c3bd420"
|
||||
},
|
||||
{
|
||||
"repo": "core-hub",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "31cb09dd3b04a7891533387479ecb340b543445b64a849d9973ee857efa4cca1"
|
||||
},
|
||||
{
|
||||
"repo": "domain-tree",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "c20b96c226bb9465d7154c127ee19a94396c7b03307e7581ff827eba42651d86"
|
||||
},
|
||||
{
|
||||
"repo": "email-connect",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "5bce1e037a368816811a21dd9123ebf47f1a7d293c424c661d30fa14f2366231"
|
||||
},
|
||||
{
|
||||
"repo": "emergent-security",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "268d9192dc9b97da462d46fe5b40ea33e678840b23944ab0cd73622c130bdef6"
|
||||
},
|
||||
{
|
||||
"repo": "evidence-anchor",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "c20b96c226bb9465d7154c127ee19a94396c7b03307e7581ff827eba42651d86"
|
||||
},
|
||||
{
|
||||
"repo": "evidence-binder",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "2664dfb5668238051b7cd540470ee2f7a5557415c53462fa4f14474829f3ce46"
|
||||
},
|
||||
{
|
||||
"repo": "evidence-source",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "7b2a3c7b108fbdc69828b088915418c87dedfee5e202e4546defe68d8df64667"
|
||||
},
|
||||
{
|
||||
"repo": "feature-control",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "2335b614d120a3e954d55389448b4c8462bfab3582de2e0bbae02dd5474dd132"
|
||||
},
|
||||
{
|
||||
"repo": "fin-hub",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "c6bb1aa6b770f684721fae9f1576f2c2f0128d1b758f4e33b8a277e0b4ff087a"
|
||||
},
|
||||
{
|
||||
"repo": "gate-house",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "2b63d94375d29649c41a7eae7cfb3f6e35210fd3438aa5013f3ddc22a662511a"
|
||||
},
|
||||
{
|
||||
"repo": "glas-harness",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "8f86dbdaf6535c909ff19967f7806d85c1f7a09db1e999033abf9b9007ed805b"
|
||||
},
|
||||
{
|
||||
"repo": "guide-board",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "abbc20c7ab14bcbdb73679a5143355c7ad3a7441b8f8e7bf5a1af393c7d1c1e1"
|
||||
},
|
||||
{
|
||||
"repo": "helix-forge",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "c20b96c226bb9465d7154c127ee19a94396c7b03307e7581ff827eba42651d86"
|
||||
},
|
||||
{
|
||||
"repo": "hub-core",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "a78d185973e0d0600f6ff05470ff3b840130fca4ed7922edaf08155d246e9008"
|
||||
},
|
||||
{
|
||||
"repo": "human-resources",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "d72d6b4f1479992e33400ed7c950cdc7112ead9c22abd38275717f0ae9255787"
|
||||
},
|
||||
{
|
||||
"repo": "ihp-railiance-probe",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "c20b96c226bb9465d7154c127ee19a94396c7b03307e7581ff827eba42651d86"
|
||||
},
|
||||
{
|
||||
"repo": "info-tech-canon",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "6015ec78772586e6e351422741430be8c40d7c7116403ee4b92933df342c4c72"
|
||||
},
|
||||
{
|
||||
"repo": "informed-decision",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "b43ea44c725dcbd59fe957ffc307805e0aabc23b60a4a9049005c70ca4871a62"
|
||||
},
|
||||
{
|
||||
"repo": "infospace-bench",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "74ba65dad84faffd6ce0e6d771f8650abe76b87a6ee09ee007780bd13c5cd9b2"
|
||||
},
|
||||
{
|
||||
"repo": "issue-core",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "70eb00870ed87072aeb0f7c38de0a879f1b3910fe6f976b982a191e261e8e7be"
|
||||
},
|
||||
{
|
||||
"repo": "kaizen-agentic",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "0236d486d6df6d928e7fd9f186a02273c8a59d534abb026603f7128876fbe776"
|
||||
},
|
||||
{
|
||||
"repo": "key-cape",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "901d15c15cacd844cf19a1a7b74c890ce5e0b00a3372a9e10bd7114f328596c8"
|
||||
},
|
||||
{
|
||||
"repo": "kings-guard",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "8a143864a6ce9a7b6c5a0f64eb75f67cfa99c2b9b155baac4f81a261d3a5312c"
|
||||
},
|
||||
{
|
||||
"repo": "kontextual-engine",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "5a5980f37031c15fc9b332f143ddbfbd6c2789ff6c6ec0f4c3e971cbc506830e"
|
||||
},
|
||||
{
|
||||
"repo": "llm-connect",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "27420d2b21fb45cf6cf6ac41890518f6b5a8c10027594a7e95ec535715a01296"
|
||||
},
|
||||
{
|
||||
"repo": "markitect-filter",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "95d2743d4201f6b89efedebfcd34fdb03e3f832fa00b6f4f646015c3264dfb60"
|
||||
},
|
||||
{
|
||||
"repo": "markitect-main",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "ba7e29cc82fd4d1eff61e48b1d870714f2d9c97531e839c469f892602509f137"
|
||||
},
|
||||
{
|
||||
"repo": "markitect-quarkdown",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "67c718d9bb8221b9b58ab30cbf9eb1cf340e2d2ecc1d77cb4b5bdaf28fa5befc"
|
||||
},
|
||||
{
|
||||
"repo": "markitect-tool",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "9159a4af2783b17602c23ca4b72b552641d5e1c3170a4dee1aee69c3ba9371d2"
|
||||
},
|
||||
{
|
||||
"repo": "maturity-engine",
|
||||
"kind": "required_local_index",
|
||||
"origin": "worktree_untracked_registry_fixture_not_publication_evidence",
|
||||
"sha256": "42b4cd1f9c6fe0960acadd10a337a579a6313fa46c2ff5a2a34bbacf2749d049"
|
||||
},
|
||||
{
|
||||
"repo": "net-kingdom",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "6fadeb0e89729a9226d6eebd44fbb07723512a3ebe6737f5a1113b14829447ed"
|
||||
},
|
||||
{
|
||||
"repo": "open-cmis-tck",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "e8fe7a7d0d3af104009f996c466353467e737069d0c2d04f880a86ae60d76bda"
|
||||
},
|
||||
{
|
||||
"repo": "open-reuse",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "f5e8b9cd3ceb7e336a2afc5097065a6c0453cc697c8c4aa2d2c39c1d1fe4fe66"
|
||||
},
|
||||
{
|
||||
"repo": "ops-bridge",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "aefd795eb16fcfdebc108450826a0f62e000bc61e9d78007bc6221bc2328e40c"
|
||||
},
|
||||
{
|
||||
"repo": "ops-hub",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "6fea9fee803a5d55ece3daefdfda0efff11eae8d2a0b719fbd0dec64f4f85314"
|
||||
},
|
||||
{
|
||||
"repo": "ops-mason",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "5eb499bc819c874835b8e6727ccbd5f9889ebb6f25a90018e3e574e5b6082b32"
|
||||
},
|
||||
{
|
||||
"repo": "ops-warden",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "4d3aba70ead10c826814dc766f9d23e3e0ce52475330acf57905bf62b01c819c"
|
||||
},
|
||||
{
|
||||
"repo": "phase-memory",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "e84b7603c98ae48879002b16a8302fb0e0e1b6c6e1099565570a8d8f98478d31"
|
||||
},
|
||||
{
|
||||
"repo": "policy-nexus",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "c1eb3e23621de354a9081cbf29a2f3d772b5f8538aaceca417574cde5a04c021"
|
||||
},
|
||||
{
|
||||
"repo": "rail-knative",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "e044b8f2f6bc1b8aa7c672ee33dfac69d6813fd672388713a70ab7ea4d4052c0"
|
||||
},
|
||||
{
|
||||
"repo": "rail-kubernetes",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "699f69facea1ce4e71d90fe0790db19b68d3a546015453651d3afc808d31d09d"
|
||||
},
|
||||
{
|
||||
"repo": "railiance-apps",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "2a550acf8545e95bd81842bb1b00f4db46c4dbb42b522823f043e948667ddf2c"
|
||||
},
|
||||
{
|
||||
"repo": "railiance-bootstrap",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "feefcc699e045a1a72f254d71f46460ad2362ed23c9de6e54c952711fcb33788"
|
||||
},
|
||||
{
|
||||
"repo": "railiance-cluster",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "0e67a2aca183ee7ef9c4c04e618fcb03647120f456a133e84a8f42c2b8d8fcde"
|
||||
},
|
||||
{
|
||||
"repo": "railiance-enablement",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "4b1f91b0da94e3098916f3e360b20b9926be4dd8683d6fb167a027aaf04ebfac"
|
||||
},
|
||||
{
|
||||
"repo": "railiance-fabric",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "f473644906fe3f331702b407c3519e6a3cd0d96d362cf30d80e03aac53c3b862"
|
||||
},
|
||||
{
|
||||
"repo": "railiance-forge",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "4df605d617b7656ffb94a43ab4633c8fc69ec723f68f5f5b8d5f24ad8efb6b98"
|
||||
},
|
||||
{
|
||||
"repo": "railiance-infra",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "14a76b1f1dd317a1d426502ff5f80780e9551cd1cd600543de6fb9a84cb8ada2"
|
||||
},
|
||||
{
|
||||
"repo": "railiance-master",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "f36cb2765ec04064314f43866f9a87a94a4f0f728adfaad46739f232b4986c0a"
|
||||
},
|
||||
{
|
||||
"repo": "railiance-platform",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "0315d526165368e0a513de6b15932778d43c3e6045c589c95060f555fca3202e"
|
||||
},
|
||||
{
|
||||
"repo": "railiance-telemetry",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "82f3f2d188e2d7ebea71b7262e106c5f48feaf39a2da881bb645ff9352884205"
|
||||
},
|
||||
{
|
||||
"repo": "rapp-openbao",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "044efa3d02b90e1872f2516c581159d388a8080a689f76fcd78bb4d048d6ee64"
|
||||
},
|
||||
{
|
||||
"repo": "rapp-playwright",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "79645df817858f96230f2443d66796d1078185c7afef5948f6a38632234d33bb"
|
||||
},
|
||||
{
|
||||
"repo": "rapp-policy-nexus",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "e68bdedf61eee9ca2026f46a2434ce384c26234616e74f7a5d08a06c75b3bb00"
|
||||
},
|
||||
{
|
||||
"repo": "rapp-postgres",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "d939333eed9d85172e006f526c445fbe2602a721d53f7c0d14bc68286ae08776"
|
||||
},
|
||||
{
|
||||
"repo": "rapp-sbom-nexus",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "d2ee0f193d2198bf8f16ce64c90ae7664e59a2bc307ad711426af3fd47deb057"
|
||||
},
|
||||
{
|
||||
"repo": "rapp-telemetry",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "ee8801f143ed5cf064205710a61c2bb24efc6ea8585d5786d6b679426ec4c1d4"
|
||||
},
|
||||
{
|
||||
"repo": "rapp-tenant-engine",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "d8e9c1282350b5d2fd4f4d69712aa7a5fbcd2367bb1a7fad1b6eb16a97e5d732"
|
||||
},
|
||||
{
|
||||
"repo": "rapp-user-engine",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "95b4ed2d0b189f6847b1c0899f740d1da46933a8f40491e1a02f2d4e57c819fc"
|
||||
},
|
||||
{
|
||||
"repo": "reef-railiance",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "84a89c376021e3cb980c02c41d1a31396b874bebcc3801c2d1a9a33ae4195eb5"
|
||||
},
|
||||
{
|
||||
"repo": "rein-aharness",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "20990ed0e016b3330a0d44e9b86bfff8966e82bc8800469ec0f320cfa8a63fbc"
|
||||
},
|
||||
{
|
||||
"repo": "rein-openweights",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "2f4e59b2dbc7ea6078405f5c2f2be0aebd9434bee171a429b9ebe6baa83f3c2b"
|
||||
},
|
||||
{
|
||||
"repo": "repo-manager",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "ba068a8dd7a282664e94e2acdbbb170a2ba48e260e7ce0ec8eaaa8aee6f9e7c2"
|
||||
},
|
||||
{
|
||||
"repo": "repo-scoping",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "480d7c57759d6997d7bd531e966c207bb290e978f9fe831d6d246b5592c3dd71"
|
||||
},
|
||||
{
|
||||
"repo": "repo-seed",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "8cb4e4baacd95a070a96db6d72f4fb3019cffda95606c65896f796ccac0c9c7c"
|
||||
},
|
||||
{
|
||||
"repo": "resource-control",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "31b16f7d824e376f21de44aff46d5d532d3679d5e72035ce84813572b11bbd44"
|
||||
},
|
||||
{
|
||||
"repo": "reuse-surface",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "a77c952e9e1a37a3504ccd7001a7a2493d60d46f147f277071829cd05646e31c"
|
||||
},
|
||||
{
|
||||
"repo": "risk-nexus",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "53f0bf9e0aa06a6db0aa991f0ebdaa1d2bf74cfaf4e30eed8c003a4d2186a746"
|
||||
},
|
||||
{
|
||||
"repo": "role-engine",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "1188e02e0333664b3554d11c59c12942fef9e29e6491b186dcec67e6ae3e8a5c"
|
||||
},
|
||||
{
|
||||
"repo": "runbook-tutorials",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "a74fa708c4fea755f5c52504bd227393888cca78c043349cdc182c4d783390e5"
|
||||
},
|
||||
{
|
||||
"repo": "sand-boxer",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "a182d5672d088e80b79eacff9cb37fc2f3518c3c100537e18d125abc7f169e96"
|
||||
},
|
||||
{
|
||||
"repo": "sbom-nexus",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "ed6efd581483d0058a3928b365b933cbbd98c035b0a75c866156a69961d74edc"
|
||||
},
|
||||
{
|
||||
"repo": "secrets-engine",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "d5d903336d05ccd0b75a49f7be7e866ef89c6bdb6444e8c747a5ffa934a6bbe2"
|
||||
},
|
||||
{
|
||||
"repo": "security-canon",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "9b9f9348e560a72d8032f62b2ba4820d81fe182353ccc886e4f5c57cbdea563f"
|
||||
},
|
||||
{
|
||||
"repo": "shard-wiki",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "b0bb63c0a080e46c60f24a87959c529da5e725334ed69c4ba985c39107c54ef9"
|
||||
},
|
||||
{
|
||||
"repo": "state-hub",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "eab454bd985ab6ab83c38fd29e6e58ebdfe1e09bbc5388edddaa07a88133619a"
|
||||
},
|
||||
{
|
||||
"repo": "tegwick-control",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "c20b96c226bb9465d7154c127ee19a94396c7b03307e7581ff827eba42651d86"
|
||||
},
|
||||
{
|
||||
"repo": "tenant-engine",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "4131f1b63e52306f267204cdfb0faa90d6550f8bca51f97cf1062becea95d805"
|
||||
},
|
||||
{
|
||||
"repo": "the-custodian",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "cfe7f25e91fee824347aa22ccee2a70e7dd58ac06a7106f7e2ec9e54dc163020"
|
||||
},
|
||||
{
|
||||
"repo": "user-engine",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "7133c52c2013cea70d07d33960f145c370d33a8b4397c1dc927769fcaf200cbd"
|
||||
},
|
||||
{
|
||||
"repo": "vantage-point",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "cc5cf52ead95adc2920db84299d994e54def089d3f8fd852e10d9857ab0fdd2e"
|
||||
},
|
||||
{
|
||||
"repo": "vergabe-teilnahme",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "73ffd306894d9839f4800aa028144883d012bf0fe74a310ad09ef9296c627d56"
|
||||
},
|
||||
{
|
||||
"repo": "whitehat-security",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "e4bf9bac7f62f60cf10a9fbd25aa8200c22f13d61861862ff5fa1cc90b55f37c"
|
||||
},
|
||||
{
|
||||
"repo": "whynot-control",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "c20b96c226bb9465d7154c127ee19a94396c7b03307e7581ff827eba42651d86"
|
||||
},
|
||||
{
|
||||
"repo": "whynot-design",
|
||||
"kind": "anonymous_cache_seed_not_runtime_evidence",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "541104bcafb17dcf7d26dabbce7ff93b2025a0bc6cad9bec71ac145f5ec72669"
|
||||
},
|
||||
{
|
||||
"repo": "zone-engine",
|
||||
"kind": "required_local_index",
|
||||
"origin": "committed_HEAD_fixture",
|
||||
"sha256": "b6b196089a3011f16e8a38f6914589efc72a29ee3aca4bc0c5e3cf30bee97ec4"
|
||||
}
|
||||
],
|
||||
"foreign_repo_writes": false,
|
||||
"network_fetch_needed": false,
|
||||
"preserved_prior_outcomes": [
|
||||
{
|
||||
"environment": "original isolated checkout without siblings",
|
||||
"passed": 226,
|
||||
"failed": 1,
|
||||
"failure": "required access-engine sibling missing"
|
||||
},
|
||||
{
|
||||
"environment": "partial fixture preparation failed at untracked maturity-engine registry",
|
||||
"passed": 226,
|
||||
"failed": 1,
|
||||
"failure": "required maturity-engine fixture missing; preparation failure not hidden"
|
||||
}
|
||||
],
|
||||
"final_result": {
|
||||
"command": "/home/worsch/reuse-surface/.venv/bin/python -m pytest -q tests",
|
||||
"cwd": "/tmp/cust81-reuse-suite-fixtures-r1/reuse-surface",
|
||||
"exit_code": 0,
|
||||
"passed": 227,
|
||||
"failed": 0,
|
||||
"warnings": 1,
|
||||
"duration_seconds": 7.8,
|
||||
"warning": "Starlette httpx testclient deprecation",
|
||||
"consumer_source_sha256": "dbaa40f25cc0f090143578cd1c7068cb2cb00960fafd17d5239bf8a5bac514dd"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import json
|
||||
import re
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
from datetime import date, datetime, timezone
|
||||
from pathlib import Path
|
||||
|
|
@ -103,11 +105,44 @@ def _auth_headers(source: dict[str, Any]) -> dict[str, str]:
|
|||
return {header_name: token}
|
||||
|
||||
|
||||
class _RejectAuthenticatedRedirect(urllib.request.HTTPRedirectHandler):
|
||||
"""Never replay a source credential at a redirect target.
|
||||
|
||||
A private source must register its final URL. This also rejects same-origin
|
||||
redirects because a path change can escape the reviewed repository scope.
|
||||
"""
|
||||
|
||||
def redirect_request(self, req, fp, code, msg, headers, newurl):
|
||||
raise ConnectionError("authenticated source redirect refused")
|
||||
|
||||
|
||||
def fetch_remote_index_text(url: str, source: dict[str, Any]) -> str:
|
||||
headers = {"User-Agent": USER_AGENT, **_auth_headers(source)}
|
||||
if source.get("auth_env"):
|
||||
if urllib.parse.urlsplit(url).scheme.lower() != "https":
|
||||
raise ValueError("authenticated source requires HTTPS")
|
||||
# Registrations are writable data, not credential-routing authority.
|
||||
# Operators must bind each allowed environment name to exact URLs in
|
||||
# runtime configuration before the credential itself can be read.
|
||||
try:
|
||||
allowlist = json.loads(os.environ.get("REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST", "{}"))
|
||||
allowed_urls = allowlist.get(source["auth_env"], [])
|
||||
permitted = isinstance(allowed_urls, list) and url in allowed_urls
|
||||
except (ValueError, AttributeError, TypeError):
|
||||
permitted = False
|
||||
if not permitted:
|
||||
raise ValueError("authenticated source URL is not runtime-authorized")
|
||||
auth_headers = _auth_headers(source)
|
||||
headers = {"User-Agent": USER_AGENT, **auth_headers}
|
||||
request = urllib.request.Request(url, headers=headers)
|
||||
# urllib's default redirect handler copies Authorization to the target.
|
||||
# Anonymous federation retains its existing redirect behavior.
|
||||
opener = (
|
||||
urllib.request.build_opener(_RejectAuthenticatedRedirect()).open
|
||||
if auth_headers
|
||||
else urllib.request.urlopen
|
||||
)
|
||||
try:
|
||||
with urllib.request.urlopen(request, timeout=30) as response:
|
||||
with opener(request, timeout=30) as response:
|
||||
return response.read().decode("utf-8")
|
||||
except urllib.error.HTTPError as exc:
|
||||
raise ConnectionError(
|
||||
|
|
|
|||
178
reuse_surface/private_source_verify.py
Normal file
178
reuse_surface/private_source_verify.py
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
"""Owner-prepared, receipt-only native acquisition check for CUST81 revision 1.
|
||||
|
||||
Run inside the hub environment after a separately approved credential rollout:
|
||||
python -m reuse_surface.private_source_verify --proposal FILE --disclosure FILE
|
||||
--consent FILE
|
||||
No credentials are accepted as arguments. No bodies or exception text are emitted.
|
||||
This command does not register sources or use the federation cache.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
from urllib.parse import quote
|
||||
|
||||
import yaml
|
||||
from jsonschema import Draft202012Validator
|
||||
|
||||
from reuse_surface import federation
|
||||
from reuse_surface.registry import load_schema
|
||||
|
||||
PROPOSAL_SHA256 = "7819eb51d249f5c7aff4df9fb520afcfb76603b05a4104439499102b6c20af99"
|
||||
DISCLOSURE_SHA256 = "46ac4fe49a08054cc215aade2fbaf3b36ee334520997857ed84f530b52da9d35"
|
||||
CONSENT_SHA256 = "4a4602ad926e3af855d6ddba4731430d33e4a7432c507ba0a5193b9f43056b52"
|
||||
AUTH_ENV = "REUSE_SURFACE_FORGEJO_SOURCE_READ_TOKEN"
|
||||
REPOSITORIES = frozenset({
|
||||
"rapp-openbao", "rapp-policy-nexus", "rapp-postgres", "rapp-sbom-nexus",
|
||||
"rapp-telemetry", "rapp-tenant-engine", "rapp-user-engine",
|
||||
})
|
||||
|
||||
|
||||
def _sha(content: bytes) -> str:
|
||||
return hashlib.sha256(content).hexdigest()
|
||||
|
||||
|
||||
def _load(path: Path, expected: str) -> dict[str, Any]:
|
||||
data = path.read_bytes()
|
||||
if _sha(data) != expected:
|
||||
raise ValueError("packet_digest_mismatch")
|
||||
return json.loads(data)
|
||||
|
||||
|
||||
def _raw_url(repo: str, commit: str, path: str) -> str:
|
||||
if not re.fullmatch(r"[0-9a-f]{40}", commit):
|
||||
raise ValueError("invalid_commit")
|
||||
if not re.fullmatch(r"registry/(indexes|capabilities)/[a-zA-Z0-9._-]+\.(yaml|md)", path):
|
||||
raise ValueError("invalid_metadata_path")
|
||||
return (
|
||||
f"https://forgejo.coulomb.social/api/v1/repos/coulomb/{repo}/raw/"
|
||||
f"{quote(path, safe='/')}?ref={commit}"
|
||||
)
|
||||
|
||||
|
||||
def _prepared_sources(proposal_path: Path, disclosure_path: Path, consent_path: Path):
|
||||
proposal = _load(proposal_path, PROPOSAL_SHA256)
|
||||
disclosure = _load(disclosure_path, DISCLOSURE_SHA256)
|
||||
consent = _load(consent_path, CONSENT_SHA256)
|
||||
if (
|
||||
consent["consent_status"] != "approved"
|
||||
or consent["proposal_sha256"] != DISCLOSURE_SHA256
|
||||
or consent["proposal_id"] != disclosure["id"]
|
||||
or consent["proposal_revision"] != disclosure["revision"]
|
||||
or set(consent["repositories"]) != REPOSITORIES
|
||||
):
|
||||
raise ValueError("invalid_consent")
|
||||
sources = proposal["sources"]
|
||||
approved = {source["repo"]: source for source in disclosure["sources"]}
|
||||
if len(sources) != 7 or {source["repo"] for source in sources} != REPOSITORIES:
|
||||
raise ValueError("invalid_source_scope")
|
||||
for source in sources:
|
||||
owner = approved[source["repo"]]
|
||||
files = {item["path"]: item["sha256"] for item in owner["files"]}
|
||||
if (
|
||||
source["consented_commit"] != owner["commit"]
|
||||
or source["capability_ids"] != owner["capability_ids"]
|
||||
or source["index_sha256"] != files["registry/indexes/capabilities.yaml"]
|
||||
or source["auth_env"] != AUTH_ENV
|
||||
or source["native_raw_url"] != _raw_url(
|
||||
source["repo"], owner["commit"], "registry/indexes/capabilities.yaml"
|
||||
)
|
||||
):
|
||||
raise ValueError("source_not_consented")
|
||||
yield source, files
|
||||
|
||||
|
||||
def verify(proposal_path: Path, disclosure_path: Path, consent_path: Path) -> dict[str, Any]:
|
||||
receipt: dict[str, Any] = {
|
||||
"id": "CUST81-PRIVATE-NATIVE-FETCH-RECEIPT-R1",
|
||||
"checked_at": datetime.now(timezone.utc).isoformat(),
|
||||
"proposal_sha256": PROPOSAL_SHA256,
|
||||
"disclosure_sha256": DISCLOSURE_SHA256,
|
||||
"consent_sha256": CONSENT_SHA256,
|
||||
"cache_used": False,
|
||||
"all_verified": False,
|
||||
"sources": [],
|
||||
}
|
||||
try:
|
||||
# Exhaust all packet checks before any native network request.
|
||||
sources = list(_prepared_sources(proposal_path, disclosure_path, consent_path))
|
||||
expected_urls = {
|
||||
_raw_url(source["repo"], source["consented_commit"], path)
|
||||
for source, files in sources for path in files
|
||||
}
|
||||
allowlist = json.loads(os.environ.get("REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST", "{}"))
|
||||
actual_urls = allowlist.get(AUTH_ENV)
|
||||
if (
|
||||
not isinstance(actual_urls, list)
|
||||
or len(actual_urls) != 14
|
||||
or set(actual_urls) != expected_urls
|
||||
):
|
||||
raise ValueError("runtime_scope_not_exact")
|
||||
schema = Draft202012Validator(load_schema())
|
||||
except Exception:
|
||||
receipt["error"] = "packet_scope_or_schema_validation_failed"
|
||||
return receipt
|
||||
for source, files in sources:
|
||||
row = {
|
||||
"repo": source["repo"],
|
||||
"commit": source["consented_commit"],
|
||||
"index_sha256": source["index_sha256"],
|
||||
"verified": False,
|
||||
"entry_sha256": [],
|
||||
}
|
||||
receipt["sources"].append(row)
|
||||
try:
|
||||
index_text = federation.fetch_remote_index_text(source["native_raw_url"], source)
|
||||
if _sha(index_text.encode("utf-8")) != source["index_sha256"]:
|
||||
raise ValueError("index_hash_mismatch")
|
||||
index = yaml.safe_load(index_text)
|
||||
entries = index["capabilities"]
|
||||
if not isinstance(entries, list) or [e["id"] for e in entries] != source["capability_ids"]:
|
||||
raise ValueError("unexpected_capability_ids")
|
||||
for entry in entries:
|
||||
if not {"id", "name", "summary", "vector", "domain", "status", "owner", "path"} <= entry.keys():
|
||||
raise ValueError("invalid_index_row")
|
||||
if entry["owner"] != source["repo"] or entry["path"] not in files:
|
||||
raise ValueError("unconsented_entry")
|
||||
entry_url = _raw_url(source["repo"], source["consented_commit"], entry["path"])
|
||||
entry_text = federation.fetch_remote_index_text(entry_url, source)
|
||||
digest = _sha(entry_text.encode("utf-8"))
|
||||
if digest != files[entry["path"]]:
|
||||
raise ValueError("entry_hash_mismatch")
|
||||
front = re.match(r"^---\n(.*?)\n---", entry_text, re.DOTALL)
|
||||
if front is None:
|
||||
raise ValueError("missing_front_matter")
|
||||
metadata = yaml.safe_load(front.group(1))
|
||||
schema.validate(metadata)
|
||||
if metadata["id"] != entry["id"] or metadata["owner"] != source["repo"]:
|
||||
raise ValueError("entry_identity_mismatch")
|
||||
row["entry_sha256"].append(digest)
|
||||
row["verified"] = True
|
||||
except Exception:
|
||||
# Never stringify errors: HTTP handlers and parsers can embed bodies,
|
||||
# headers, credentials, or private source text in their messages.
|
||||
row["error"] = "native_fetch_or_metadata_validation_failed"
|
||||
receipt["all_verified"] = len(receipt["sources"]) == 7 and all(
|
||||
row["verified"] for row in receipt["sources"]
|
||||
)
|
||||
return receipt
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
for name in ("proposal", "disclosure", "consent"):
|
||||
parser.add_argument(f"--{name}", type=Path, required=True)
|
||||
args = parser.parse_args()
|
||||
receipt = verify(args.proposal, args.disclosure, args.consent)
|
||||
print(json.dumps(receipt, sort_keys=True))
|
||||
return 0 if receipt["all_verified"] else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
188
tests/test_private_source_verify.py
Normal file
188
tests/test_private_source_verify.py
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
import hashlib
|
||||
import io
|
||||
import json
|
||||
from email.message import Message
|
||||
from urllib.request import HTTPSHandler, build_opener
|
||||
from urllib.response import addinfourl
|
||||
|
||||
import pytest
|
||||
import yaml
|
||||
|
||||
from reuse_surface import federation, private_source_verify as verifier
|
||||
|
||||
|
||||
def _digest(text):
|
||||
return hashlib.sha256(text.encode()).hexdigest()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def packets(tmp_path, monkeypatch):
|
||||
"""Fake owner-consented bodies; no estate credential or network transport."""
|
||||
approved, sources, bodies = [], [], {}
|
||||
for repo in sorted(verifier.REPOSITORIES):
|
||||
cap_id = f"capability.fixture.{repo}"
|
||||
commit = "a" * 40
|
||||
path = f"registry/capabilities/{cap_id}.md"
|
||||
metadata = {
|
||||
"id": cap_id, "name": "Fixture", "summary": "Fixture metadata",
|
||||
"owner": repo, "status": "draft",
|
||||
"maturity": {"discovery": {"current": "D3", "target": "D5"}, "availability": {"current": "A2", "target": "A5"}},
|
||||
"external_evidence": {"completeness": {"level": "C1"}, "reliability": {"level": "R0"}},
|
||||
}
|
||||
entry = "---\n" + yaml.safe_dump(metadata) + "---\nPrivate fixture body\n"
|
||||
index = yaml.safe_dump({"capabilities": [{
|
||||
"id": cap_id, "name": "Fixture", "summary": "Fixture metadata", "vector": "D3 / A2 / C1 / R0",
|
||||
"domain": "infotech", "status": "draft", "owner": repo, "path": path,
|
||||
}]})
|
||||
index_url = verifier._raw_url(repo, commit, "registry/indexes/capabilities.yaml")
|
||||
bodies[index_url] = index
|
||||
bodies[verifier._raw_url(repo, commit, path)] = entry
|
||||
sources.append({"repo": repo, "capability_ids": [cap_id], "consented_commit": commit,
|
||||
"native_raw_url": index_url, "index_sha256": _digest(index), "auth_env": verifier.AUTH_ENV})
|
||||
approved.append({"repo": repo, "capability_ids": [cap_id], "commit": commit, "files": [
|
||||
{"path": "registry/indexes/capabilities.yaml", "sha256": _digest(index)},
|
||||
{"path": path, "sha256": _digest(entry)},
|
||||
]})
|
||||
disclosure = json.dumps({"id": "fixture", "revision": 1, "sources": approved})
|
||||
consent = json.dumps({"consent_status": "approved", "proposal_sha256": _digest(disclosure),
|
||||
"proposal_id": "fixture", "proposal_revision": 1, "repositories": sorted(verifier.REPOSITORIES)})
|
||||
proposal = json.dumps({"sources": sources})
|
||||
paths = []
|
||||
for name, text in (("proposal", proposal), ("disclosure", disclosure), ("consent", consent)):
|
||||
path = tmp_path / f"{name}.json"
|
||||
path.write_text(text)
|
||||
paths.append(path)
|
||||
monkeypatch.setattr(verifier, f"{name.upper()}_SHA256", _digest(text))
|
||||
return paths, bodies
|
||||
|
||||
|
||||
def _transport(monkeypatch, bodies, mutate=None):
|
||||
calls = []
|
||||
|
||||
class FakeResponse:
|
||||
def __init__(self, body):
|
||||
self.body = body
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, *args):
|
||||
return False
|
||||
|
||||
def read(self):
|
||||
return self.body.encode()
|
||||
|
||||
class FakeOpener:
|
||||
def open(self, req, timeout):
|
||||
assert req.get_header("Authorization") == "Bearer fixture-secret"
|
||||
assert timeout == 30
|
||||
calls.append(req.full_url)
|
||||
body = bodies[req.full_url]
|
||||
return FakeResponse(mutate(req.full_url, body) if mutate else body)
|
||||
|
||||
monkeypatch.setenv(verifier.AUTH_ENV, "fixture-secret")
|
||||
monkeypatch.setenv("REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST", json.dumps({verifier.AUTH_ENV: list(bodies)}))
|
||||
monkeypatch.setattr(federation.urllib.request, "build_opener", lambda handler: FakeOpener())
|
||||
# Deliberately prohibit composition/cache resolution; direct fetch must work.
|
||||
monkeypatch.setattr(federation, "resolve_source_index_path", lambda *a, **k: pytest.fail("cache path used"))
|
||||
return calls
|
||||
|
||||
|
||||
def test_exact_consented_native_index_and_entries_no_cache(packets, monkeypatch):
|
||||
paths, bodies = packets
|
||||
calls = _transport(monkeypatch, bodies)
|
||||
receipt = verifier.verify(*paths)
|
||||
assert receipt["all_verified"]
|
||||
assert not receipt["cache_used"]
|
||||
assert len(calls) == 14
|
||||
assert all("?ref=" + "a" * 40 in url for url in calls)
|
||||
output = json.dumps(receipt)
|
||||
assert "fixture-secret" not in output
|
||||
assert "Private fixture body" not in output
|
||||
|
||||
|
||||
@pytest.mark.parametrize("suffix", [".yaml", ".md"])
|
||||
def test_changed_unconsented_body_fails(packets, monkeypatch, suffix):
|
||||
paths, bodies = packets
|
||||
_transport(monkeypatch, bodies, lambda url, text: text + "changed" if suffix in url else text)
|
||||
receipt = verifier.verify(*paths)
|
||||
assert not receipt["all_verified"]
|
||||
assert all(not row["verified"] for row in receipt["sources"])
|
||||
|
||||
|
||||
def test_packet_tampering_rejected_before_fetch(packets, monkeypatch):
|
||||
paths, _ = packets
|
||||
paths[0].write_text(paths[0].read_text() + " ")
|
||||
monkeypatch.setattr(federation, "fetch_remote_index_text", lambda *a: pytest.fail("network before consent checks"))
|
||||
assert verifier.verify(*paths)["error"] == "packet_scope_or_schema_validation_failed"
|
||||
|
||||
|
||||
def test_missing_auth_fails_before_transport(packets, monkeypatch):
|
||||
paths, bodies = packets
|
||||
monkeypatch.setenv("REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST", json.dumps({verifier.AUTH_ENV: list(bodies)}))
|
||||
monkeypatch.delenv(verifier.AUTH_ENV, raising=False)
|
||||
monkeypatch.setattr(federation.urllib.request, "build_opener", lambda *a: pytest.fail("transport without auth"))
|
||||
receipt = verifier.verify(*paths)
|
||||
assert not receipt["all_verified"]
|
||||
assert all(not row["verified"] for row in receipt["sources"])
|
||||
|
||||
|
||||
def test_error_output_never_embeds_secret_or_body(packets, monkeypatch):
|
||||
paths, bodies = packets
|
||||
monkeypatch.setenv("REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST", json.dumps({verifier.AUTH_ENV: list(bodies)}))
|
||||
|
||||
def failure(*args):
|
||||
raise ValueError("fixture-secret Private fixture body")
|
||||
|
||||
monkeypatch.setattr(federation, "fetch_remote_index_text", failure)
|
||||
output = json.dumps(verifier.verify(*paths))
|
||||
assert "fixture-secret" not in output
|
||||
assert "Private fixture body" not in output
|
||||
|
||||
|
||||
def test_broadened_runtime_reader_scope_fails_before_fetch(packets, monkeypatch):
|
||||
paths, bodies = packets
|
||||
monkeypatch.setenv("REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST", json.dumps({verifier.AUTH_ENV: list(bodies) + ["https://attacker.invalid/"]}))
|
||||
monkeypatch.setattr(federation, "fetch_remote_index_text", lambda *a: pytest.fail("broadened scope used"))
|
||||
assert not verifier.verify(*paths)["all_verified"]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("target", ["https://attacker.invalid/stolen", "https://forgejo.coulomb.social/other/repo"])
|
||||
def test_native_auth_redirect_never_replays_header(monkeypatch, target):
|
||||
calls = []
|
||||
|
||||
class FakeHTTPS(HTTPSHandler):
|
||||
def https_open(self, req):
|
||||
calls.append(req.full_url)
|
||||
headers = Message()
|
||||
headers["Location"] = target
|
||||
result = addinfourl(io.BytesIO(b""), headers, req.full_url, 302)
|
||||
result.msg = "Found"
|
||||
return result
|
||||
|
||||
# Use real urllib opener/redirect/error processors and fake HTTPS transport.
|
||||
opener = build_opener(FakeHTTPS(), federation._RejectAuthenticatedRedirect())
|
||||
monkeypatch.setattr(federation.urllib.request, "build_opener", lambda *a: opener)
|
||||
monkeypatch.setenv("FIXTURE_AUTH", "fixture-secret")
|
||||
monkeypatch.setenv("REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST", json.dumps({"FIXTURE_AUTH": ["https://forgejo.coulomb.social/approved"]}))
|
||||
with pytest.raises(ConnectionError, match="redirect refused"):
|
||||
federation.fetch_remote_index_text("https://forgejo.coulomb.social/approved", {"repo": "fixture", "auth_env": "FIXTURE_AUTH"})
|
||||
assert calls == ["https://forgejo.coulomb.social/approved"]
|
||||
|
||||
|
||||
def test_authenticated_http_rejected_before_transport(monkeypatch):
|
||||
monkeypatch.setenv("FIXTURE_AUTH", "fixture-secret")
|
||||
monkeypatch.setattr(federation.urllib.request, "build_opener", lambda *a: pytest.fail("cleartext transport"))
|
||||
with pytest.raises(ValueError, match="HTTPS"):
|
||||
federation.fetch_remote_index_text("http://forgejo.coulomb.social/approved", {"repo": "fixture", "auth_env": "FIXTURE_AUTH"})
|
||||
|
||||
|
||||
@pytest.mark.parametrize("config", [None, "broken-json", "[]", '{"FIXTURE_AUTH":"https://attacker.invalid/"}', '{"OTHER_AUTH":["https://attacker.invalid/"]}'])
|
||||
def test_unapproved_auth_target_rejected_before_credential_lookup(monkeypatch, config):
|
||||
if config is None:
|
||||
monkeypatch.delenv("REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST", raising=False)
|
||||
else:
|
||||
monkeypatch.setenv("REUSE_SURFACE_AUTH_SOURCE_ALLOWLIST", config)
|
||||
monkeypatch.setattr(federation, "_auth_headers", lambda *a: pytest.fail("unauthorized credential lookup"))
|
||||
with pytest.raises(ValueError, match="runtime-authorized"):
|
||||
federation.fetch_remote_index_text("https://attacker.invalid/", {"repo": "fixture", "auth_env": "FIXTURE_AUTH"})
|
||||
66
workplans/REUSE-WP-0025-private-source-reader-boundary.md
Normal file
66
workplans/REUSE-WP-0025-private-source-reader-boundary.md
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
---
|
||||
id: REUSE-WP-0025
|
||||
type: workplan
|
||||
title: "Bind private source reader to consented metadata URLs"
|
||||
domain: infotech
|
||||
repo: reuse-surface
|
||||
status: blocked
|
||||
owner: reuse-surface-owner-preparation-worker
|
||||
created: "2026-10-15"
|
||||
updated: "2026-10-15"
|
||||
flavor: implementation
|
||||
depends_on: [CUST-WP-0081]
|
||||
---
|
||||
|
||||
Prepared in an isolated owner checkout under the user's explicit worker-agent
|
||||
request. This worker has supervised local preparation scope only; no founder
|
||||
credential approval, permanent repo-owner authority, deployment, or source-owner
|
||||
consent is inferred. The custodian integration agent handles hub synchronization
|
||||
after accepting the proposed owner workitem. No State Hub mutation is performed
|
||||
from this isolated checkout.
|
||||
|
||||
## Independent consumer review
|
||||
|
||||
```task
|
||||
id: REUSE-WP-0025-T01
|
||||
status: done
|
||||
priority: high
|
||||
```
|
||||
|
||||
Reviewed consumer INTENT, native authenticated acquisition, public API registration
|
||||
fields, and stale-cache behavior. The deployed baseline is unsuitable for a new
|
||||
private reader: registered auth_env/URL can route runtime credentials to arbitrary
|
||||
hosts; urllib's default redirect handler also forwards Authorization. Review is
|
||||
`changes_requested`; metadata consent does not authorize reader provisioning.
|
||||
See docs/assessments/cust81-private-reader/owner-review-r1.md.
|
||||
|
||||
## Prepare native boundary and verification helper
|
||||
|
||||
```task
|
||||
id: REUSE-WP-0025-T02
|
||||
status: done
|
||||
priority: high
|
||||
```
|
||||
|
||||
Patch rejects authenticated cleartext URLs and all redirects, and requires an
|
||||
operator-controlled exact env-to-URL allowlist before credential lookup. Receipt
|
||||
helper invokes native fetch directly for all seven consented index/entry pairs,
|
||||
checks exact hashes and entry schema, and never uses cached fallback. Fake
|
||||
transport tests and local committed metadata validation are preparation evidence,
|
||||
not live authenticated fetch receipts.
|
||||
|
||||
## Review, publish and verify deployed consumer
|
||||
|
||||
```task
|
||||
id: REUSE-WP-0025-T03
|
||||
status: wait
|
||||
priority: high
|
||||
```
|
||||
|
||||
Owner review and exact credential/rollout disposition remain pending. Platform/apps
|
||||
must inventory every existing authenticated dynamic registration without reading
|
||||
Secret values, migrate approved exact URLs, and deliver a reviewed image containing
|
||||
the fix plus the exact reader allowlist. Then run helper inside the hub environment
|
||||
with the owner-provided packets, verify all seven sources fresh, and hand a
|
||||
sanitized receipt to the custodian for registration and final 70-capability audit.
|
||||
No new PAT is delivered to the current baseline consumer.
|
||||
Loading…
Add table
Add a link
Reference in a new issue