fix(work): import hashlib and wire heal subparser into main CLI

This commit is contained in:
operator
2026-10-09 23:13:43 +00:00
parent c3b4b1cd28
commit f75977ca6e
202 changed files with 4185 additions and 4142 deletions
+220 -24
View File
@@ -153,6 +153,10 @@ VALID_NODES = ["muse", "pip", "646", "opm", "def", "dev"]
KEY_REQUEST_TTL_SECONDS = 2 * 3600
INPUT_WAIT_TTL_SECONDS = 30 * 60
BROWSER_APPROVAL_TTL_SECONDS = 30 * 60
# Tail cap for key-request audit scans: check_node_key_request scans only the
# last N lines of box-ctl.jsonl (key events cluster at the end), falling back
# to a full scan when the tail holds no relevant record for the node.
KEY_SCAN_TAIL_LINES = 5000
# Trusted infrastructure IPs safe for automated approval
TRUSTED_IPS = {
@@ -187,6 +191,51 @@ def is_trusted_target(target: str, card_text: str = "") -> bool:
return True
return False
def is_plausible_target(target: str) -> bool:
"""True if target looks like a real network endpoint, not a parser artifact.
P1 fix (2026-10-08): the target-extraction regex happily captures garbage
tokens like "echo" from dialog text ("connect to echo over SSH"), which
then fail-closed to is_trusted=False and page CRITICAL ~6/day for pip's
routine Heartbeat dialog. This validator runs BEFORE the is_trusted check:
only strict IPv4 (0-255 octets) or plausible hostnames pass.
"""
if not target or not isinstance(target, str):
return False
t = target.strip().lower().rstrip(".")
if not t:
return False
# Strict IPv4: four octets, each 0-255, no leading-zero weirdness
parts = t.split(".")
if len(parts) == 4:
try:
octets = [int(p) for p in parts]
# Reject leading zeros ("01") to avoid octal ambiguity, except "0" itself
if all(0 <= o <= 255 for o in octets) and all(
p == str(o) for p, o in zip(parts, octets)
):
return True
except ValueError:
pass
# Four numeric parts but invalid octets (e.g. 999.999.999.999) -> not plausible
if all(p.isdigit() for p in parts):
return False
# Hostname: "localhost" or a dotted name with valid labels
if t == "localhost":
return True
# All-numeric dotted tokens that aren't valid IPv4 (e.g. "1.2.3") are
# parser artifacts, not hostnames
if "." in t and all(c.isdigit() or c == "." for c in t):
return False
if "." in t:
import re as _re
if _re.match(r"^[a-z0-9]([a-z0-9.-]*[a-z0-9])?$", t):
# Each label 1-63 chars, no empty labels
if all(1 <= len(label) <= 63 for label in t.split(".")):
return True
return False
REDACT_PATTERNS = [
(re.compile(r"Bearer\s+[A-Za-z0-9._~+/-]+=*", re.IGNORECASE), "Bearer [REDACTED]"),
@@ -308,6 +357,63 @@ def _rec_approval_type(rec: dict) -> str:
return _approval_type(rec.get("action", ""))
def _tail_lines(path: Path, n: int) -> list:
"""Return up to the last n lines of path as strings (seek-based, no full read)."""
with open(path, "rb") as f:
f.seek(0, os.SEEK_END)
pos = f.tell()
if pos == 0:
return []
data = b""
while pos > 0 and data.count(b"\n") <= n:
step = min(8192, pos)
pos -= step
f.seek(pos)
data = f.read(step) + data
return data.decode("utf-8", "replace").split("\n")[-n:]
def _scan_key_lines(lines, node: str):
"""Scan audit lines (forward order) for a node's key-request state.
Returns (latest_req, resolved, saw_relevant). A suffix-slice scan is
authoritative when saw_relevant: the newest relevant record in a suffix
decides the outcome identically to a full scan (any newer request or
later resolution would itself lie in the suffix).
"""
latest_req = None
resolved = False
saw_relevant = False
for line in lines:
line = line.strip()
if not line:
continue
# Prefilter: only key-approval actions can affect the outcome, and
# all carry this substring; skip json.loads for everything else.
if "key-approval" not in line:
continue
try:
rec = json.loads(line)
except Exception:
continue
if rec.get("name") != node:
continue
act = rec.get("action")
if act == "key-approval-request":
latest_req = rec
resolved = False
saw_relevant = True
elif _rec_approval_type(rec) == "key" and act in (
"key-approval-allow", "key-approval-deny", "key-approval-expired",
):
# Only a KEY-type resolution clears a key request. A browser
# approval-allow/deny must never resolve a pending key request
# (cross-type resolution bug).
resolved = True
saw_relevant = True
return latest_req, resolved, saw_relevant
def check_node_key_request(node: str) -> dict:
"""Check if node has an active unfulfilled key approval request in box-ctl.jsonl.
@@ -317,32 +423,15 @@ def check_node_key_request(node: str) -> dict:
"""
if not CTL_LOG.exists():
return None
latest_req = None
resolved = False
now = datetime.now(timezone.utc).timestamp()
try:
with open(CTL_LOG, "r") as f:
for line in f:
line = line.strip()
if not line:
continue
try:
rec = json.loads(line)
except Exception:
continue
if rec.get("name") != node:
continue
act = rec.get("action")
if act == "key-approval-request":
latest_req = rec
resolved = False
elif _rec_approval_type(rec) == "key" and act in (
"key-approval-allow", "key-approval-deny", "key-approval-expired",
):
# Only a KEY-type resolution clears a key request. A browser
# approval-allow/deny must never resolve a pending key request
# (cross-type resolution bug).
resolved = True
latest_req, resolved, saw = _scan_key_lines(
_tail_lines(CTL_LOG, KEY_SCAN_TAIL_LINES), node)
if not saw:
# No relevant record in tail: older history may hold an
# unresolved request; fall back to a full scan.
with open(CTL_LOG, "r") as f:
latest_req, resolved, _ = _scan_key_lines(f, node)
except Exception:
return None
@@ -760,6 +849,11 @@ def inspect_node_approvals(node: str) -> dict:
if bg_tasks_count > 0 and "need review" not in purpose.lower() and "need review" not in title.lower():
purpose = f"{purpose} [{bg_tasks_count} queued task(s) awaiting review]".strip()
# P1: reject implausible targets (parser artifacts like "echo")
# before the trust check. Garbage tokens -> parser-suspect.
target_plausible = is_plausible_target(target or ip)
if target and not target_plausible:
target = None
is_trusted = is_trusted_target(target or ip, card_text)
return {
@@ -770,6 +864,7 @@ def inspect_node_approvals(node: str) -> dict:
"purpose": purpose,
"ip": ip,
"target": target or ip or "-",
"target_plausible": target_plausible,
"is_trusted": is_trusted,
"buttons": data.get("buttons", []),
"has_allow_once": data.get("has_allow_once", False),
@@ -1355,3 +1450,104 @@ def dismiss_node_task(node: str, caller: str = "box-approvals") -> dict:
"cleared_waits": clear_res.get("cleared_per_node", {}).get(node, 0),
}
# ---------------------------------------------------------------------------
# Coordinator Gating & Markdown Decision Records
# ---------------------------------------------------------------------------
DOCS_DIR = REPO_ROOT / "docs"
def parse_yaml_frontmatter(text: str) -> dict:
"""Parse YAML frontmatter delimited by ^--- from Markdown text without external dependencies."""
if not text or not text.startswith("---"):
return {}
parts = text.split("---", 2)
if len(parts) < 3:
return {}
raw_yaml = parts[1].strip()
data = {}
current_key = None
for line in raw_yaml.splitlines():
line = line.strip()
if not line or line.startswith("#"):
continue
if ":" in line:
k, v = line.split(":", 1)
k = k.strip()
v = v.strip().strip("'\"")
if v.lower() == "true":
v = True
elif v.lower() == "false":
v = False
elif v == "":
v = []
current_key = k
data[k] = v
continue
data[k] = v
current_key = k
elif line.startswith("- ") and current_key and isinstance(data.get(current_key), list):
item = line[2:].strip().strip("'\"")
data[current_key].append(item)
return data
def scan_coordinator_gates(docs_dir: Path = None) -> list:
"""Scan docs/*.md for coordinator gate decision records."""
target_dir = docs_dir or DOCS_DIR
gates = []
if not target_dir.exists():
return gates
for doc in target_dir.glob("*.md"):
try:
content = doc.read_text(encoding="utf-8")
meta = parse_yaml_frontmatter(content)
if meta.get("gate") == "coordinator" or "coordinator" in meta:
meta["doc_path"] = str(doc)
meta["doc_name"] = doc.name
meta["is_signed_off"] = meta.get("status") in ("signed-off", "accepted", "final")
gates.append(meta)
except Exception:
pass
gates.sort(key=lambda x: str(x.get("accepted_at", "")), reverse=True)
return gates
def verify_coordinator_signoff(scope: str, docs_dir: Path = None) -> dict:
"""Verify if a specific scope or target has a signed-off coordinator decision record.
Scope can match `scope` or any item in `signoff_targets`.
"""
gates = scan_coordinator_gates(docs_dir)
for g in gates:
targets = g.get("signoff_targets") or []
if not isinstance(targets, list):
targets = [targets]
if g.get("scope") == scope or scope in targets:
if g.get("is_signed_off"):
return {
"ok": True,
"scope": scope,
"status": g.get("status"),
"coordinator": g.get("coordinator"),
"accepted_at": g.get("accepted_at"),
"doc_name": g.get("doc_name"),
"doc_path": g.get("doc_path"),
}
else:
return {
"ok": False,
"scope": scope,
"status": g.get("status"),
"coordinator": g.get("coordinator"),
"doc_name": g.get("doc_name"),
"error": f"Gate for scope '{scope}' exists in {g.get('doc_name')} but status is '{g.get('status')}' (not signed-off)",
}
return {
"ok": False,
"scope": scope,
"error": f"No coordinator decision record found covering scope '{scope}' in {docs_dir or DOCS_DIR}",
}