Add value-safe verification and audit reporting
Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a0217e-8c4c-7383-be6b-f50a6e485306
This commit is contained in:
parent
491e706a70
commit
c4504c6de9
19 changed files with 598 additions and 50 deletions
|
|
@ -7,12 +7,13 @@ Command surface (FR7):
|
|||
plan <decision-or-ref> --stage <stage>
|
||||
apply <decision-or-ref> --stage <stage> [--dry-run] [--bootstrap-token-file F]
|
||||
provision <catalog-id> --stage <stage> (--from-file F | --generate) --field NAME
|
||||
verify <catalog-id> [--positive] [--negative] [--field NAME]
|
||||
verify <catalog-id> [--positive] [--negative] [--field NAME] [--negative-token-file F]
|
||||
handoff <catalog-id> --stage <stage> --role-id-file F --secret-id-file F
|
||||
exec --catalog <catalog-id> [--field NAME] [--mode auto|npm-config|exec-env] -- CMD...
|
||||
route <catalog-id> [--json]
|
||||
revoke <catalog-id>
|
||||
lifecycle suspend|deactivate|destroy <catalog-id>
|
||||
audit <catalog-id> [--json]
|
||||
|
||||
Every privileged action is decision-gated and writes non-secret evidence.
|
||||
`plan` and `apply --dry-run` never mutate OpenBao.
|
||||
|
|
@ -221,6 +222,13 @@ def cmd_verify(cfg: Config, args) -> int:
|
|||
raise VerificationError(f"lane '{entry.id}' has no field to verify")
|
||||
positive = args.positive or not args.negative
|
||||
negative = args.negative or not args.positive
|
||||
unrelated_token = None
|
||||
if entry.stores_kv_value() and negative and args.negative_token_file:
|
||||
from secrets_engine.openbao import read_strict_token_file
|
||||
|
||||
unrelated_token = read_strict_token_file(
|
||||
Path(args.negative_token_file), purpose="negative verification token"
|
||||
)
|
||||
if entry.stores_kv_value():
|
||||
results = []
|
||||
if positive:
|
||||
|
|
@ -234,7 +242,12 @@ def cmd_verify(cfg: Config, args) -> int:
|
|||
# Denial is path-scoped, so one probe covers every field on this path.
|
||||
results.extend(
|
||||
run_verification(
|
||||
client, entry, fields[0], positive=False, negative=True
|
||||
client,
|
||||
entry,
|
||||
fields[0],
|
||||
positive=False,
|
||||
negative=True,
|
||||
unrelated_token=unrelated_token,
|
||||
)
|
||||
)
|
||||
else:
|
||||
|
|
@ -460,6 +473,21 @@ def cmd_lifecycle(cfg: Config, args) -> int:
|
|||
return 0
|
||||
|
||||
|
||||
def cmd_audit(cfg: Config, args) -> int:
|
||||
"""Summarize allowlisted non-secret evidence for one cataloged lane."""
|
||||
import json
|
||||
|
||||
from secrets_engine.audit import summarize_lane_evidence
|
||||
|
||||
entry = get_entry(cfg.catalog_dir, args.catalog_id)
|
||||
summary = summarize_lane_evidence(cfg.evidence_dir, entry.id)
|
||||
if args.json:
|
||||
print(json.dumps(summary.to_json(), indent=2, sort_keys=True))
|
||||
else:
|
||||
print(summary.render())
|
||||
return 0
|
||||
|
||||
|
||||
# -- parser ----------------------------------------------------------------
|
||||
|
||||
|
||||
|
|
@ -512,6 +540,11 @@ def build_parser() -> argparse.ArgumentParser:
|
|||
ve.add_argument("--field", default=None)
|
||||
ve.add_argument("--positive", action="store_true")
|
||||
ve.add_argument("--negative", action="store_true")
|
||||
ve.add_argument(
|
||||
"--negative-token-file",
|
||||
default=None,
|
||||
help="mode-0600 out-of-repo token for a real unrelated identity",
|
||||
)
|
||||
add_token_arg(ve)
|
||||
ve.set_defaults(func=cmd_verify)
|
||||
|
||||
|
|
@ -571,6 +604,11 @@ def build_parser() -> argparse.ArgumentParser:
|
|||
add_token_arg(lp)
|
||||
lp.set_defaults(func=cmd_lifecycle)
|
||||
|
||||
au = sub.add_parser("audit", help="summarize non-secret local lane evidence")
|
||||
au.add_argument("catalog_id")
|
||||
au.add_argument("--json", action="store_true")
|
||||
au.set_defaults(func=cmd_audit)
|
||||
|
||||
return p
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue