Prepare canonical custody for audit E2 third attempt
Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a02991-be07-7bb3-8b6d-e9701b5621de
This commit is contained in:
parent
fc198b896f
commit
2a5c002aa5
5 changed files with 123 additions and 0 deletions
|
|
@ -107,6 +107,7 @@
|
|||
| task | RAILIANCE-WP-0025-T03 | done | — | workplans/RAILIANCE-WP-0025-versioned-ephemeral-custody-lifecycle.md |
|
||||
| task | RAILIANCE-WP-0025-T04 | done | — | workplans/RAILIANCE-WP-0025-versioned-ephemeral-custody-lifecycle.md |
|
||||
| task | RAILIANCE-WP-0025-T05 | done | — | workplans/RAILIANCE-WP-0025-versioned-ephemeral-custody-lifecycle.md |
|
||||
| task | RAILIANCE-WP-0025-T06 | done | — | workplans/RAILIANCE-WP-0025-versioned-ephemeral-custody-lifecycle.md |
|
||||
| task | RPF-WP-0018-T01 | done | — | workplans/RPF-WP-0018-policy-surface-alignment.md |
|
||||
| task | RPF-WP-0018-T02 | done | — | workplans/RPF-WP-0018-policy-surface-alignment.md |
|
||||
| task | RPF-WP-0018-T03 | done | — | workplans/RPF-WP-0018-policy-surface-alignment.md |
|
||||
|
|
|
|||
59
docs/WH-ENG-20260822-AUDIT-E2-03-custody-contract.json
Normal file
59
docs/WH-ENG-20260822-AUDIT-E2-03-custody-contract.json
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
{
|
||||
"interface": "railiance.custody-projection-contract",
|
||||
"version": 1,
|
||||
"workplan_id": "RAILIANCE-WP-0025",
|
||||
"engagement_id": "WH-ENG-20260822-AUDIT-E2-03",
|
||||
"status": "proposed",
|
||||
"engagement_contract_sha256": "3e70e4581ddcfc8dea35ec21e72d3e2cdd7699ee9e9932d8eee97b64b648d5cd",
|
||||
"target": {
|
||||
"id": "audit-core",
|
||||
"namespace": "audit-core",
|
||||
"deployment": "audit-core",
|
||||
"container": "audit-core",
|
||||
"sender_external_secret": "audit-core-senders",
|
||||
"revision": "abd22fa0a64faca4c77752bb500c892d4f075ac5",
|
||||
"image_digest": "sha256:c2fe39a0185b99be3fc0cb14d2de69772b8e66e20490097c9d11d90cc39719a6",
|
||||
"contract_sha256": "5bf926e9da394da28e1d2cb11ead33d6d27a20b40914d8097b1041b35a18696e"
|
||||
},
|
||||
"runner": {
|
||||
"namespace": "whitehat",
|
||||
"service_account": "whitehat-runner",
|
||||
"secret_name": "whitehat-e2-audit-credentials-03",
|
||||
"mount_root": "/var/run/secrets/whitehat",
|
||||
"manifest_sha256": "41201af08df484659f1eb89e19acb8042fc44cd3eeac1369784e8ce9e2166c4a"
|
||||
},
|
||||
"window": {
|
||||
"starts_at": "2026-08-22T22:00:00Z",
|
||||
"projection_cutoff": "2026-08-22T22:03:00Z",
|
||||
"expires_at": "2026-08-22T22:15:00Z"
|
||||
},
|
||||
"authority": {
|
||||
"remote": "railiance01",
|
||||
"registry_path": "platform/workloads/audit-core/senders",
|
||||
"registry_field": "senders.json",
|
||||
"kv_mount": "platform",
|
||||
"kv_prefix": "engagements/WH-ENG-20260822-AUDIT-E2-03/audit-core",
|
||||
"eso_service_account": "external-secrets",
|
||||
"eso_namespace": "external-secrets"
|
||||
},
|
||||
"identities": [
|
||||
{
|
||||
"handle": "token-a",
|
||||
"role": "attacker",
|
||||
"sender_name": "whitehat-e2-a-20260822-03",
|
||||
"tenant": "tenant:trial:whitehat-a-20260822-03",
|
||||
"mount_path": "/var/run/secrets/whitehat/token-a",
|
||||
"may_read": true,
|
||||
"may_write": true
|
||||
},
|
||||
{
|
||||
"handle": "token-b",
|
||||
"role": "owner",
|
||||
"sender_name": "whitehat-e2-b-20260822-03",
|
||||
"tenant": "tenant:trial:whitehat-b-20260822-03",
|
||||
"mount_path": "/var/run/secrets/whitehat/token-b",
|
||||
"may_read": true,
|
||||
"may_write": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ from __future__ import annotations
|
|||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
|
@ -177,6 +178,17 @@ def post_receipt(receipt: dict[str, Any], api_base: str) -> dict[str, Any]:
|
|||
return response
|
||||
|
||||
|
||||
def write_receipt(path: Path, receipt: dict[str, Any]) -> None:
|
||||
"""Persist only the canonical broker receipt, never the CLI wrapper."""
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
descriptor = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
|
||||
try:
|
||||
with os.fdopen(descriptor, "w", encoding="utf-8") as handle:
|
||||
handle.write(json.dumps(receipt, indent=2, sort_keys=True) + "\n")
|
||||
finally:
|
||||
os.chmod(path, 0o600)
|
||||
|
||||
|
||||
def show(contract: dict[str, Any]) -> dict[str, Any]:
|
||||
return {
|
||||
"interface": BROKER_INTERFACE,
|
||||
|
|
@ -206,10 +218,12 @@ def main() -> int:
|
|||
parser.add_argument("--consumer-root", type=Path)
|
||||
parser.add_argument("--reviewer")
|
||||
parser.add_argument("--note")
|
||||
parser.add_argument("--receipt-out", type=Path)
|
||||
parser.add_argument("--state-hub", default="http://127.0.0.1:8000")
|
||||
args = parser.parse_args()
|
||||
try:
|
||||
contract = validate_projection_contract(load_json(args.contract))
|
||||
receipt_out = None
|
||||
if args.command == "show":
|
||||
result = show(contract)
|
||||
elif args.command == "status":
|
||||
|
|
@ -223,6 +237,7 @@ def main() -> int:
|
|||
"secret_values_observed": False,
|
||||
}
|
||||
else:
|
||||
receipt_out = receipt
|
||||
result = {
|
||||
"ready": True,
|
||||
"engagement_id": contract["engagement_id"],
|
||||
|
|
@ -239,6 +254,7 @@ def main() -> int:
|
|||
verification = verify_adapter(args.consumer_root)
|
||||
receipt = build_approval(contract, args.reviewer, verification)
|
||||
posted = post_receipt(receipt, args.state_hub)
|
||||
receipt_out = receipt
|
||||
result = {"submitted": True, "message_id": posted["id"], "receipt": receipt}
|
||||
else:
|
||||
if not args.reviewer or not args.note:
|
||||
|
|
@ -246,6 +262,10 @@ def main() -> int:
|
|||
receipt = build_change_request(contract, args.reviewer, args.note)
|
||||
posted = post_receipt(receipt, args.state_hub)
|
||||
result = {"submitted": True, "message_id": posted["id"], "receipt": receipt}
|
||||
if args.receipt_out:
|
||||
if receipt_out is None:
|
||||
raise ReadinessError("--receipt-out requires approve or a ready status")
|
||||
write_receipt(args.receipt_out, receipt_out)
|
||||
print(json.dumps(result, indent=2, sort_keys=True))
|
||||
return 0
|
||||
except (ContractError, OSError, ReadinessError) as exc:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import importlib.util
|
||||
import json
|
||||
import subprocess
|
||||
import stat
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
|
@ -84,6 +86,29 @@ class BrokerReadinessTests(unittest.TestCase):
|
|||
with self.assertRaises(module.ReadinessError):
|
||||
module.build_change_request(contract, "whitehat-owner", "")
|
||||
|
||||
def test_receipt_export_is_raw_canonical_document_with_private_mode(self) -> None:
|
||||
contract = projection_contract()
|
||||
verification = {
|
||||
"passed": True,
|
||||
"adapter": {
|
||||
"repo": "whitehat-security",
|
||||
"revision": "a" * 40,
|
||||
"path": str(module.DEFAULT_ADAPTER),
|
||||
"sha256": "b" * 64,
|
||||
"tests_passed": True,
|
||||
},
|
||||
"secret_values_observed": False,
|
||||
}
|
||||
receipt = module.build_approval(contract, "whitehat-owner", verification)
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
path = Path(directory) / "nested" / "broker.json"
|
||||
module.write_receipt(path, receipt)
|
||||
observed = json.loads(path.read_text(encoding="utf-8"))
|
||||
mode = stat.S_IMODE(path.stat().st_mode)
|
||||
self.assertEqual(receipt, observed)
|
||||
self.assertNotIn("submitted", observed)
|
||||
self.assertEqual(0o600, mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
|
|||
|
|
@ -162,3 +162,21 @@ was deployed; those remain a separate owner decision.
|
|||
- [x] Remote command arguments survive shell-hostile templates unchanged.
|
||||
- [x] Every modeled partial mutation cleans only its receipt-bound scope.
|
||||
- [x] Expired cleanup is executable but no unattended scheduler is deployed.
|
||||
|
||||
## T06 — Export the broker receipt consumed by admission
|
||||
|
||||
```task
|
||||
id: RAILIANCE-WP-0025-T06
|
||||
status: done
|
||||
priority: high
|
||||
```
|
||||
|
||||
The first fresh-run setup review found that `approve` posted the canonical
|
||||
broker-readiness receipt to State Hub but exposed no safe way to persist the
|
||||
raw receipt document required by Whitehat's `--broker-receipt` input. Redirecting
|
||||
the command output would save a CLI wrapper rather than the canonical document.
|
||||
|
||||
`approve` and a ready `status` now accept `--receipt-out`, create parent
|
||||
directories, write only the canonical value-safe receipt, and force mode
|
||||
`0600`. Other commands fail closed when that option is supplied. Regression
|
||||
coverage proves the wrapper is excluded and the file mode is private.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue