Enterprise-grade security patterns for Claude Code — audit logging, compliance frameworks, secrets management, permission hardening, network security, and managed settings enforcement
From claude-code-expertnpx claudepluginhub markus41/claude --plugin claude-code-expertThis skill is limited to using the following tools:
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Implements structured self-debugging workflow for AI agent failures: capture errors, diagnose patterns like loops or context overflow, apply contained recoveries, and generate introspection reports.
Production-grade security architecture for Claude Code deployments with compliance, audit, and policy enforcement.
{
"permissions": {
"allow": [
"Read",
"Glob",
"Grep",
"Edit",
"Bash(npm test)",
"Bash(npm run dev)",
"Bash(git status)",
"Bash(git diff)",
"Bash(git log)"
],
"deny": [
"Bash(rm -rf *)",
"Bash(sudo *)",
"Bash(chmod 777 *)",
"Write",
"WebFetch",
"WebSearch"
]
}
}
{
"permissions": {
"allow": [
"Read",
"Glob",
"Grep",
"Bash(git log *)",
"Bash(git show *)",
"Bash(git diff *)"
],
"deny": [
"Write",
"Edit",
"Bash(git commit *)",
"Bash(git push *)",
"Bash(git rebase *)"
]
}
}
{
"permissions": {
"allow": [
"Read",
"Write",
"Edit",
"Glob",
"Grep",
"Bash(*)",
"Agent",
"WebFetch"
]
},
"hooks": {
"PostToolUse": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "bash /opt/audit/admin-audit-log.sh"
}
]
}
]
}
}
PreToolUse hook to block access to credential files:
#!/bin/bash
# name: block-credential-files.sh
# Prevent Claude from reading .env, .pem, .key, .secret files
INPUT=$(cat)
TOOL=$(echo "$INPUT" | jq -r '.tool_name' 2>/dev/null)
PATH_ARG=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.path // ""' 2>/dev/null)
# List of blocked patterns
BLOCKED_PATTERNS=(
'\.env'
'\.env\..*'
'\.pem$'
'\.key$'
'\.secret$'
'\.jks$'
'credentials\.json'
'service-account\.json'
'firebase-key\.json'
'/vault/'
'/secrets/'
'/private/'
)
# Check each pattern
for pattern in "${BLOCKED_PATTERNS[@]}"; do
if echo "$PATH_ARG" | grep -qE "$pattern"; then
echo '{
"decision": "deny",
"reason": "Access to credential files is blocked",
"blocked_pattern": "'$pattern'"
}'
exit 0
fi
done
echo '{"decision": "approve"}'
For HashiCorp Vault:
#!/bin/bash
# name: vault-credential-retrieval.sh
# Fetch credentials from Vault instead of storing locally
SECRET_NAME=$1
VAULT_ADDR=${VAULT_ADDR:-"https://vault.internal:8200"}
VAULT_TOKEN=${VAULT_TOKEN:-}
if [ -z "$VAULT_TOKEN" ]; then
echo '{"error": "VAULT_TOKEN not set"}'
exit 1
fi
curl -s \
-H "X-Vault-Token: $VAULT_TOKEN" \
"$VAULT_ADDR/v1/secret/data/$SECRET_NAME" | jq -r '.data.data'
#!/bin/bash
# name: aws-secrets-retrieval.sh
SECRET_NAME=$1
REGION=${AWS_REGION:-us-east-1}
aws secretsmanager get-secret-value \
--secret-id "$SECRET_NAME" \
--region "$REGION" \
--query 'SecretString' \
--output text 2>/dev/null
#!/bin/bash
# name: pre-commit-secret-scan.sh
# Placed in .git/hooks/pre-commit
# Patterns that suggest credentials
PATTERNS=(
'PRIVATE.*KEY'
'PASSWORD.*='
'APIKEY'
'api.key'
'secret_access_key'
'aws_secret'
'postgresql://.*:.*@'
'mongodb+srv://.*:.*@'
)
EXIT_CODE=0
for file in $(git diff --cached --name-only); do
for pattern in "${PATTERNS[@]}"; do
if git diff --cached "$file" | grep -qiE "$pattern"; then
echo "ERROR: Potential secret detected in $file (pattern: $pattern)"
EXIT_CODE=1
fi
done
done
exit $EXIT_CODE
#!/bin/bash
# name: audit-trail.sh
# PostToolUse hook: log all tool usage with context
INPUT=$(cat)
TOOL=$(echo "$INPUT" | jq -r '.tool_name' 2>/dev/null)
RESULT=$(echo "$INPUT" | jq -r '.tool_result' 2>/dev/null)
USER=${CLAUDE_USER:-"unknown"}
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
AUDIT_LOG="/var/log/claude-code/audit.log"
# Truncate output for logging (first 200 chars)
RESULT_SUMMARY=$(echo "$RESULT" | head -c 200)
INPUT_SUMMARY=$(echo "$INPUT" | jq -r '.tool_input | @json' 2>/dev/null | head -c 200)
# Append to audit log with atomic write
{
flock -x 200
echo "{
\"timestamp\": \"$TIMESTAMP\",
\"user\": \"$USER\",
\"tool\": \"$TOOL\",
\"input_summary\": \"$INPUT_SUMMARY\",
\"output_length\": $(echo "$RESULT" | wc -c),
\"status\": \"success\"
}" >> "$AUDIT_LOG"
} 200>"/var/log/claude-code/audit.log.lock"
# Also log file modifications
if [[ "$TOOL" == "Write" ]] || [[ "$TOOL" == "Edit" ]]; then
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.path // ""')
echo "{
\"timestamp\": \"$TIMESTAMP\",
\"user\": \"$USER\",
\"action\": \"file_modified\",
\"file\": \"$FILE_PATH\",
\"tool\": \"$TOOL\"
}" >> "/var/log/claude-code/file-changes.log"
fi
#!/bin/bash
# name: setup-audit-log-rotation.sh
# Configure logrotate for Claude Code audit trails
cat > /etc/logrotate.d/claude-code << 'EOF'
/var/log/claude-code/*.log {
daily
rotate 90
compress
delaycompress
missingok
notifempty
create 0600 root root
sharedscripts
postrotate
# Notify SIEM or archival system
/opt/claude-code/archive-audit-logs.sh
endscript
}
EOF
chmod 644 /etc/logrotate.d/claude-code
Control: Restrict access to systems and data.
Implementation:
Evidence Collection:
# Audit: Current permissions configuration
grep -r "permissions" ~/.claude/settings.json
# Audit: Session permission changes
grep "permission_mode" /var/log/claude-code/audit.log
# Audit: Denied tool uses
grep '"decision": "deny"' /var/log/claude-code/audit.log | wc -l
Control: Monitor activity for anomalies.
Implementation:
Evidence Collection:
# Daily activity summary
awk '$tool == "Bash(rm*)" {rm_count++} END {
print "Dangerous Bash uses: " rm_count
}' /var/log/claude-code/audit.log
# User activity timeline
grep '"user":' /var/log/claude-code/audit.log | \
jq -s 'group_by(.user) |
map({user: .[0].user, count: length})'
Control: Authorize and document changes.
Implementation:
Evidence Collection:
# Git change log with user and timestamp
git log --pretty=format:"%h %an %ad %s" --date=short
# Files modified in past 30 days
find . -mtime -30 -type f | xargs ls -lt | head -20
Protected Health Information (PHI) prevention hooks:
#!/bin/bash
# name: hipaa-phi-guard.sh
# PreToolUse: Block tools that could expose PHI
INPUT=$(cat)
TOOL=$(echo "$INPUT" | jq -r '.tool_name' 2>/dev/null)
# Reject tools that could output to insecure locations
case "$TOOL" in
WebFetch|WebSearch)
echo '{"decision": "deny", "reason": "WebFetch/WebSearch blocked in HIPAA environment"}'
exit 0
;;
Bash)
CMD=$(echo "$INPUT" | jq -r '.tool_input.command' 2>/dev/null)
# Block commands that might exfiltrate data
if echo "$CMD" | grep -qE '(curl|wget|scp|rsync|nc).*[^127\.0\.0\.1]'; then
echo '{"decision": "deny", "reason": "Network exfiltration patterns blocked"}'
exit 0
fi
;;
esac
echo '{"decision": "approve"}'
PostToolUse hook to prevent PHI in responses:
#!/bin/bash
# name: hipaa-output-sanitizer.sh
# PostToolUse: Scan outputs for PHI patterns
INPUT=$(cat)
OUTPUT=$(echo "$INPUT" | jq -r '.tool_result' 2>/dev/null)
# Common PHI patterns
PHI_PATTERNS=(
'[0-9]{3}-[0-9]{2}-[0-9]{4}' # SSN
'[0-9]{16,19}' # Credit card
'MRN[:\s]*[0-9]{6,12}' # Medical Record Number
)
for pattern in "${PHI_PATTERNS[@]}"; do
if echo "$OUTPUT" | grep -qE "$pattern"; then
echo "{
\"warning\": \"PHI pattern detected in output\",
\"pattern\": \"$pattern\",
\"action\": \"sanitize\"
}"
# Do not output the matched text
exit 1
fi
done
{
"dataRetention": {
"auditLogs": "90 days",
"fileLogs": "30 days",
"sessionLogs": "7 days",
"backups": "365 days"
},
"zeroDataRetention": true
}
#!/bin/bash
# name: gdpr-pii-detector.sh
# PreToolUse: Detect PII in inputs and flag for review
INPUT=$(cat)
# PII patterns
PII_PATTERNS=(
'name[:\s]*[A-Z][a-z]+ [A-Z]'
'email[:\s]*[a-z0-9._%+-]+@'
'phone[:\s]*\+?[0-9\s().-]{10,}'
'address[:\s]*[0-9]+ .{5,}'
)
for pattern in "${PII_PATTERNS[@]}"; do
if echo "$INPUT" | grep -qiE "$pattern"; then
echo "{
\"warning\": \"PII detected in input\",
\"pattern\": \"$pattern\",
\"user_confirmation_required\": true
}"
exit 1
fi
done
echo '{"decision": "approve"}'
{
"auth": {
"method": "apiKey",
"apiKey": "${ANTHROPIC_API_KEY}",
"organizationId": "${ANTHROPIC_ORG_ID}",
"forceLoginOrgUUID": "org-12345"
}
}
{
"auth": {
"method": "bedrock",
"region": "us-east-1",
"roleArn": "arn:aws:iam::123456789:role/claude-code-role",
"assumeRoleSessionName": "claude-code-session"
}
}
{
"auth": {
"method": "vertexAI",
"projectId": "my-gcp-project",
"location": "us-central1",
"serviceAccountPath": "/etc/gcloud/service-account.json"
}
}
export GLOBAL_AGENT_HTTP_PROXY="http://proxy.internal:3128"
export GLOBAL_AGENT_HTTPS_PROXY="https://proxy.internal:3128"
export NO_PROXY="localhost,127.0.0.1,*.internal,.local"
For deployments without internet access:
{
"auth": {
"method": "bedrock",
"vpcEndpoint": "vpce-12345678",
"region": "us-east-1"
},
"network": {
"allowedDomains": [
"bedrock.us-east-1.amazonaws.com"
],
"blockPublicInternet": true
}
}
{
"mcpServers": {
"filesystem": {
"command": "node",
"args": ["mcp-filesystem-server.js"],
"allowedPaths": ["/app", "/data"],
"allowNetworkAccess": false
},
"web": {
"allowedDomains": [
"api.internal",
"docs.internal"
]
}
}
}
Place at /Library/Application Support/ClaudeCode/CLAUDE.md and settings.json:
{
"managedSettings": {
"version": "1.0",
"enforced": true,
"permissions": {
"deny": [
"Bash(curl * | bash)",
"Bash(wget * | bash)",
"Bash(sudo *)",
"Bash(chmod 777 *)"
]
},
"model": "claude-sonnet-4-6",
"hooks": {
"PostToolUse": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "/opt/audit/enterprise-audit.sh"
}
]
}
]
},
"network": {
"proxy": "https://proxy.corp:3128"
}
}
}
Place at /etc/claude-code/CLAUDE.md and /etc/claude-code/settings.json.
Store at HKEY_LOCAL_MACHINE\Software\Policies\ClaudeCode\:
New-Item -Path "HKLM:\Software\Policies\ClaudeCode" -Force
New-ItemProperty -Path "HKLM:\Software\Policies\ClaudeCode" `
-Name "PermissionMode" -Value "plan" -PropertyType String
Ctrl+C)#!/bin/bash
# name: incident-response-credential-leak.sh
INCIDENT_ID=$(date +%s)
INCIDENT_LOG="/var/log/claude-code/incidents.log"
echo "{
\"incident_id\": \"$INCIDENT_ID\",
\"type\": \"credential_exposure\",
\"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",
\"status\": \"investigating\",
\"actions_taken\": []
}" > "/tmp/incident-$INCIDENT_ID.json"
# Rotate all API keys
echo " Revoking API keys..."
# Call credential rotation service
# Archive audit logs for forensics
echo " Archiving audit logs..."
tar czf "/var/log/claude-code/incident-$INCIDENT_ID.tar.gz" \
/var/log/claude-code/*.log
echo " Incident $INCIDENT_ID logged"
#!/bin/bash
# name: audit-forensics.sh
DATA_PATTERN="$1"
LOG_DIR="/var/log/claude-code"
echo "=== Forensic Analysis ==="
echo "Pattern: $DATA_PATTERN"
echo ""
echo "Occurrences in audit trail:"
grep -rn "$DATA_PATTERN" "$LOG_DIR" | wc -l
echo ""
echo "Timeline:"
grep -rn "$DATA_PATTERN" "$LOG_DIR" | cut -d: -f3 | jq '.timestamp'