{
  "version": "act 2.0.0",
  "slug": "agent-safety-audit",
  "name": "agent-safety-audit",
  "description": "Audit whether an AI agent edit to a file is safe \u2014 composes secret-surface, taint-flow, change-impact, and API-surface analysis into an agent-edit safety report. Use before letting an agent modify a sensitive or high-blast-radius file.",
  "url": "https://act101.ai/docs/skills/agent-safety-audit",
  "body_md": "# agent-safety-audit\n\nProduce an **agent-edit safety report** for a file an autonomous agent is about\nto (or just did) modify. It answers: does this code touch secrets, carry\nuntrusted data to dangerous sinks, have a large blast radius, or expose a wide\nAPI the agent could break? Builds on the change-impact approach but its value is\nthe composition of the four named tools below.\n\n## Honesty caveat (read first)\n\nEvery dimension is AST/heuristic. `secret_surface`, `taint_flow`, and\n`unsafe`-style scans report `modeled_kinds`; an empty `modeled_kinds` for the\nfile's grammar means that dimension is **not covered** \u2014 that is \"no evidence,\"\nNOT \"all clear.\" Read `modeled_kinds` per call \u2014 per the COVERAGE LAW taint (and\nthe unsafe/secret scans) apply to **every applicable tier-1+ grammar**, so there\nis no fixed \"supported\" grammar list. A non-empty mask for a dimension means it\n**was modeled** for this grammar (an empty finding is then genuine \u2014 e.g. no\nsource\u2192sink path); an **empty/absent** mask means that dimension was **not\nmodeled** \u2014 \"no evidence,\" not \"clear,\" and a coverage gap to close. Always name\nthe language and the uncovered dimensions. Verdicts are advisory, not a security\nguarantee.\n\n## Tier\n\n**Architecture.** `taint_flow` is Architecture; the composed tools enforce their own tiers.\nIf a tool is rejected for tier, the corresponding dimension is UNAUDITED \u2014 say\nso rather than implying it passed.\n\n## Tools (in order)\n\n| Step | Tool | What it answers |\n|---|---|---|\n| 1 | `secret_surface` | Does the file touch credentials, tokens, signing keys, env-secret reads, or hardcoded secret literals? |\n| 2 | `taint_flow` | Does untrusted input reach a dangerous sink (raw SQL, eval, command exec, fs path, deserialization)? |\n| 3 | `analyze_impact` | What is the change's blast radius \u2014 which files transitively depend on the target? |\n| 4 | `analyze_surface` | How wide is the public API at this boundary the agent might break? |\n| 5 | `scan` | Repo-level AI-code threats the per-file tools miss: hardcoded credentials across the tree (pattern + entropy heuristic), `.cursorrules`/AI-config hidden-Unicode backdoors, MCP-config RCE (CVE-2025-59944), typosquat/hallucinated dependencies, GitHub Actions expression injection, LLM-output-to-exec flows, and prompt-injection surfaces. |\n\n## Workflow\n\n1. Call `secret_surface` on the file. Any `CredentialParam` / `TokenVar` /\n   `SigningKey` / `EnvSecretRead` / `HardcodedLiteral` hit means the agent is\n   editing secret-adjacent code \u2014 flag for human review.\n2. Call `taint_flow` with `target` + `file`. A source\u2192sink path is a hard flag;\n   note any unresolved tainted-arg callees on the frontier (analysis stopped\n   there, so downstream is unverified).\n3. Call `analyze_impact` with the file as `target` to size the blast radius.\n   Many transitive dependents \u2192 an agent mistake here cascades widely.\n4. Call `analyze_surface` at the file's boundary. A wide exposed API means more\n   contract the agent can silently break.\n5. Call `scan` on the repo (`root` = repo root). Any `ai_config_backdoor`,\n   `mcp_config_rce`, `llm_output_execution`, or `dependency_hallucination`\n   finding is a hard **HUMAN REVIEW** flag \u2014 these are agent-targeted\n   supply-chain / injection attacks the per-file dimensions above do not\n   cover. If the repo has a committed `.act/baseline.json`, pass\n   `baseline=\".act/baseline.json\"` \u2014 the report's `baseline` section separates\n   `new` findings from `baselined` (acknowledged) repo debt, and for an edit\n   audit the `new` partition (IDs in `baseline.new_finding_ids`) is the signal\n   that matters. Private repos require the scan entitlement; if absent, mark\n   this dimension UNAUDITED (never present it as clear).\n\n## Verdict synthesis\n\n- **SAFE-ish** \u2014 no secret surface, no taint source\u2192sink path, small blast\n  radius, narrow API, and the relevant `modeled_kinds` are non-empty for the\n  grammar.\n- **HUMAN REVIEW** \u2014 secret surface present, a confirmed taint path, large blast\n  radius, or a wide API. Name which signal fired \u2014 or any `mcp_config_rce` /\n  `ai_config_backdoor` finding from `scan`.\n- **UNAUDITED** \u2014 `taint_flow`/`secret_surface` returned empty `modeled_kinds`\n  for this grammar, or a tool was tier-blocked. State which dimension is\n  uncovered; never present UNAUDITED as SAFE.\n\n## Output\n\nA per-file safety card: secret hits, taint paths (source \u2192 sink, with frontier\nnotes), blast-radius count, API width, and the verdict. Quote `modeled_kinds`\nand the language for every uncovered dimension."
}