Files
Anthropic-Cybersecurity-Skills/skills/securing-agentic-ai-tool-invocation/scripts/agent.py
T
mukul975 8cae0648ec Add 55 new skills across 3 new domains + 6 undercovered areas (762 -> 817)
Demand-driven expansion targeting the fastest-growing 2025-2026 threat and
skills categories (ISC2/WEF/CrowdStrike/Mandiant signals):

- AI Security (NEW domain, 12 skills): LLM red-teaming with garak/PyRIT,
  prompt injection (direct/indirect/RAG), MCP tool-poisoning, agentic tool
  invocation, guardrails, model/data poisoning, system-prompt leakage,
  embedding/vector weaknesses, model extraction, continuous red-teaming
- Supply Chain Security (NEW domain, 5 skills): SBOMs, dependency confusion,
  malicious-npm triage, typosquatting, SLSA/Sigstore provenance
- Hardware & Firmware Security (NEW domain, 4 skills): CHIPSEC/UEFI audit,
  Secure Boot bypass, TPM measured-boot attestation, ESP bootkit hunting
- Identity (10): Entra ID/ROADtools, GraphRunner, AADInternals, ADCS/Certipy,
  shadow credentials, coercion, BloodHound CE, device-code phishing, SSO abuse
- Cloud-native (8): Stratus, Pacu, CloudFox, container escape, K8s RBAC,
  Falco, Trivy, kube-bench
- Offensive C2 (6): Sliver, Havoc, NetExec, DPAPI, NTLM relay ESC8, redirectors
- DFIR (6): Hayabusa, Chainsaw, KAPE, Velociraptor, EZ Tools, Plaso
- Backfill (4): OpenCTI, MISP, honeytokens, post-quantum crypto migration

Each skill follows the repo taxonomy (SKILL.md + references/{standards,api-reference}.md
+ scripts/agent.py + LICENSE), with researched real tool commands (no placeholders),
complete frontmatter, and ATT&CK/ATLAS + NIST CSF mappings. Updates README domain
table, skill count, and index.json.
2026-06-22 19:08:16 +02:00

150 lines
5.4 KiB
Python

#!/usr/bin/env python3
# Defensive AI-security control. Deploy on agents you own/operate.
"""Agentic AI tool-invocation policy gate.
Implements deny-by-default tool allowlisting, per-tool JSON-schema argument
validation, an allow/require_approval/deny decision, an interactive human-in-the-loop
approval gate for high-impact tools, and a structured audit log for SIEM ingestion.
Examples:
python agent.py --tool search_docs --args '{"query":"vpn policy"}'
python agent.py --tool send_email --args '{"to":"a@example.com","subject":"x","body":"y"}'
python agent.py --tool transfer_funds --args '{"amount":50}' --auto-approve
"""
import argparse
import hashlib
import json
import sys
from datetime import datetime, timezone
try:
from jsonschema import validate, ValidationError
except ImportError:
print("Install: pip install jsonschema", file=sys.stderr)
sys.exit(1)
# Impact tiers and approval requirements (deny-by-default: unknown tools rejected).
TOOL_POLICY = {
"search_docs": {"impact": "read", "approval": False},
"create_ticket": {"impact": "write", "approval": False},
"send_email": {"impact": "high", "approval": True},
"transfer_funds":{"impact": "high", "approval": True},
"run_shell": {"impact": "high", "approval": True},
}
# Per-tool argument allowlists.
TOOL_SCHEMAS = {
"search_docs": {
"type": "object",
"properties": {"query": {"type": "string", "maxLength": 500}},
"required": ["query"], "additionalProperties": False,
},
"create_ticket": {
"type": "object",
"properties": {"title": {"type": "string", "maxLength": 200},
"body": {"type": "string", "maxLength": 5000}},
"required": ["title"], "additionalProperties": False,
},
"send_email": {
"type": "object",
"properties": {
"to": {"type": "string", "pattern": r"^[^@\s]+@example\.com$"},
"subject": {"type": "string", "maxLength": 200},
"body": {"type": "string", "maxLength": 5000},
},
"required": ["to", "subject", "body"], "additionalProperties": False,
},
"transfer_funds": {
"type": "object",
"properties": {"amount": {"type": "number", "minimum": 0, "maximum": 1000},
"account": {"type": "string"}},
"required": ["amount"], "additionalProperties": False,
},
"run_shell": {
"type": "object",
"properties": {"cmd": {"type": "string", "enum": ["ls", "whoami", "df -h"]}},
"required": ["cmd"], "additionalProperties": False,
},
}
def validate_args(tool, args):
schema = TOOL_SCHEMAS.get(tool)
if schema is None:
return False, "no schema (deny-by-default)"
try:
validate(instance=args, schema=schema)
return True, "ok"
except ValidationError as exc:
return False, f"schema: {exc.message}"
def authorize(tool, args, actor):
policy = TOOL_POLICY.get(tool)
if policy is None:
return _event("deny", tool, args, actor, "tool not in allowlist")
ok, why = validate_args(tool, args)
if not ok:
return _event("deny", tool, args, actor, why)
if policy["approval"]:
return _event("require_approval", tool, args, actor,
f"high-impact ({policy['impact']})")
return _event("allow", tool, args, actor, "allowlisted")
def _event(decision, tool, args, actor, reason):
return {
"ts": datetime.now(timezone.utc).isoformat(), "actor": actor, "tool": tool,
"args_sha256": hashlib.sha256(
json.dumps(args, sort_keys=True).encode()).hexdigest(),
"decision": decision, "reason": reason, "atlas": "AML.T0053",
}
def hitl_prompt(event, auto_approve):
"""Fail-closed human-in-the-loop gate."""
if auto_approve:
return True
if not sys.stdin.isatty():
return False # no interactive approver -> deny
ans = input(f"APPROVAL: call {event['tool']} "
f"(args {event['args_sha256'][:12]})? [y/N] ").strip().lower()
return ans == "y"
def main():
ap = argparse.ArgumentParser(description="Agentic AI tool-invocation policy gate")
ap.add_argument("--tool", required=True, help="Tool the agent wants to call")
ap.add_argument("--args", default="{}", help="JSON tool arguments")
ap.add_argument("--actor", default="agent-session", help="Acting user/session id")
ap.add_argument("--auto-approve", action="store_true",
help="Auto-approve HITL (testing only)")
ap.add_argument("--audit-log", help="Append audit events to this JSONL file")
args = ap.parse_args()
try:
tool_args = json.loads(args.args)
if not isinstance(tool_args, dict):
raise ValueError("args must be a JSON object")
except (json.JSONDecodeError, ValueError) as exc:
print(f"[!] Invalid --args: {exc}", file=sys.stderr)
sys.exit(2)
event = authorize(args.tool, tool_args, args.actor)
if event["decision"] == "require_approval":
approved = hitl_prompt(event, args.auto_approve)
event["decision"] = "allow" if approved else "deny"
event["reason"] += "; approved" if approved else "; not approved (fail-closed)"
print(json.dumps(event, indent=2))
if args.audit_log:
with open(args.audit_log, "a", encoding="utf-8") as fh:
fh.write(json.dumps(event) + "\n")
sys.exit(0 if event["decision"] == "allow" else 1)
if __name__ == "__main__":
main()