Finish KG-WP-0003: stream completeness and live qonto observation
Classify evidence as load-bearing or attributive, draft the emission-cadence declaration for Taxonomy, treat silence as a stream finding, keep completeness separate from record richness, forbid immune memory as a state plane, and make containment proposals reconstructable to their origin. Observe real qonto-assistant audit events; deny-class completeness stays unknown until the source publishes a heartbeat. Assistant: grok Assistant-Session: 01a05ef1-9e5a-70f2-b0ff-0b05d6b38ae9
This commit is contained in:
parent
c85646dc3c
commit
9daea96c43
35 changed files with 2023 additions and 138 deletions
|
|
@ -25,6 +25,7 @@ from __future__ import annotations
|
|||
|
||||
import argparse
|
||||
import ast
|
||||
import re
|
||||
import sys
|
||||
from datetime import date
|
||||
from pathlib import Path
|
||||
|
|
@ -53,6 +54,28 @@ TOOLING_IMPORTS = {
|
|||
"docker": "container runtime client",
|
||||
}
|
||||
|
||||
# §3.4 rule 1 — no standing credential held in the repository or its
|
||||
# configuration. Filenames that would be a standing secret, and assignments
|
||||
# of well-known secret env vars to string literals in src/.
|
||||
CREDENTIAL_FILENAMES = {
|
||||
".env",
|
||||
".env.local",
|
||||
".env.production",
|
||||
"credentials.json",
|
||||
"secrets.yaml",
|
||||
"secrets.yml",
|
||||
"id_rsa",
|
||||
"id_ed25519",
|
||||
"id_ecdsa",
|
||||
}
|
||||
CREDENTIAL_LITERAL = re.compile(
|
||||
r"""(?x)
|
||||
\b(?:VAULT_TOKEN|OPENBAO_TOKEN|BAO_TOKEN|AWS_SECRET_ACCESS_KEY|
|
||||
PRIVATE_KEY|BEGIN\ (?:RSA\ )?PRIVATE\ KEY)
|
||||
"""
|
||||
)
|
||||
SKIP_CREDENTIAL_SCAN_DIRS = {".git", ".venv", "__pycache__", ".pytest_cache", ".ruff_cache"}
|
||||
|
||||
|
||||
def load_declaration() -> dict:
|
||||
if not DECL.exists():
|
||||
|
|
@ -98,6 +121,27 @@ def scan() -> list[tuple[Path, str, str]]:
|
|||
return hits
|
||||
|
||||
|
||||
def scan_standing_credentials() -> list[tuple[Path, str]]:
|
||||
"""§3.4 rule 1: no standing credential in the repository or its config."""
|
||||
hits: list[tuple[Path, str]] = []
|
||||
for path in ROOT.rglob("*"):
|
||||
if not path.is_file():
|
||||
continue
|
||||
if any(part in SKIP_CREDENTIAL_SCAN_DIRS for part in path.parts):
|
||||
continue
|
||||
if path.name in CREDENTIAL_FILENAMES:
|
||||
hits.append((path, f"credential-shaped file {path.name}"))
|
||||
continue
|
||||
if path.suffix in {".pem", ".key"} and "test" not in path.parts:
|
||||
hits.append((path, f"key material file {path.name}"))
|
||||
if SRC.is_dir():
|
||||
for path in sorted(SRC.rglob("*.py")):
|
||||
text = path.read_text(encoding="utf-8")
|
||||
if CREDENTIAL_LITERAL.search(text):
|
||||
hits.append((path, "standing-credential literal or private-key block"))
|
||||
return hits
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--report", action="store_true", help="print the declaration summary")
|
||||
|
|
@ -106,8 +150,11 @@ def main() -> int:
|
|||
decl = load_declaration()
|
||||
declared = {c.get("id") for c in decl.get("tooling_contacts") or []}
|
||||
hits = scan()
|
||||
credential_hits = scan_standing_credentials()
|
||||
|
||||
undeclared = [h for h in hits if h[1] not in declared]
|
||||
rules = decl.get("agent_principal_rules") or {}
|
||||
checks = decl.get("agent_principal_rule_checks") or {}
|
||||
|
||||
if args.report:
|
||||
print(f"kings-guard — layer {decl['layer']}, standard v{decl['standard_version']}")
|
||||
|
|
@ -121,6 +168,10 @@ def main() -> int:
|
|||
if review and date.fromisoformat(str(review)) < today:
|
||||
stale = " [REVIEW OVERDUE]"
|
||||
print(f" - {cap['id']}: {cap.get('owner_status', '?')}{stale}")
|
||||
print(" agent-principal rule checks (§3.4):")
|
||||
for name, meta in checks.items():
|
||||
form = meta.get("form", "unspecified") if isinstance(meta, dict) else "unspecified"
|
||||
print(f" - {name}: {form} (claimed={rules.get(name)})")
|
||||
|
||||
if undeclared:
|
||||
print("", file=sys.stderr)
|
||||
|
|
@ -134,8 +185,29 @@ def main() -> int:
|
|||
print(" engine gap — do not declare this to make the check pass.", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
if credential_hits:
|
||||
print("", file=sys.stderr)
|
||||
print("FAIL: standing credential material (§3.4 rule 1)", file=sys.stderr)
|
||||
for path, what in credential_hits:
|
||||
try:
|
||||
rel = path.relative_to(ROOT)
|
||||
except ValueError:
|
||||
rel = path
|
||||
print(f" {rel}: {what}", file=sys.stderr)
|
||||
print("", file=sys.stderr)
|
||||
print(" Authority is per task, time-bounded, and attributable to the", file=sys.stderr)
|
||||
print(" principal acted for. Do not hold a standing secret here.", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
if rules.get("no_standing_credential") is not True:
|
||||
print("FAIL: layer.yaml does not claim no_standing_credential (§3.4 rule 1)", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
if not args.report:
|
||||
print(f"OK: no direct Tooling client in {SRC.relative_to(ROOT)} (§5, §11)")
|
||||
print(
|
||||
f"OK: no direct Tooling client in {SRC.relative_to(ROOT)}; "
|
||||
"no standing credential (§5, §11, §3.4 rule 1)"
|
||||
)
|
||||
return 0
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue