aws--agent-toolkit-for-aws
94 行
3.1 KiB
Python
94 行
3.1 KiB
Python
#!/usr/bin/env python3
|
|
"""PreToolUse hook: block direct secret fetching from AWS Secrets Manager.
|
|
|
|
Reads JSON from stdin, checks tool_name and tool_input, and returns
|
|
a deny decision if the call would fetch secret values directly.
|
|
|
|
Use {{resolve:secretsmanager:secret-id:SecretString:key}} with asm-exec instead.
|
|
"""
|
|
|
|
import json
|
|
import re
|
|
import sys
|
|
|
|
DENY_MSG = (
|
|
"Direct secret fetching is blocked. "
|
|
"Use {{resolve:secretsmanager:secret-id:SecretString:key}} with asm-exec instead. "
|
|
"Run /aws-secrets-manager for details."
|
|
)
|
|
|
|
SMA_PATTERN = re.compile(
|
|
r'(localhost|127\.0\.0\.1|0\.0\.0\.0|\[::1\]|::1):2773/secretsmanager/get'
|
|
)
|
|
|
|
# Match the operation regardless of casing/separators:
|
|
# GetSecretValue, get_secret_value, get-secret-value, BatchGetSecretValue, ...
|
|
GSV_PATTERN = re.compile(r'(batch[-_]?)?get[-_]?secret[-_]?value', re.I)
|
|
|
|
# Structured operation names normalized to lowercase, no separators.
|
|
GSV_OPERATIONS = ("getsecretvalue", "batchgetsecretvalue")
|
|
|
|
|
|
def _normalize_op(operation):
|
|
"""Collapse casing and -/_ separators so GetSecretValue == get-secret-value."""
|
|
return operation.lower().replace("-", "").replace("_", "")
|
|
|
|
|
|
def deny():
|
|
json.dump({
|
|
"hookSpecificOutput": {
|
|
"hookEventName": "PreToolUse",
|
|
"permissionDecision": "deny",
|
|
"permissionDecisionReason": DENY_MSG
|
|
}
|
|
}, sys.stdout)
|
|
sys.exit(0)
|
|
|
|
|
|
def allow():
|
|
sys.exit(0)
|
|
|
|
|
|
def main():
|
|
data = json.load(sys.stdin)
|
|
tool_name = data.get("tool_name", "")
|
|
tool_input = data.get("tool_input", {})
|
|
|
|
# Check structured AWS tool calls (use_aws or MCP AWS tools)
|
|
if tool_name == "use_aws" or tool_name.startswith("mcp__"):
|
|
service = (tool_input.get("service_name") or tool_input.get("service") or tool_input.get("serviceName") or "").lower()
|
|
operation = tool_input.get("operation_name") or tool_input.get("operation") or tool_input.get("operationName") or ""
|
|
if service == "secretsmanager" and _normalize_op(operation) in GSV_OPERATIONS:
|
|
deny()
|
|
# Fallback: search all string values for secret-fetching patterns
|
|
if GSV_PATTERN.search(json.dumps(tool_input)):
|
|
if "secretsmanager" in json.dumps(tool_input).lower():
|
|
deny()
|
|
# Check run_script tools for secret fetching in code
|
|
if "run_script" in tool_name:
|
|
for key, val in tool_input.items():
|
|
if isinstance(val, str) and GSV_PATTERN.search(val):
|
|
deny()
|
|
if GSV_PATTERN.search(json.dumps(tool_input)):
|
|
deny()
|
|
allow()
|
|
|
|
# Check Bash commands
|
|
if tool_name == "Bash":
|
|
command = tool_input.get("command", "")
|
|
# AWS CLI secret fetching
|
|
if re.search(r'aws\s+secretsmanager\s+(get-secret-value|batch-get-secret-value)', command, re.I):
|
|
deny()
|
|
# Direct SMA access
|
|
if SMA_PATTERN.search(command):
|
|
deny()
|
|
# boto3/SDK secret fetching in scripts
|
|
if GSV_PATTERN.search(command):
|
|
deny()
|
|
|
|
allow()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|