audit-code install-tools.sh:
- Buffer the opengrep release JSON before grep -m1; curl died with (23)
under pipefail when grep quit early.
- Use ${m}: in the PowerShell block; $m: parsed as a scope-qualified var.
- On Arch, skip paru/yay when pacman -Q shows every package installed,
since --needed still invokes sudo.
- Add --check-only (fast, installs nothing, non-zero naming missing tools)
and --user-only (no system package managers, no sudo).
log-run.py (both skills): put the skill dir on sys.path so running it as
a script from any cwd no longer raises ModuleNotFoundError.
audit-terraform: move deps from requirements.txt into pyproject
dependency groups and add scripts/install-tools.sh (uv sync --group tools,
then check trivy, tflint, tofu, terragrunt, gh).
Both SKILL.md files gain a 0.5 Preflight step and call scripts through
uv run --project ${SKILL_DIR}. tools_unavailable is now a map of tool to
exact install command; audit-terraform skips trivy when absent and stops
with an install hint instead of crashing when tofu/terragrunt is missing.
54 lines
1.6 KiB
Python
54 lines
1.6 KiB
Python
"""Append-only JSONL telemetry for audit-terraform runs."""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from datetime import datetime, timezone, UTC
|
|
from pathlib import Path
|
|
|
|
|
|
def _now() -> str:
|
|
return datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
|
|
|
|
def _append(log_path: Path, record: dict) -> None:
|
|
log_path.parent.mkdir(parents=True, exist_ok=True)
|
|
with log_path.open("a", encoding="utf-8") as f:
|
|
f.write(json.dumps(record) + "\n")
|
|
|
|
|
|
def append_subagent_run(
|
|
log_path: Path, *, run_id: str, repo: str, mode: str, agent: str,
|
|
model: str, input_tokens: int, output_tokens: int,
|
|
duration_ms: int, finding_count: int,
|
|
) -> None:
|
|
_append(log_path, {
|
|
"kind": "subagent_run", "ts": _now(),
|
|
"run_id": run_id, "repo": repo, "mode": mode, "agent": agent,
|
|
"model": model,
|
|
"input_tokens": input_tokens, "output_tokens": output_tokens,
|
|
"duration_ms": duration_ms, "finding_count": finding_count,
|
|
})
|
|
|
|
|
|
def append_verdict(
|
|
log_path: Path, *, run_id: str, agent: str, rule_id: str,
|
|
file: str, line: int, verdict: str, notes: str = "",
|
|
) -> None:
|
|
if verdict not in {"kept", "dismissed", "false_positive"}:
|
|
raise ValueError(f"invalid verdict: {verdict!r}")
|
|
_append(log_path, {
|
|
"kind": "verdict", "ts": _now(),
|
|
"run_id": run_id, "agent": agent, "rule_id": rule_id,
|
|
"file": file, "line": line, "verdict": verdict, "notes": notes,
|
|
})
|
|
|
|
|
|
def read_runs(log_path: Path) -> list[dict]:
|
|
if not log_path.exists():
|
|
return []
|
|
return [
|
|
json.loads(line)
|
|
for line in log_path.read_text(encoding="utf-8").splitlines()
|
|
if line.strip()
|
|
]
|