133 lines
4.2 KiB
Python
133 lines
4.2 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Example: Applying L3-L5 Security Hooks to a Claude Code Agent.
|
|
|
|
This script shows how to configure and test each security level
|
|
from the Security Foundation skill kit.
|
|
|
|
Usage:
|
|
python example-security-hooks.py --level 3 # Test L3 blacklist
|
|
python example-security-hooks.py --level 4 # Test L4 whitelist
|
|
python example-security-hooks.py --level 5 # Test L5 no-bash
|
|
"""
|
|
|
|
import argparse
|
|
import sys
|
|
|
|
|
|
# ── L3: Blacklist Hook ────────────────────────────────────────────
|
|
|
|
BLOCKED_PATTERNS = [
|
|
r"rm\s+-rf",
|
|
r"git\s+clean\s+-fdx",
|
|
r"DROP\s+(TABLE|DATABASE)",
|
|
r"truncate\s+",
|
|
r"curl\s+-X\s+POST",
|
|
]
|
|
|
|
def l3_check(command: str) -> tuple[bool, str]:
|
|
"""Check command against blacklist. Returns (blocked, reason)."""
|
|
import re
|
|
for pattern in BLOCKED_PATTERNS:
|
|
if re.search(pattern, command, re.IGNORECASE):
|
|
return True, pattern
|
|
return False, ""
|
|
|
|
|
|
# ── L4: Whitelist Hook ────────────────────────────────────────────
|
|
|
|
ALLOWED_COMMANDS = [
|
|
r"^npm test$",
|
|
r"^git status$",
|
|
r"^uv run pytest",
|
|
r"^cat [\w/\.-]+$",
|
|
r"^pwd$",
|
|
r"^ls\b",
|
|
]
|
|
|
|
def l4_check(command: str) -> bool:
|
|
"""Check command against whitelist. Returns True if allowed."""
|
|
import re
|
|
return any(re.match(p, command) for p in ALLOWED_COMMANDS)
|
|
|
|
|
|
# ── L5: No Bash (Tool-based only) ─────────────────────────────────
|
|
|
|
def l5_execute(tool_name: str, params: dict) -> str:
|
|
"""Execute only through approved tools. No bash at all."""
|
|
allowed_tools = {
|
|
"read_file": lambda p: f"Reading file: {p['path']}",
|
|
"write_file": lambda p: f"Writing {len(p['content'])} chars to {p['path']}",
|
|
"grep_search": lambda p: f"Searching for '{p['pattern']}' in {p.get('path', '.')}",
|
|
}
|
|
if tool_name not in allowed_tools:
|
|
return f"BLOCKED: {tool_name} is not allowed at L5"
|
|
return allowed_tools[tool_name](params)
|
|
|
|
|
|
# ── Test Harness ──────────────────────────────────────────────────
|
|
|
|
def test_blacklist():
|
|
print(" L3 Blacklist Tests:")
|
|
tests = [
|
|
("rm -rf /", True),
|
|
("git status", False),
|
|
("DROP TABLE users", True),
|
|
("npm install express", False),
|
|
("git clean -fdx", True),
|
|
("pwd", False),
|
|
]
|
|
for cmd, expected in tests:
|
|
blocked, reason = l3_check(cmd)
|
|
status = "⛔ blocked" if blocked else "✓ allowed"
|
|
assert blocked == expected, f"FAIL: {cmd}"
|
|
print(f" {status} → {cmd}")
|
|
|
|
|
|
def test_whitelist():
|
|
print(" L4 Whitelist Tests:")
|
|
tests = [
|
|
("npm test", True),
|
|
("python cleanup.py", False),
|
|
("git status", True),
|
|
("rm -rf node_modules", False),
|
|
("cat config.json", True),
|
|
]
|
|
for cmd, expected in tests:
|
|
allowed = l4_check(cmd)
|
|
status = "✓ allowed" if allowed else "⛔ blocked"
|
|
assert allowed == expected, f"FAIL: {cmd}"
|
|
print(f" {status} → {cmd}")
|
|
|
|
|
|
def test_nobash():
|
|
print(" L5 No-Bash Tests:")
|
|
tests = [
|
|
("read_file", {"path": "test.txt"}, True),
|
|
("delete_file", {"path": "test.txt"}, False),
|
|
("write_file", {"path": "out.txt", "content": "hi"}, True),
|
|
]
|
|
for tool, params, expected in tests:
|
|
result = l5_execute(tool, params)
|
|
is_allowed = not result.startswith("BLOCKED")
|
|
status = "✓ allowed" if is_allowed else "⛔ blocked"
|
|
assert is_allowed == expected, f"FAIL: {tool}"
|
|
print(f" {status} → {tool}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--level", type=int, choices=[3, 4, 5], default=3)
|
|
args = parser.parse_args()
|
|
|
|
print(f"\nSecurity Foundation — Testing L{args.level}\n")
|
|
|
|
if args.level == 3:
|
|
test_blacklist()
|
|
elif args.level == 4:
|
|
test_whitelist()
|
|
elif args.level == 5:
|
|
test_nobash()
|
|
|
|
print("\n All tests passed.\n")
|