update first - 84

This commit is contained in:
thanhnv
2026-06-30 02:21:39 +09:00
commit 07ac1bdcdd
561 changed files with 88164 additions and 0 deletions
@@ -0,0 +1,126 @@
#!/usr/bin/env bash
set -euo pipefail
# CASAN Level 5 tool registry gate.
# Usage:
# tool-registry-gate.sh <tool-id>
TOOL_ID="${1:-}"
if [[ -z "$TOOL_ID" ]]; then
echo "Usage: tool-registry-gate.sh <tool-id>" >&2
exit 64
fi
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
REGISTRY="$PROJECT_ROOT/.specify/level5/tool-registry.yaml"
LOG_DIR="$PROJECT_ROOT/.specify/logs/level5"
AUDIT_DIR="$PROJECT_ROOT/.specify/logs/audit"
mkdir -p "$LOG_DIR" "$AUDIT_DIR"
TRACE_ID="$(uuidgen 2>/dev/null | tr '[:upper:]' '[:lower:]' || printf 'tool-%s-%s' "$(date +%s)" "$$")"
# shellcheck source=tool-audit-lib.sh
source "$SCRIPT_DIR/tool-audit-lib.sh"
AUDIT_TMP="$(mktemp)"
trap 'rm -f "$AUDIT_TMP"' EXIT
DECISION_LINE="$(python3 - "$REGISTRY" "$TOOL_ID" "${CASAN_IDEMPOTENCY_KEY:-}" "$LOG_DIR/tool-registry.jsonl" "$TRACE_ID" "${CASAN_AGENT:-}" "$AUDIT_TMP" "${CASAN_RUN_ID:-adhoc-$$}" <<'PY'
import json
import os
import re
import sys
from datetime import datetime, timezone
registry_path, tool_id, idempotency_key, log_path, trace_id, agent, audit_tmp, run_id = sys.argv[1:]
text = open(registry_path, encoding="utf-8").read()
blocks = re.split(r"\n\s*-\s+id:\s+", text)
tools = {}
for block in blocks[1:]:
lines = block.splitlines()
current_id = lines[0].strip()
attrs = {"id": current_id, "has_rollback": "strategy:" in block}
for line in lines[1:]:
if ":" in line and not line.startswith(" "):
key, value = line.split(":", 1)
attrs[key.strip()] = value.strip().strip('"')
tools[current_id] = attrs
tool = tools.get(tool_id)
decision, reason = "approved", "registered"
if not tool:
decision, reason = "denied", "unknown_tool"
side_effect = idem_required = False
owner = risk = ""
allowed = ""
else:
side_effect = tool.get("side_effect", "false") == "true"
idem_required = tool.get("idempotency_required", "false") == "true"
owner = tool.get("owner", "")
risk = tool.get("risk_level", "")
allowed = tool.get("allowed_agents", "")
allowed_list = [a.strip() for a in allowed.split(",") if a.strip()]
# 1. Per-agent least-privilege: restricted tools require an authorized caller.
if allowed_list:
if not agent:
decision, reason = "denied", "missing_agent_identity"
elif agent not in allowed_list:
decision, reason = "denied", "unauthorized_agent"
# 2. Side-effecting + idempotency-required tools must carry an idempotency key.
if decision == "approved" and side_effect and idem_required and not idempotency_key:
decision, reason = "denied", "missing_idempotency_key"
# 3. Every side-effecting tool must declare a rollback strategy.
if decision == "approved" and side_effect and not tool.get("has_rollback"):
decision, reason = "denied", "missing_rollback_strategy"
# 4. Runtime rate limit: count prior APPROVED calls for this tool in this run.
if decision == "approved" and tool.get("rate_limit_per_run", "").isdigit():
limit = int(tool["rate_limit_per_run"])
prior = 0
if os.path.exists(log_path):
for line in open(log_path, encoding="utf-8"):
try:
r = json.loads(line)
except ValueError:
continue
if (r.get("tool_id") == tool_id and r.get("run_id") == run_id
and r.get("decision") == "approved"):
prior += 1
if prior >= limit:
decision, reason = "denied", f"rate_limit_exceeded(limit={limit})"
ts = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
record = {
"timestamp": ts, "trace_id": trace_id, "harness": "L5-tool-registry",
"tool_id": tool_id, "agent": agent, "run_id": run_id, "owner": owner, "risk_level": risk,
"side_effect": side_effect, "idempotency_required": idem_required,
"idempotency_key_present": bool(idempotency_key),
"decision": decision, "reason": reason,
}
with open(log_path, "a", encoding="utf-8") as f:
f.write(json.dumps(record) + "\n")
# Tamper-evident central audit record (appended + signed by the bash caller).
with open(audit_tmp, "w", encoding="utf-8") as f:
f.write(json.dumps({
"timestamp": ts, "trace_id": trace_id, "tool": tool_id, "agent": agent,
"idempotency_key": idempotency_key, "decision": decision, "reason": reason,
"risk_level": risk, "owner": owner,
}))
print(f"{decision} {reason}")
PY
)"
DECISION="${DECISION_LINE%% *}"
REASON="${DECISION_LINE#* }"
append_tool_audit "$(cat "$AUDIT_TMP")" "$PROJECT_ROOT"
if [[ "$DECISION" == "approved" ]]; then
echo "TOOL_APPROVED tool=$TOOL_ID reason=$REASON"
else
echo "TOOL_DENIED tool=$TOOL_ID reason=$REASON" >&2
exit 2
fi