Securing Claude Code for Windows Enterprise Deployments: A Comprehensive Security Framework
Securing Claude Code for Windows Enterprise Deployments: A Comprehensive Security Framework
A Complete Guide to Enterprise-Grade Security Controls, Managed Policies, and Zero-Trust Architecture for Claude Code on Windows
Executive Summary
As enterprises increasingly adopt AI-powered development tools like Claude Code, the security implications of granting AI assistants access to codebases, credentials, and corporate infrastructure have become critical concerns. This guide provides a comprehensive security framework specifically designed for Windows enterprise environments, covering installation hardening, configuration management, hook-based access controls, and compliance monitoring.
Key Security Challenges Addressed:
- Preventing AI access to sensitive files (.env, credentials, certificates)
- Blocking modifications to Windows system directories
- Deploying immutable, centrally-managed security policies
- Implementing zero-trust access controls via hooks
- Ensuring compliance with SOC2, GDPR, and industry regulations
- Protecting against prompt injection and data exfiltration
Target Audience: Enterprise Security Architects, IT Administrators, DevSecOps Engineers, Compliance Officers
1. Threat Model & Risk Assessment
1.1 Understanding the Attack Surface
Claude Code operates as an AI-powered CLI tool with significant system access:
Read Permissions (Default):
- Can read any file accessible to the user account
- Accesses system libraries and dependencies outside project scope
- Reads configuration files across the filesystem
Write Permissions (Configurable):
- By default, limited to project starting folder and subfolders
- Can be configured to write to additional directories
- Executes bash commands with user privileges
Network Access:
- Communicates with Anthropic API endpoints
- Can fetch web content (with restrictions)
- Supports proxy configurations for corporate networks
1.2 Key Threat Vectors
1. Credential Exfiltration
- Risk: AI reads
.env
,.aws/credentials
, SSH keys, certificates - Impact: Unauthorized access to cloud resources, databases, APIs
- Likelihood: HIGH without proper controls
2. Sensitive Data Leakage
- Risk: AI includes proprietary code, trade secrets in prompts sent to Anthropic
- Impact: Intellectual property theft, competitive disadvantage
- Likelihood: MEDIUM (Anthropic has data usage policies, but risk remains)
3. System Modification
- Risk: AI modifies system files, registry, critical configurations
- Impact: System instability, privilege escalation, persistence mechanisms
- Likelihood: LOW with default settings, HIGH if permissions loosened
4. Prompt Injection Attacks
- Risk: Malicious code in repository tricks AI into executing harmful commands
- Impact: Arbitrary code execution, data destruction, lateral movement
- Likelihood: MEDIUM (Claude has built-in protections, but not foolproof)
5. Supply Chain Attacks
- Risk: AI modifies dependencies, package files, build scripts
- Impact: Backdoored software, compromised builds
- Likelihood: MEDIUM without proper hooks validation
1.3 Compliance Requirements
SOC 2 Type II:
- Audit trails for all AI operations
- Access controls and permission reviews
- Data encryption in transit and at rest
- Incident response procedures
GDPR/CCPA:
- Personal data handling restrictions
- Data minimization in AI prompts
- Right to deletion compliance
- Cross-border data transfer controls
HIPAA (Healthcare):
- PHI protection mechanisms
- Business Associate Agreements (BAAs)
- Encryption and audit logging
- De-identification requirements
Industry-Specific:
- PCI-DSS for payment card data
- FINRA/SEC for financial services
- FedRAMP for government contractors
- ISO 27001 for international operations
1.4 Risk Severity Matrix
Threat Vector | Likelihood | Impact | Risk Level | Mitigation Priority |
---|---|---|---|---|
Credential Exfiltration | High | Critical | CRITICAL | IMMEDIATE |
System File Modification | Low | Critical | HIGH | HIGH |
Sensitive Data Leakage | Medium | High | HIGH | HIGH |
Prompt Injection | Medium | High | HIGH | MEDIUM |
Supply Chain Compromise | Medium | High | HIGH | MEDIUM |
Network Data Exfiltration | Low | Medium | MEDIUM | MEDIUM |
2. Secure Installation Strategy
2.1 The npm Installation Challenge on Windows
Problem: In enterprise Windows environments, default npm global installation paths create security conflicts:
Default npm Global Path:
C:\Users\<username>\AppData\Roaming\npm
Enterprise Security Issues:
- AppData Execution Blocking: Many enterprises block code execution from
AppData
to prevent ransomware - User-Specific Installation: Not truly "global" - each user gets separate installation
- Folder Redirection: Domain environments redirect
AppData
to network shares, causing performance issues - Permission Conflicts: UAC and folder virtualization interfere with installation
2.2 Solution: Enterprise-Controlled Installation Path
Recommended Approach: Install Claude Code in a centrally-managed, non-writable location.
Option 1: ProgramData Installation (Recommended)
# Step 1: Configure npm to use ProgramData for global packages
npm config set prefix "C:\ProgramData\ClaudeCode\npm-global" --global
# Step 2: Create directory structure with proper permissions
New-Item -ItemType Directory -Force -Path "C:\ProgramData\ClaudeCode\npm-global"
New-Item -ItemType Directory -Force -Path "C:\ProgramData\ClaudeCode\managed-policies"
# Step 3: Set NTFS permissions (Admins write, Users read+execute)
icacls "C:\ProgramData\ClaudeCode" /grant "Administrators:(OI)(CI)F" /grant "Users:(OI)(CI)RX" /T
# Step 4: Add to system PATH (requires admin)
[Environment]::SetEnvironmentVariable(
"Path",
$env:Path + ";C:\ProgramData\ClaudeCode\npm-global",
"Machine"
)
# Step 5: Install Claude Code
npm install -g @anthropic-ai/claude-code
Benefits:
- ✅ Centralized installation (single source of truth)
- ✅ Not blocked by AppData execution policies
- ✅ Works with domain folder redirection
- ✅ Users can execute but not modify installation
- ✅ Compatible with AppLocker/WDAC policies
Option 2: Program Files Installation
# Configure npm prefix to Program Files
npm config set prefix "C:\Program Files\ClaudeCode" --global
# Install with elevated permissions
Start-Process powershell -Verb RunAs -ArgumentList "-Command npm install -g @anthropic-ai/claude-code"
# Note: UAC virtualization may interfere - ProgramData preferred
Option 3: Native Binary Installation (Beta)
# Download and execute install script with controlled path
$installPath = "C:\ProgramData\ClaudeCode\bin"
$env:CLAUDE_INSTALL_DIR = $installPath
# Run installer (adapt for Windows)
# Note: As of 2025, native installer primarily targets Unix-like systems
# For Windows, npm installation remains primary method
2.3 AppLocker/WDAC Integration
For environments using Microsoft Defender Application Control (WDAC) or AppLocker:
Step 1: Create AppLocker Rule
<RuleCollection Type="Exe">
<FilePathRule Id="claude-code-allow" Name="Claude Code Allowed Path"
Description="Allow Claude Code from ProgramData"
UserOrGroupSid="S-1-1-0" Action="Allow">
<Conditions>
<FilePathCondition Path="C:\ProgramData\ClaudeCode\npm-global\claude.cmd"/>
</Conditions>
</FilePathRule>
</RuleCollection>
Step 2: WDAC Policy XML
<FileRules>
<Allow ID="ID_ALLOW_CLAUDE"
FriendlyName="Claude Code Executable"
FileName="node.exe"
FilePath="C:\ProgramData\ClaudeCode\npm-global\*"/>
</FileRules>
Step 3: Deploy via Group Policy
# Export WDAC policy to binary
ConvertFrom-CIPolicy -XmlFilePath .\ClaudeCodePolicy.xml -BinaryFilePath .\ClaudeCodePolicy.bin
# Deploy via GPO
Copy-Item .\ClaudeCodePolicy.bin -Destination "\\domain\SYSVOL\domain\Policies\{GPO-ID}\Machine\AppLocker\"
2.4 Deployment Script for Enterprise Rollout
<#
.SYNOPSIS
Enterprise deployment script for Claude Code on Windows
.DESCRIPTION
Installs Claude Code in ProgramData with proper permissions and managed policies
.NOTES
Requires: Administrator privileges, npm installed
#>
[CmdletBinding()]
param(
[string]$InstallPath = "C:\ProgramData\ClaudeCode",
[string]$ManagedPolicySource = "\\fileserver\IT\ClaudeCode\managed-settings.json"
)
# Check admin privileges
if (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
Write-Error "This script requires Administrator privileges"
exit 1
}
Write-Host "Installing Claude Code for Enterprise..." -ForegroundColor Green
# Step 1: Create directory structure
$paths = @(
"$InstallPath\npm-global",
"$InstallPath\managed-policies"
)
foreach ($path in $paths) {
if (-not (Test-Path $path)) {
New-Item -ItemType Directory -Force -Path $path | Out-Null
Write-Host "Created: $path" -ForegroundColor Cyan
}
}
# Step 2: Configure npm prefix
npm config set prefix "$InstallPath\npm-global" --global
Write-Host "Configured npm global prefix" -ForegroundColor Cyan
# Step 3: Set NTFS permissions
# Admins: Full Control (recursive)
# Users: Read & Execute (recursive)
icacls $InstallPath /grant "BUILTIN\Administrators:(OI)(CI)F" /T | Out-Null
icacls $InstallPath /grant "BUILTIN\Users:(OI)(CI)RX" /T | Out-Null
Write-Host "Configured NTFS permissions" -ForegroundColor Cyan
# Step 4: Add to system PATH
$currentPath = [Environment]::GetEnvironmentVariable("Path", "Machine")
if ($currentPath -notlike "*$InstallPath\npm-global*") {
[Environment]::SetEnvironmentVariable(
"Path",
"$currentPath;$InstallPath\npm-global",
"Machine"
)
Write-Host "Added to system PATH" -ForegroundColor Cyan
}
# Step 5: Install Claude Code
Write-Host "Installing @anthropic-ai/claude-code..." -ForegroundColor Cyan
npm install -g @anthropic-ai/claude-code --quiet
# Step 6: Deploy managed policies
if (Test-Path $ManagedPolicySource) {
Copy-Item $ManagedPolicySource -Destination "$InstallPath\managed-policies\managed-settings.json" -Force
# Make managed policies read-only
$policyFile = "$InstallPath\managed-policies\managed-settings.json"
Set-ItemProperty -Path $policyFile -Name IsReadOnly -Value $true
icacls $policyFile /inheritance:r /grant "BUILTIN\Administrators:(F)" /grant "BUILTIN\Users:(R)" | Out-Null
Write-Host "Deployed managed security policies" -ForegroundColor Cyan
}
# Step 7: Verify installation
$claudeVersion = claude --version 2>$null
if ($LASTEXITCODE -eq 0) {
Write-Host "`nInstallation successful!" -ForegroundColor Green
Write-Host "Claude Code version: $claudeVersion" -ForegroundColor Cyan
Write-Host "Installation path: $InstallPath" -ForegroundColor Cyan
} else {
Write-Error "Installation verification failed"
exit 1
}
# Step 8: Display next steps
Write-Host "`nNext Steps:" -ForegroundColor Yellow
Write-Host "1. Review managed policies at: $InstallPath\managed-policies\managed-settings.json"
Write-Host "2. Configure project-specific settings in .claude/settings.json"
Write-Host "3. Implement security hooks for sensitive file protection"
Write-Host "4. Test with: claude --help"
2.5 Avoiding Common Pitfalls
Issue | Problem | Solution |
---|---|---|
AppData Blocked | Enterprise security blocks AppData execution | Use ProgramData path instead |
Network AppData | Folder redirection causes slow performance | Install locally in ProgramData |
UAC Virtualization | Program Files writes get virtualized | Use ProgramData, not Program Files |
Per-User Install | Each user gets separate installation | Use system-wide ProgramData installation |
Path Issues | claude.cmd not found | Add to system PATH, not user PATH |
npm Prefix Conflicts | Global npmrc vs user npmrc | Set at system level with --global flag |
3. Configuration Hierarchy & Managed Policies
3.1 Understanding Settings Precedence
Claude Code uses a hierarchical configuration system with higher priority overriding lower priority:
1. HIGHEST: Enterprise Managed Policies (C:\ProgramData\ClaudeCode\managed-settings.json)
↓
2. Command-line arguments (--permissions, --model, etc.)
↓
3. Local project settings (.claude/settings.local.json)
↓
4. Shared project settings (.claude/settings.json)
↓
5. LOWEST: User settings (~/.config/claude/settings.json or %APPDATA%)
Key Principle: Managed policies CANNOT be overridden by users or project configurations.
3.2 Deploying Managed Policies
Location on Windows:
C:\ProgramData\ClaudeCode\managed-settings.json (Settings)
C:\ProgramData\ClaudeCode\managed-mcp.json (MCP Servers)
Enterprise Managed Settings Example:
{
"$schema": "https://api.claude.com/schemas/settings-v1.json",
"model": "claude-sonnet-4-5",
"permissions": {
"defaultMode": "plan",
"deny": [
{
"tool": "Edit",
"matcher": "**/.env*"
},
{
"tool": "Edit",
"matcher": "**/*.key"
},
{
"tool": "Edit",
"matcher": "**/*.pem"
},
{
"tool": "Edit",
"matcher": "**/credentials*"
},
{
"tool": "Read",
"matcher": "C:/Windows/**"
},
{
"tool": "Read",
"matcher": "C:/Program Files/**"
},
{
"tool": "Read",
"matcher": "**/node_modules/**"
},
{
"tool": "Bash",
"matcher": "**/rm *"
},
{
"tool": "Bash",
"matcher": "**/del *"
},
{
"tool": "Bash",
"matcher": "**/format *"
}
],
"ask": [
{
"tool": "Edit",
"matcher": "**/*.json"
},
{
"tool": "Edit",
"matcher": "**/*.yaml"
},
{
"tool": "Bash",
"matcher": "**"
}
],
"allow": [
{
"tool": "Read",
"matcher": "**/*.md"
},
{
"tool": "Read",
"matcher": "**/*.js"
},
{
"tool": "Read",
"matcher": "**/*.ts"
}
],
"additionalDirectories": []
},
"hooks": {
"PreToolUse": [
{
"matcher": "Edit:**",
"hooks": [
{
"type": "command",
"command": "powershell -File C:\\ProgramData\\ClaudeCode\\hooks\\validate-edit.ps1"
}
]
},
{
"matcher": "Bash:**",
"hooks": [
{
"type": "command",
"command": "powershell -File C:\\ProgramData\\ClaudeCode\\hooks\\validate-bash.ps1"
}
]
}
],
"PostToolUse": [
{
"matcher": "**",
"hooks": [
{
"type": "command",
"command": "powershell -File C:\\ProgramData\\ClaudeCode\\hooks\\audit-log.ps1"
}
]
}
]
},
"envVars": {
"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "true",
"NODE_EXTRA_CA_CERTS": "C:\\ProgramData\\ClaudeCode\\certs\\corporate-ca.crt",
"HTTP_PROXY": "http://proxy.corp.example.com:8080",
"NO_PROXY": "localhost,127.0.0.1,.corp.example.com"
}
}
3.3 Making Managed Policies Immutable
PowerShell Script to Deploy and Lock:
# Deploy managed policies with read-only protection
$managedPolicyPath = "C:\ProgramData\ClaudeCode\managed-settings.json"
$policyContent = Get-Content "\\fileserver\IT\ClaudeCode\managed-settings.json" -Raw
# Write policy file
Set-Content -Path $managedPolicyPath -Value $policyContent -Force
# Set read-only attribute
Set-ItemProperty -Path $managedPolicyPath -Name IsReadOnly -Value $true
# Remove inheritance and set explicit permissions
icacls $managedPolicyPath /inheritance:r
icacls $managedPolicyPath /grant "BUILTIN\Administrators:(F)" # Full control for admins
icacls $managedPolicyPath /grant "BUILTIN\Users:(R)" # Read-only for users
icacls $managedPolicyPath /deny "BUILTIN\Users:(W,D,WD)" # Explicitly deny write/delete
Write-Host "Managed policy deployed and locked" -ForegroundColor Green
3.4 Group Policy Deployment
Option 1: GPO File Deployment
# Create GPO for Claude Code settings distribution
$gpoName = "Claude Code Enterprise Settings"
$gpo = New-GPO -Name $gpoName
# Configure file deployment via GPP (Group Policy Preferences)
# Path: Computer Configuration > Preferences > Windows Settings > Files
# Set source file
$sourceFile = "\\domain\SYSVOL\domain\ClaudeCode\managed-settings.json"
$targetPath = "C:\ProgramData\ClaudeCode\managed-settings.json"
# Apply: Replace if exists, Run once
Option 2: Logon Script Deployment
# In GPO > Computer Configuration > Windows Settings > Scripts > Startup
# deploy-claude-settings.ps1
$source = "\\fileserver\IT\ClaudeCode\managed-settings.json"
$dest = "C:\ProgramData\ClaudeCode\managed-settings.json"
if (Test-Path $source) {
Copy-Item $source $dest -Force
Set-ItemProperty -Path $dest -Name IsReadOnly -Value $true
}
3.5 Managed MCP Server Configuration
C:\ProgramData\ClaudeCode\managed-mcp.json:
{
"mcpServers": {
"corporate-knowledge": {
"command": "node",
"args": ["C:\\ProgramData\\ClaudeCode\\mcp-servers\\corporate-kb\\index.js"],
"env": {
"KB_DATABASE_URL": "https://kb.corp.example.com/api",
"KB_API_KEY": "${CORPORATE_KB_API_KEY}"
},
"disabled": false
},
"compliance-checker": {
"command": "python",
"args": ["C:\\ProgramData\\ClaudeCode\\mcp-servers\\compliance\\server.py"],
"env": {
"COMPLIANCE_RULES": "C:\\ProgramData\\ClaudeCode\\compliance\\rules.json"
},
"disabled": false
}
}
}
Security Considerations for MCP Servers:
- ✅ Store MCP server code in protected ProgramData directory
- ✅ Use environment variables for sensitive credentials (not hardcoded)
- ✅ Validate MCP server inputs to prevent injection attacks
- ✅ Log all MCP server interactions for audit trails
- ✅ Restrict MCP server network access via firewall rules
3.6 Configuration Validation Script
<#
.SYNOPSIS
Validates Claude Code configuration security
#>
function Test-ClaudeCodeSecurity {
$issues = @()
# Check 1: Managed policy exists and is read-only
$managedPolicy = "C:\ProgramData\ClaudeCode\managed-settings.json"
if (-not (Test-Path $managedPolicy)) {
$issues += "ERROR: Managed policy not found at $managedPolicy"
} else {
$isReadOnly = (Get-ItemProperty $managedPolicy).IsReadOnly
if (-not $isReadOnly) {
$issues += "WARNING: Managed policy is not read-only"
}
}
# Check 2: Installation path is not in AppData
$npmPrefix = npm config get prefix --global
if ($npmPrefix -like "*AppData*") {
$issues += "ERROR: npm prefix is in AppData ($npmPrefix) - should be in ProgramData"
}
# Check 3: Hooks directory exists
$hooksDir = "C:\ProgramData\ClaudeCode\hooks"
if (-not (Test-Path $hooksDir)) {
$issues += "WARNING: Hooks directory not found at $hooksDir"
}
# Check 4: Sensitive file protections in place
$managedConfig = Get-Content $managedPolicy -Raw | ConvertFrom-Json
$hasSensitiveFileDeny = $managedConfig.permissions.deny | Where-Object {
$_.matcher -like "*/.env*" -or $_.matcher -like "**/*.key"
}
if (-not $hasSensitiveFileDeny) {
$issues += "ERROR: No sensitive file deny rules found in managed policy"
}
# Check 5: Verify NTFS permissions
$acl = Get-Acl "C:\ProgramData\ClaudeCode"
$usersCanWrite = $acl.Access | Where-Object {
$_.IdentityReference -like "*Users*" -and $_.FileSystemRights -like "*Write*"
}
if ($usersCanWrite) {
$issues += "ERROR: Users have write access to ClaudeCode directory"
}
# Report results
if ($issues.Count -eq 0) {
Write-Host "✓ All security checks passed" -ForegroundColor Green
return $true
} else {
Write-Host "✗ Security issues found:" -ForegroundColor Red
$issues | ForEach-Object { Write-Host " $_" -ForegroundColor Yellow }
return $false
}
}
# Run validation
Test-ClaudeCodeSecurity
4. Hooks-Based Security Framework
4.1 Understanding Claude Code Hooks
Hooks are the primary enforcement mechanism for custom security policies. They execute shell commands at specific points in Claude's workflow:
Hook Types:
- PreToolUse: Executes BEFORE a tool is used (can block operations)
- PostToolUse: Executes AFTER a tool completes (for logging/notification)
- UserPromptSubmit: Executes when user submits a prompt
- SessionStart: Executes when Claude session begins
- SessionEnd: Executes when Claude session ends
- Notification: Executes when Claude needs user input
Control Mechanisms:
- Exit Code Method (Simple):
- Exit code
0
: Allow operation - Exit code
2
: BLOCK operation (critical for security) - Other codes: Log error but allow
- JSON Output Method (Advanced):
{
"continue": false,
"stopReason": "Blocked: Attempting to access sensitive file",
"suppressOutput": true,
"systemMessage": "Security policy violation detected"
}
4.2 Core Security Hooks Architecture
Directory Structure:
C:\ProgramData\ClaudeCode\hooks\
├── validate-edit.ps1 # Pre-edit validation
├── validate-bash.ps1 # Bash command validation
├── validate-read.ps1 # Read operation validation
├── audit-log.ps1 # Post-operation audit logging
├── sensitive-files.json # Sensitive file patterns database
└── blocked-directories.json # Blocked directory list
4.3 Sensitive File Protection Hook
validate-edit.ps1:
<#
.SYNOPSIS
PreToolUse hook to block edits to sensitive files
.DESCRIPTION
Validates Edit tool usage against sensitive file patterns
Exits with code 2 to BLOCK the operation if sensitive file detected
#>
param(
[Parameter(Mandatory=$false)]
[string]$CLAUDE_HOOK_INPUT
)
# Parse hook input JSON
$input = $CLAUDE_HOOK_INPUT | ConvertFrom-Json
# Extract file path from tool parameters
$filePath = $input.parameters.file_path
if (-not $filePath) {
# No file path provided, allow operation
exit 0
}
# Normalize path for comparison
$normalizedPath = $filePath -replace '/', '\'
$fileName = Split-Path $filePath -Leaf
# Load sensitive file patterns
$patternsFile = "C:\ProgramData\ClaudeCode\hooks\sensitive-files.json"
if (Test-Path $patternsFile) {
$patterns = Get-Content $patternsFile -Raw | ConvertFrom-Json
} else {
# Fallback patterns if file not found
$patterns = @{
"extensions" = @("*.env", "*.key", "*.pem", "*.pfx", "*.p12", "*.jks", "*.keystore", "*.credentials")
"filenames" = @("credentials.json", "secrets.json", ".env", ".env.local", ".env.production", "id_rsa", "id_dsa")
"paths" = @("**/.ssh/*", "**/.aws/*", "**/.gcp/*", "**/credentials/*")
}
}
# Check file extensions
foreach ($ext in $patterns.extensions) {
if ($fileName -like $ext) {
# BLOCK: Sensitive file extension detected
$blockMessage = @{
continue = $false
stopReason = "SECURITY BLOCK: Cannot edit sensitive file with extension: $ext"
suppressOutput = $false
} | ConvertTo-Json -Compress
Write-Output $blockMessage
exit 2 # Exit code 2 = BLOCK
}
}
# Check exact filenames
if ($patterns.filenames -contains $fileName) {
$blockMessage = @{
continue = $false
stopReason = "SECURITY BLOCK: Cannot edit protected file: $fileName"
suppressOutput = $false
} | ConvertTo-Json -Compress
Write-Output $blockMessage
exit 2
}
# Check path patterns (simplified glob matching)
foreach ($pathPattern in $patterns.paths) {
# Convert glob pattern to regex
$regexPattern = $pathPattern -replace '\*\*', '.*' -replace '\*', '[^\\]*' -replace '/', '\\'
if ($normalizedPath -match $regexPattern) {
$blockMessage = @{
continue = $false
stopReason = "SECURITY BLOCK: Path matches protected pattern: $pathPattern"
suppressOutput = $false
} | ConvertTo-Json -Compress
Write-Output $blockMessage
exit 2
}
}
# Additional check: Windows system directories
$systemPaths = @(
"C:\Windows",
"C:\Windows\System32",
"C:\Windows\SysWOW64",
"C:\Program Files",
"C:\Program Files (x86)",
"C:\ProgramData\ClaudeCode" # Protect our own installation
)
foreach ($sysPath in $systemPaths) {
if ($normalizedPath -like "$sysPath\*") {
$blockMessage = @{
continue = $false
stopReason = "SECURITY BLOCK: Cannot edit Windows system directory: $sysPath"
suppressOutput = $false
} | ConvertTo-Json -Compress
Write-Output $blockMessage
exit 2
}
}
# Passed all checks - allow operation
exit 0
4.4 Bash Command Validation Hook
validate-bash.ps1:
<#
.SYNOPSIS
PreToolUse hook to validate Bash commands
.DESCRIPTION
Blocks dangerous bash commands and validates against security policy
#>
param(
[Parameter(Mandatory=$false)]
[string]$CLAUDE_HOOK_INPUT
)
# Parse hook input
$input = $CLAUDE_HOOK_INPUT | ConvertFrom-Json
$command = $input.parameters.command
if (-not $command) {
exit 0
}
# Dangerous command patterns (case-insensitive)
$dangerousPatterns = @(
# Destructive commands
'rm\s+-rf',
'del\s+/[fqs]',
'format\s+',
'diskpart',
# System modification
'reg\s+(add|delete)',
'sc\s+(config|delete)',
'net\s+user',
'net\s+localgroup',
# Credential access
'cmdkey',
'vaultcmd',
'Get-Credential',
# Network exfiltration
'curl\s+.*\s+-d',
'wget\s+.*--post',
'Invoke-WebRequest.*-Method\s+Post',
# Encoding/obfuscation
'[System.Convert]::FromBase64String',
'iex\s+\(',
'Invoke-Expression',
# File operations on sensitive paths
'copy.*credentials',
'copy.*\.env',
'type.*\.key',
'cat.*id_rsa'
)
foreach ($pattern in $dangerousPatterns) {
if ($command -match $pattern) {
$blockMessage = @{
continue = $false
stopReason = "SECURITY BLOCK: Dangerous command pattern detected: $pattern"
suppressOutput = $false
systemMessage = "Command blocked by security policy. Contact IT security if this is a legitimate operation."
} | ConvertTo-Json -Compress
Write-Output $blockMessage
exit 2
}
}
# Check for access to Windows system directories
$systemDirPatterns = @(
'C:\\Windows',
'C:\\Program Files',
'System32',
'SysWOW64'
)
foreach ($dirPattern in $systemDirPatterns) {
if ($command -match $dirPattern) {
# Log warning but allow (may be legitimate)
Write-Warning "Bash command accesses system directory: $dirPattern"
# Could be changed to exit 2 to block system access entirely
}
}
# Passed validation
exit 0
4.5 Comprehensive Audit Logging Hook
audit-log.ps1:
<#
.SYNOPSIS
PostToolUse hook for comprehensive audit logging
.DESCRIPTION
Logs all Claude operations to centralized audit trail
#>
param(
[Parameter(Mandatory=$false)]
[string]$CLAUDE_HOOK_INPUT
)
# Parse input
$input = $CLAUDE_HOOK_INPUT | ConvertFrom-Json
# Audit log configuration
$auditLogPath = "C:\ProgramData\ClaudeCode\logs\audit.jsonl" # JSON Lines format
$maxLogSizeMB = 100
# Create log directory if not exists
$logDir = Split-Path $auditLogPath -Parent
if (-not (Test-Path $logDir)) {
New-Item -ItemType Directory -Force -Path $logDir | Out-Null
}
# Rotate log if too large
if (Test-Path $auditLogPath) {
$logSize = (Get-Item $auditLogPath).Length / 1MB
if ($logSize -gt $maxLogSizeMB) {
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$archivePath = "$logDir\audit_$timestamp.jsonl"
Move-Item $auditLogPath $archivePath
# Optionally compress old logs
Compress-Archive -Path $archivePath -DestinationPath "$archivePath.zip"
Remove-Item $archivePath
}
}
# Build audit entry
$auditEntry = @{
timestamp = (Get-Date).ToUniversalTime().ToString("o")
user = $env:USERNAME
computer = $env:COMPUTERNAME
project_dir = $env:CLAUDE_PROJECT_DIR
tool = $input.tool
parameters = $input.parameters
result = $input.result # Available in PostToolUse hooks
session_id = $env:CLAUDE_SESSION_ID # If available
}
# Add to audit log (JSON Lines format - one JSON object per line)
$auditJson = $auditEntry | ConvertTo-Json -Compress
Add-Content -Path $auditLogPath -Value $auditJson
# Optionally forward to SIEM
$siemEnabled = $true
$siemEndpoint = "https://siem.corp.example.com/api/events"
if ($siemEnabled) {
try {
Invoke-RestMethod -Uri $siemEndpoint -Method Post -Body $auditJson -ContentType "application/json" -TimeoutSec 5
} catch {
# Log SIEM forwarding failure but don't block operation
Write-Warning "Failed to forward audit log to SIEM: $_"
}
}
# Always allow (PostToolUse hook for logging only)
exit 0
4.6 Sensitive Files Database
sensitive-files.json:
{
"extensions": [
"*.env",
"*.env.*",
"*.key",
"*.pem",
"*.pfx",
"*.p12",
"*.p7b",
"*.p7s",
"*.der",
"*.crt",
"*.cer",
"*.jks",
"*.keystore",
"*.pkcs12",
"*.credentials",
"*.secrets",
"*.ppk",
"*.asc",
"*.gpg",
"*.kdbx",
"*.wallet",
"*.dat"
],
"filenames": [
".env",
".env.local",
".env.development",
".env.production",
".env.staging",
".env.test",
"credentials.json",
"secrets.json",
"secrets.yaml",
"secrets.yml",
"id_rsa",
"id_dsa",
"id_ecdsa",
"id_ed25519",
"known_hosts",
"authorized_keys",
".pgpass",
".my.cnf",
"web.config",
"appsettings.Production.json",
"appsettings.Secrets.json",
"ServiceConfiguration.Cloud.cscfg",
"shadow",
"passwd",
"master.key",
"encryption.key",
"private.key",
"privatekey.pem"
],
"paths": [
"**/.ssh/*",
"**/.aws/*",
"**/.azure/*",
"**/.gcp/*",
"**/.config/gcloud/*",
"**/credentials/*",
"**/secrets/*",
"**/.gnupg/*",
"**/.docker/config.json",
"**/AppData/Roaming/Microsoft/Crypto/*",
"**/AppData/Local/Microsoft/Credentials/*",
"C:/Users/*/AppData/Roaming/Microsoft/Protect/*",
"C:/ProgramData/Microsoft/Crypto/*",
"**/.kube/config",
"**/terraform.tfstate",
"**/terraform.tfvars",
"**/*.tfvars.json"
],
"content_patterns": [
{
"name": "AWS Access Key",
"regex": "AKIA[0-9A-Z]{16}",
"description": "AWS access key pattern"
},
{
"name": "Private Key Header",
"regex": "-----BEGIN (RSA|DSA|EC|OPENSSH) PRIVATE KEY-----",
"description": "Private key file header"
},
{
"name": "Generic API Key",
"regex": "api[_-]?key['\"]?\\s*[:=]\\s*['\"]?[a-zA-Z0-9]{32,}",
"description": "Generic API key assignment"
}
]
}
4.7 Hook Configuration in Managed Settings
Integration with managed-settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit:**",
"hooks": [
{
"type": "command",
"command": "powershell -ExecutionPolicy Bypass -File C:\\ProgramData\\ClaudeCode\\hooks\\validate-edit.ps1",
"timeout": 10000
}
]
},
{
"matcher": "Bash:**",
"hooks": [
{
"type": "command",
"command": "powershell -ExecutionPolicy Bypass -File C:\\ProgramData\\ClaudeCode\\hooks\\validate-bash.ps1",
"timeout": 10000
}
]
},
{
"matcher": "Read:**",
"hooks": [
{
"type": "command",
"command": "powershell -ExecutionPolicy Bypass -File C:\\ProgramData\\ClaudeCode\\hooks\\validate-read.ps1",
"timeout": 5000
}
]
}
],
"PostToolUse": [
{
"matcher": "**",
"hooks": [
{
"type": "command",
"command": "powershell -ExecutionPolicy Bypass -File C:\\ProgramData\\ClaudeCode\\hooks\\audit-log.ps1",
"timeout": 5000
}
]
}
],
"SessionStart": [
{
"matcher": "**",
"hooks": [
{
"type": "command",
"command": "powershell -ExecutionPolicy Bypass -File C:\\ProgramData\\ClaudeCode\\hooks\\session-start.ps1"
}
]
}
],
"SessionEnd": [
{
"matcher": "**",
"hooks": [
{
"type": "command",
"command": "powershell -ExecutionPolicy Bypass -File C:\\ProgramData\\ClaudeCode\\hooks\\session-end.ps1"
}
]
}
]
}
}
4.8 Testing Hooks
test-hooks.ps1:
<#
.SYNOPSIS
Test suite for Claude Code security hooks
#>
Write-Host "Testing Claude Code Security Hooks..." -ForegroundColor Cyan
# Test 1: Sensitive file edit should be blocked
Write-Host "`n[TEST 1] Attempting to edit .env file (should BLOCK)..." -ForegroundColor Yellow
$env:CLAUDE_HOOK_INPUT = @{
tool = "Edit"
parameters = @{
file_path = "C:\projects\myapp\.env"
}
} | ConvertTo-Json -Compress
$result = powershell -File "C:\ProgramData\ClaudeCode\hooks\validate-edit.ps1"
if ($LASTEXITCODE -eq 2) {
Write-Host "✓ PASS: .env edit blocked as expected" -ForegroundColor Green
} else {
Write-Host "✗ FAIL: .env edit was not blocked (exit code: $LASTEXITCODE)" -ForegroundColor Red
}
# Test 2: Normal file edit should be allowed
Write-Host "`n[TEST 2] Attempting to edit regular file (should ALLOW)..." -ForegroundColor Yellow
$env:CLAUDE_HOOK_INPUT = @{
tool = "Edit"
parameters = @{
file_path = "C:\projects\myapp\src\index.js"
}
} | ConvertTo-Json -Compress
$result = powershell -File "C:\ProgramData\ClaudeCode\hooks\validate-edit.ps1"
if ($LASTEXITCODE -eq 0) {
Write-Host "✓ PASS: Regular file edit allowed" -ForegroundColor Green
} else {
Write-Host "✗ FAIL: Regular file edit was blocked (exit code: $LASTEXITCODE)" -ForegroundColor Red
}
# Test 3: Dangerous bash command should be blocked
Write-Host "`n[TEST 3] Attempting dangerous bash command (should BLOCK)..." -ForegroundColor Yellow
$env:CLAUDE_HOOK_INPUT = @{
tool = "Bash"
parameters = @{
command = "rm -rf /important/data"
}
} | ConvertTo-Json -Compress
$result = powershell -File "C:\ProgramData\ClaudeCode\hooks\validate-bash.ps1"
if ($LASTEXITCODE -eq 2) {
Write-Host "✓ PASS: Dangerous command blocked" -ForegroundColor Green
} else {
Write-Host "✗ FAIL: Dangerous command was not blocked" -ForegroundColor Red
}
# Test 4: Audit logging should work
Write-Host "`n[TEST 4] Testing audit logging..." -ForegroundColor Yellow
$auditLog = "C:\ProgramData\ClaudeCode\logs\audit.jsonl"
$beforeCount = if (Test-Path $auditLog) { (Get-Content $auditLog).Count } else { 0 }
$env:CLAUDE_HOOK_INPUT = @{
tool = "Read"
parameters = @{
file_path = "C:\projects\test.txt"
}
result = @{
success = $true
}
} | ConvertTo-Json -Compress
powershell -File "C:\ProgramData\ClaudeCode\hooks\audit-log.ps1"
$afterCount = if (Test-Path $auditLog) { (Get-Content $auditLog).Count } else { 0 }
if ($afterCount -gt $beforeCount) {
Write-Host "✓ PASS: Audit log entry created" -ForegroundColor Green
} else {
Write-Host "✗ FAIL: Audit log entry not created" -ForegroundColor Red
}
Write-Host "`nHook testing complete!" -ForegroundColor Cyan
5. Sensitive File Protection Patterns
5.1 Comprehensive File Pattern Database
Categories of Sensitive Files:
5.1.1 Environment and Configuration Files
{
"environment_files": [
".env",
".env.local",
".env.development",
".env.production",
".env.staging",
".env.test",
".env.*.local",
"env",
"env.sh",
".envrc",
".env.example" // Even examples may contain patterns
]
}
5.1.2 Cryptographic Keys and Certificates
{
"crypto_files": {
"private_keys": [
"*.key",
"*.pem",
"privatekey.pem",
"private.pem",
"private-key.pem",
"*.private.key",
"id_rsa",
"id_dsa",
"id_ecdsa",
"id_ed25519"
],
"certificates": [
"*.pfx",
"*.p12",
"*.p7b",
"*.p7s",
"*.der",
"*.crt",
"*.cer",
"*.cert",
"*.cacert"
],
"keystores": [
"*.jks",
"*.keystore",
"*.pkcs12",
"keystore.jks",
"truststore.jks",
"*.kdb",
"*.sth"
],
"pgp_gpg": [
"*.asc",
"*.gpg",
"*.pgp",
"pubring.gpg",
"secring.gpg",
"trustdb.gpg"
]
}
}
5.1.3 Cloud Provider Credentials
{
"cloud_credentials": {
"aws": [
".aws/credentials",
".aws/config",
"aws_access_key_id",
"credentials.csv",
"*_accessKeys.csv",
"*.aws_credentials"
],
"azure": [
".azure/credentials",
"azureProfile.json",
"*.publishsettings",
"*.azurePubxml",
"ServiceConfiguration.*.cscfg"
],
"gcp": [
".config/gcloud/*",
"*-service-account.json",
"*-credentials.json",
"*.json" // If in .gcp or credentials directories
],
"general": [
"credentials.json",
"credentials.yml",
"credentials.yaml",
"*.credentials",
"secrets.json",
"secrets.yml",
"secrets.yaml",
"*.secrets"
]
}
}
5.1.4 SSH and Remote Access
{
"ssh_remote": [
".ssh/id_rsa",
".ssh/id_dsa",
".ssh/id_ecdsa",
".ssh/id_ed25519",
".ssh/identity",
".ssh/config",
".ssh/known_hosts",
".ssh/authorized_keys",
"*.ppk", // PuTTY private key
"*.pem", // SSH private key in PEM format
".putty/sessions/*"
]
}
5.1.5 Database Credentials
{
"database": [
".my.cnf",
".pgpass",
"*.sql" // If contains passwords
"database.yml",
"database.json",
"connection.config",
"connectionStrings.config",
"*.mdf", // SQL Server database files
"*.ldf", // SQL Server log files
"*.sqlite",
"*.sqlite3",
"*.db"
]
}
5.1.6 Application-Specific Secrets
{
"app_secrets": {
"dotnet": [
"appsettings.Production.json",
"appsettings.Secrets.json",
"appsettings.*.json", // If production/sensitive
"web.config",
"app.config",
"secrets.xml",
"*.exe.config"
],
"java": [
"*.properties" // If contains passwords
"application-prod.properties",
"application-secret.properties",
"hibernate.cfg.xml"
],
"nodejs": [
".npmrc" // If contains auth tokens
".yarnrc.yml",
"package-lock.json", // Only if contains private registry credentials
"npm-shrinkwrap.json"
],
"python": [
".pypirc",
"*.cfg" // If contains credentials
"settings_local.py",
"secrets.py"
],
"ruby": [
"database.yml",
"secrets.yml",
".bundle/config" // If contains credentials
],
"docker": [
".docker/config.json",
"docker-compose.override.yml", // May contain production secrets
"*.dockercfg"
],
"kubernetes": [
".kube/config",
"kubeconfig",
"*.kubeconfig",
"*-kubeconfig.yaml"
],
"terraform": [
"terraform.tfstate",
"terraform.tfstate.backup",
"terraform.tfvars",
"*.tfvars",
"*.tfvars.json",
"*.auto.tfvars"
]
}
}
5.1.7 Windows-Specific Sensitive Files
{
"windows_sensitive": [
"ntuser.dat",
"SAM",
"SYSTEM",
"SECURITY",
"SOFTWARE",
"*.reg", // Registry exports may contain credentials
"Unattend.xml",
"Autounattend.xml",
"sysprep.inf",
"sysprep.xml",
"AppData/Roaming/Microsoft/Crypto/*",
"AppData/Local/Microsoft/Credentials/*",
"AppData/Roaming/Microsoft/Protect/*",
"ProgramData/Microsoft/Crypto/RSA/MachineKeys/*",
"*.rdp" // Remote Desktop settings may contain saved credentials
]
}
5.1.8 Cryptocurrency Wallets
{
"crypto_wallets": [
"wallet.dat",
"*.wallet",
"*.keystore", // Ethereum keystores
"UTC--*", // Ethereum keystore format
"*.kdbx", // KeePass database
"*.dat" // Generic wallet files
]
}
5.1.9 Browser and Email Credentials
{
"browser_email": [
"AppData/Local/Google/Chrome/User Data/*/Login Data",
"AppData/Local/Microsoft/Edge/User Data/*/Login Data",
"AppData/Roaming/Mozilla/Firefox/Profiles/**/logins.json",
"AppData/Roaming/Mozilla/Firefox/Profiles/**/key4.db",
"AppData/Roaming/Thunderbird/Profiles/**/logins.json",
"*.pst", // Outlook data files
"*.ost"
]
}
5.2 Content-Based Detection Patterns
validate-read-content.ps1 (Advanced hook for content scanning):
<#
.SYNOPSIS
Content-based sensitive data detection
.DESCRIPTION
Scans file contents for sensitive patterns like API keys, passwords
#>
param(
[Parameter(Mandatory=$false)]
[string]$CLAUDE_HOOK_INPUT
)
# Parse input
$input = $CLAUDE_HOOK_INPUT | ConvertFrom-Json
$filePath = $input.parameters.file_path
if (-not $filePath -or -not (Test-Path $filePath)) {
exit 0
}
# Only scan text files (skip binaries)
$textExtensions = @('.txt', '.md', '.json', '.yaml', '.yml', '.xml', '.config', '.properties', '.env', '.ini', '.conf', '.toml', '.js', '.ts', '.py', '.java', '.cs', '.rb', '.go', '.php', '.sh', '.ps1', '.bat', '.cmd')
$extension = [System.IO.Path]::GetExtension($filePath).ToLower()
if ($textExtensions -notcontains $extension) {
# Binary file, skip content scanning
exit 0
}
# Read file content (max 1MB for performance)
$maxSize = 1MB
$fileSize = (Get-Item $filePath).Length
if ($fileSize -gt $maxSize) {
# File too large, skip content scanning
exit 0
}
$content = Get-Content $filePath -Raw -ErrorAction SilentlyContinue
if (-not $content) {
exit 0
}
# Sensitive patterns (regex)
$patterns = @(
@{
name = "AWS Access Key"
regex = 'AKIA[0-9A-Z]{16}'
severity = "CRITICAL"
},
@{
name = "AWS Secret Key"
regex = '[''"][0-9a-zA-Z/+]{40}[''"]'
severity = "CRITICAL"
},
@{
name = "Private Key"
regex = '-----BEGIN (RSA|DSA|EC|OPENSSH|ENCRYPTED|PGP) PRIVATE KEY-----'
severity = "CRITICAL"
},
@{
name = "Generic API Key"
regex = '(?i)(api[_-]?key|apikey)[''"]?\s*[:=]\s*[''"]?[a-zA-Z0-9]{32,}[''"]?'
severity = "HIGH"
},
@{
name = "Generic Password"
regex = '(?i)(password|passwd|pwd)[''"]?\s*[:=]\s*[''"][^''"]{8,}[''"]'
severity = "HIGH"
},
@{
name = "Database Connection String"
regex = '(?i)(Server|Data Source|Initial Catalog|User ID|Password)=[^;]+;'
severity = "HIGH"
},
@{
name = "JWT Token"
regex = 'eyJ[a-zA-Z0-9_-]+\.eyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+'
severity = "MEDIUM"
},
@{
name = "GitHub Token"
regex = 'ghp_[a-zA-Z0-9]{36}'
severity = "CRITICAL"
},
@{
name = "Slack Token"
regex = 'xox[baprs]-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24,32}'
severity = "HIGH"
},
@{
name = "Stripe API Key"
regex = 'sk_live_[0-9a-zA-Z]{24,}'
severity = "CRITICAL"
}
)
# Scan for patterns
$detectedPatterns = @()
foreach ($pattern in $patterns) {
if ($content -match $pattern.regex) {
$detectedPatterns += $pattern
}
}
if ($detectedPatterns.Count -gt 0) {
# Sort by severity
$criticalPatterns = $detectedPatterns | Where-Object { $_.severity -eq "CRITICAL" }
if ($criticalPatterns.Count -gt 0) {
# CRITICAL severity = BLOCK
$patternNames = ($criticalPatterns | ForEach-Object { $_.name }) -join ", "
$blockMessage = @{
continue = $false
stopReason = "SECURITY BLOCK: File contains sensitive data patterns: $patternNames"
suppressOutput = $false
systemMessage = "This file contains potentially sensitive credentials or keys. Access denied by security policy."
} | ConvertTo-Json -Compress
Write-Output $blockMessage
exit 2
} else {
# HIGH/MEDIUM severity = WARN but allow
$patternNames = ($detectedPatterns | ForEach-Object { $_.name }) -join ", "
Write-Warning "File contains potentially sensitive patterns: $patternNames"
# Log to audit trail
$auditEntry = @{
timestamp = (Get-Date).ToUniversalTime().ToString("o")
user = $env:USERNAME
file = $filePath
detected_patterns = $patternNames
action = "ALLOWED_WITH_WARNING"
} | ConvertTo-Json -Compress
Add-Content -Path "C:\ProgramData\ClaudeCode\logs\content-scan.jsonl" -Value $auditEntry
# Allow but warn
exit 0
}
}
# No sensitive patterns detected
exit 0
5.3 Directory-Based Protection
blocked-directories.json:
{
"windows_system": [
"C:\\Windows",
"C:\\Windows\\System32",
"C:\\Windows\\SysWOW64",
"C:\\Windows\\WinSxS",
"C:\\Windows\\Boot",
"C:\\Windows\\Fonts",
"C:\\Windows\\inf",
"C:\\Windows\\PolicyDefinitions",
"C:\\Windows\\Registration",
"C:\\Windows\\rescache",
"C:\\Windows\\Resources",
"C:\\Windows\\schemas",
"C:\\Windows\\security",
"C:\\Windows\\servicing",
"C:\\Windows\\System",
"C:\\Windows\\SystemApps",
"C:\\Windows\\SystemResources",
"C:\\Windows\\WaaS"
],
"program_files": [
"C:\\Program Files",
"C:\\Program Files (x86)",
"C:\\ProgramData\\Microsoft\\Windows\\Start Menu",
"C:\\ProgramData\\Microsoft\\Windows\\AppRepository",
"C:\\ProgramData\\WindowsHolographicDevices"
],
"security_sensitive": [
"C:\\ProgramData\\Microsoft\\Crypto",
"C:\\Users\\*\\AppData\\Roaming\\Microsoft\\Crypto",
"C:\\Users\\*\\AppData\\Local\\Microsoft\\Credentials",
"C:\\Users\\*\\AppData\\Roaming\\Microsoft\\Protect",
"C:\\Users\\*\\AppData\\Roaming\\Microsoft\\SystemCertificates",
"C:\\Windows\\System32\\config",
"C:\\Windows\\System32\\config\\SAM",
"C:\\Windows\\System32\\config\\SECURITY",
"C:\\Windows\\System32\\config\\SYSTEM",
"C:\\Windows\\System32\\config\\SOFTWARE"
],
"user_sensitive": [
"C:\\Users\\*\\.ssh",
"C:\\Users\\*\\.aws",
"C:\\Users\\*\\.azure",
"C:\\Users\\*\\.gcp",
"C:\\Users\\*\\.gnupg",
"C:\\Users\\*\\.docker",
"C:\\Users\\*\\.kube",
"C:\\Users\\*\\AppData\\Local\\Google\\Chrome\\User Data\\*\\Login Data",
"C:\\Users\\*\\AppData\\Local\\Microsoft\\Edge\\User Data\\*\\Login Data",
"C:\\Users\\*\\AppData\\Roaming\\Mozilla\\Firefox\\Profiles"
],
"claude_installation": [
"C:\\ProgramData\\ClaudeCode",
"C:\\ProgramData\\ClaudeCode\\managed-policies",
"C:\\ProgramData\\ClaudeCode\\hooks",
"C:\\ProgramData\\ClaudeCode\\npm-global"
]
}
5.4 Integration into Managed Settings
Complete permission deny rules:
{
"permissions": {
"deny": [
// Environment files
{"tool": "Edit", "matcher": "**/.env*"},
{"tool": "Edit", "matcher": "**/env"},
{"tool": "Edit", "matcher": "**/env.sh"},
{"tool": "Edit", "matcher": "**/.envrc"},
{"tool": "Read", "matcher": "**/.env.production"},
{"tool": "Read", "matcher": "**/.env.staging"},
// Cryptographic keys
{"tool": "Edit", "matcher": "**/*.key"},
{"tool": "Edit", "matcher": "**/*.pem"},
{"tool": "Edit", "matcher": "**/*.pfx"},
{"tool": "Edit", "matcher": "**/*.p12"},
{"tool": "Edit", "matcher": "**/*.jks"},
{"tool": "Edit", "matcher": "**/*.keystore"},
{"tool": "Read", "matcher": "**/id_rsa"},
{"tool": "Read", "matcher": "**/id_dsa"},
{"tool": "Read", "matcher": "**/id_ecdsa"},
{"tool": "Read", "matcher": "**/id_ed25519"},
// Credentials
{"tool": "Edit", "matcher": "**/credentials.json"},
{"tool": "Edit", "matcher": "**/credentials.yml"},
{"tool": "Edit", "matcher": "**/secrets.json"},
{"tool": "Edit", "matcher": "**/secrets.yml"},
{"tool": "Read", "matcher": "**/.aws/credentials"},
{"tool": "Read", "matcher": "**/.azure/credentials"},
{"tool": "Read", "matcher": "**/*-service-account.json"},
// Windows system directories
{"tool": "Edit", "matcher": "C:/Windows/**"},
{"tool": "Edit", "matcher": "C:/Program Files/**"},
{"tool": "Edit", "matcher": "C:/Program Files (x86)/**"},
{"tool": "Read", "matcher": "C:/Windows/System32/config/**"},
{"tool": "Read", "matcher": "**/AppData/Roaming/Microsoft/Crypto/**"},
{"tool": "Read", "matcher": "**/AppData/Local/Microsoft/Credentials/**"},
// Dangerous bash operations
{"tool": "Bash", "matcher": "**/rm -rf*"},
{"tool": "Bash", "matcher": "**/del /f*"},
{"tool": "Bash", "matcher": "**/format*"},
{"tool": "Bash", "matcher": "**/reg delete*"},
{"tool": "Bash", "matcher": "**/net user*"},
// Protected installation
{"tool": "Edit", "matcher": "C:/ProgramData/ClaudeCode/**"}
]
}
}
6. Windows System Directory Protection
6.1 Critical Windows Directories
Tier 1: Absolute No Access (BLOCK ALL OPERATIONS)
Directory | Purpose | Risk Level | Protection |
---|---|---|---|
C:\Windows\System32 |
Core system files (64-bit) | CRITICAL | BLOCK Read/Write |
C:\Windows\SysWOW64 |
Core system files (32-bit) | CRITICAL | BLOCK Read/Write |
C:\Windows\System32\config |
Registry hives (SAM, SYSTEM) | CRITICAL | BLOCK Read/Write |
C:\Program Files |
Installed applications | HIGH | BLOCK Write |
C:\Program Files (x86) |
32-bit applications | HIGH | BLOCK Write |
C:\ProgramData\Microsoft\Crypto |
Encryption keys | CRITICAL | BLOCK Read/Write |
Tier 2: Read-Only (Allow Read, Block Write)
Directory | Purpose | Risk Level | Protection |
---|---|---|---|
C:\Windows |
General Windows files | HIGH | Allow Read, Block Write |
C:\Windows\Fonts |
System fonts | MEDIUM | Allow Read, Block Write |
C:\Windows\inf |
Driver information files | MEDIUM | Allow Read, Block Write |
Tier 3: Credential Stores (BLOCK ALL)
Directory | Purpose | Risk Level | Protection |
---|---|---|---|
%APPDATA%\Microsoft\Crypto |
User crypto keys | CRITICAL | BLOCK Read/Write |
%LOCALAPPDATA%\Microsoft\Credentials |
Stored credentials | CRITICAL | BLOCK Read/Write |
%APPDATA%\Microsoft\Protect |
DPAPI master keys | CRITICAL | BLOCK Read/Write |
%APPDATA%\Microsoft\SystemCertificates |
User certificates | HIGH | BLOCK Read/Write |
6.2 Windows System Protection Hook
validate-windows-paths.ps1:
<#
.SYNOPSIS
Validates operations against Windows system directories
#>
param(
[Parameter(Mandatory=$false)]
[string]$CLAUDE_HOOK_INPUT
)
# Parse input
$input = $CLAUDE_HOOK_INPUT | ConvertFrom-Json
$tool = $input.tool
$filePath = $input.parameters.file_path -or $input.parameters.path
if (-not $filePath) {
exit 0
}
# Normalize path
$normalizedPath = $filePath -replace '/', '\' | Resolve-Path -ErrorAction SilentlyContinue
# Critical directories (BLOCK ALL)
$criticalDirs = @(
"$env:SystemRoot\System32\config",
"$env:SystemRoot\System32",
"$env:SystemRoot\SysWOW64",
"$env:ProgramData\Microsoft\Crypto",
"$env:APPDATA\Microsoft\Crypto",
"$env:LOCALAPPDATA\Microsoft\Credentials",
"$env:APPDATA\Microsoft\Protect",
"C:\ProgramData\ClaudeCode" # Protect our installation
)
foreach ($criticalDir in $criticalDirs) {
if ($normalizedPath -like "$criticalDir\*") {
$blockMessage = @{
continue = $false
stopReason = "SECURITY BLOCK: Access to critical Windows directory denied: $criticalDir"
suppressOutput = $false
systemMessage = "This directory contains critical system files or credentials. Access is prohibited by security policy."
} | ConvertTo-Json -Compress
Write-Output $blockMessage
exit 2
}
}
# Write-protected directories (BLOCK Write, Allow Read)
if ($tool -eq "Edit" -or $tool -eq "Write") {
$writeProtectedDirs = @(
"$env:SystemRoot",
"${env:ProgramFiles}",
"${env:ProgramFiles(x86)}"
)
foreach ($protectedDir in $writeProtectedDirs) {
if ($normalizedPath -like "$protectedDir\*") {
$blockMessage = @{
continue = $false
stopReason = "SECURITY BLOCK: Write access to Windows directory denied: $protectedDir"
suppressOutput = $false
} | ConvertTo-Json -Compress
Write-Output $blockMessage
exit 2
}
}
}
# Allow operation
exit 0
6.3 Registry Protection
Block registry modifications via Bash hook:
# In validate-bash.ps1, add registry protection
# Dangerous registry operations
$registryPatterns = @(
'reg\s+(add|delete|import)',
'regedit\s+',
'New-ItemProperty.*HKLM',
'Set-ItemProperty.*HKLM',
'Remove-ItemProperty.*HKLM',
'HKEY_LOCAL_MACHINE',
'HKLM:'
)
foreach ($pattern in $registryPatterns) {
if ($command -match $pattern) {
$blockMessage = @{
continue = $false
stopReason = "SECURITY BLOCK: Registry modification attempts are prohibited"
suppressOutput = $false
} | ConvertTo-Json -Compress
Write-Output $blockMessage
exit 2
}
}
6.4 Windows Service Protection
Block service manipulation:
# Service manipulation patterns
$servicePatterns = @(
'sc\s+(create|delete|config|stop|start)',
'New-Service',
'Set-Service',
'Stop-Service',
'Start-Service',
'Remove-Service',
'net\s+stop',
'net\s+start'
)
foreach ($pattern in $servicePatterns) {
if ($command -match $pattern) {
$blockMessage = @{
continue = $false
stopReason = "SECURITY BLOCK: Windows service manipulation is prohibited"
suppressOutput = $false
} | ConvertTo-Json -Compress
Write-Output $blockMessage
exit 2
}
}
6.5 Complete Windows Protection Configuration
managed-settings.json (Windows system protection section):
{
"permissions": {
"deny": [
// Windows System32
{"tool": "Read", "matcher": "C:/Windows/System32/config/**"},
{"tool": "Read", "matcher": "C:/Windows/System32/SAM"},
{"tool": "Read", "matcher": "C:/Windows/System32/SECURITY"},
{"tool": "Read", "matcher": "C:/Windows/System32/SYSTEM"},
{"tool": "Edit", "matcher": "C:/Windows/System32/**"},
{"tool": "Write", "matcher": "C:/Windows/System32/**"},
// Windows SysWOW64
{"tool": "Edit", "matcher": "C:/Windows/SysWOW64/**"},
{"tool": "Write", "matcher": "C:/Windows/SysWOW64/**"},
// Windows root
{"tool": "Edit", "matcher": "C:/Windows/**"},
{"tool": "Write", "matcher": "C:/Windows/**"},
// Program Files
{"tool": "Edit", "matcher": "C:/Program Files/**"},
{"tool": "Write", "matcher": "C:/Program Files/**"},
{"tool": "Edit", "matcher": "C:/Program Files (x86)/**"},
{"tool": "Write", "matcher": "C:/Program Files (x86)/**"},
// Crypto and credentials
{"tool": "Read", "matcher": "**/AppData/Roaming/Microsoft/Crypto/**"},
{"tool": "Read", "matcher": "**/AppData/Local/Microsoft/Credentials/**"},
{"tool": "Read", "matcher": "**/AppData/Roaming/Microsoft/Protect/**"},
{"tool": "Read", "matcher": "**/AppData/Roaming/Microsoft/SystemCertificates/**"},
{"tool": "Read", "matcher": "C:/ProgramData/Microsoft/Crypto/**"},
// System operations via Bash
{"tool": "Bash", "matcher": "**/reg add*"},
{"tool": "Bash", "matcher": "**/reg delete*"},
{"tool": "Bash", "matcher": "**/regedit*"},
{"tool": "Bash", "matcher": "**/sc create*"},
{"tool": "Bash", "matcher": "**/sc delete*"},
{"tool": "Bash", "matcher": "**/net user*"},
{"tool": "Bash", "matcher": "**/net localgroup*"},
// Protect Claude installation
{"tool": "Edit", "matcher": "C:/ProgramData/ClaudeCode/**"},
{"tool": "Write", "matcher": "C:/ProgramData/ClaudeCode/**"}
]
},
"hooks": {
"PreToolUse": [
{
"matcher": "Edit:**",
"hooks": [
{
"type": "command",
"command": "powershell -ExecutionPolicy Bypass -File C:\\ProgramData\\ClaudeCode\\hooks\\validate-windows-paths.ps1"
}
]
},
{
"matcher": "Read:**",
"hooks": [
{
"type": "command",
"command": "powershell -ExecutionPolicy Bypass -File C:\\ProgramData\\ClaudeCode\\hooks\\validate-windows-paths.ps1"
}
]
}
]
}
}
7. Permission Models & Deny Rules
7.1 Understanding Permission Modes
Claude Code supports multiple permission modes for controlling tool access:
Mode | Behavior | Use Case | Security Level |
---|---|---|---|
plan |
Analysis only, no modifications | Initial codebase exploration | HIGHEST |
default |
Prompt for first tool use | Standard development | HIGH |
ask |
Confirm each tool use | Sensitive operations | HIGH |
acceptEdits |
Auto-accept file edits | Trusted projects | MEDIUM |
bypassPermissions |
Skip all prompts | NEVER use in enterprise | NONE |
Enterprise Recommendation: Use plan
mode by default in managed policies.
7.2 Permission Rule Types
1. Deny Rules (Highest Priority - Always Block)
{
"deny": [
{"tool": "Edit", "matcher": "**/.env*"},
{"tool": "Bash", "matcher": "**/rm -rf*"}
]
}
2. Ask Rules (Require Confirmation)
{
"ask": [
{"tool": "Edit", "matcher": "**/*.json"},
{"tool": "Bash", "matcher": "**"}
]
}
3. Allow Rules (Permit Without Prompt)
{
"allow": [
{"tool": "Read", "matcher": "**/*.md"},
{"tool": "Read", "matcher": "**/*.js"}
]
}
7.3 Enterprise Permission Matrix
Recommended Enterprise Configuration:
{
"permissions": {
"defaultMode": "plan",
"deny": [
// === CRITICAL: Credentials & Keys ===
{"tool": "Edit", "matcher": "**/.env*"},
{"tool": "Edit", "matcher": "**/*.key"},
{"tool": "Edit", "matcher": "**/*.pem"},
{"tool": "Edit", "matcher": "**/*.pfx"},
{"tool": "Edit", "matcher": "**/*.p12"},
{"tool": "Edit", "matcher": "**/credentials*"},
{"tool": "Edit", "matcher": "**/secrets*"},
{"tool": "Read", "matcher": "**/id_rsa"},
{"tool": "Read", "matcher": "**/id_dsa"},
{"tool": "Read", "matcher": "**/.aws/credentials"},
{"tool": "Read", "matcher": "**/.ssh/id_*"},
// === CRITICAL: Windows System ===
{"tool": "Edit", "matcher": "C:/Windows/**"},
{"tool": "Edit", "matcher": "C:/Program Files/**"},
{"tool": "Edit", "matcher": "C:/Program Files (x86)/**"},
{"tool": "Read", "matcher": "C:/Windows/System32/config/**"},
{"tool": "Read", "matcher": "**/AppData/**/Crypto/**"},
{"tool": "Read", "matcher": "**/AppData/**/Credentials/**"},
// === CRITICAL: Dangerous Commands ===
{"tool": "Bash", "matcher": "**/rm -rf*"},
{"tool": "Bash", "matcher": "**/del /f*"},
{"tool": "Bash", "matcher": "**/format*"},
{"tool": "Bash", "matcher": "**/diskpart*"},
{"tool": "Bash", "matcher": "**/reg delete*"},
{"tool": "Bash", "matcher": "**/net user*"},
{"tool": "Bash", "matcher": "**/sc delete*"},
// === HIGH: Build & Dependency Files ===
{"tool": "Edit", "matcher": "**/package-lock.json"},
{"tool": "Edit", "matcher": "**/yarn.lock"},
{"tool": "Edit", "matcher": "**/Gemfile.lock"},
{"tool": "Edit", "matcher": "**/Pipfile.lock"},
{"tool": "Edit", "matcher": "**/composer.lock"},
{"tool": "Edit", "matcher": "**/.git/**"},
// === HIGH: Infrastructure as Code ===
{"tool": "Edit", "matcher": "**/terraform.tfstate"},
{"tool": "Edit", "matcher": "**/terraform.tfvars"},
{"tool": "Edit", "matcher": "**/*.tfvars"},
// === MEDIUM: Configuration Files (Require Review) ===
{"tool": "Edit", "matcher": "**/web.config"},
{"tool": "Edit", "matcher": "**/app.config"},
{"tool": "Edit", "matcher": "**/appsettings.*.json"},
// === Protect Claude Installation ===
{"tool": "Edit", "matcher": "C:/ProgramData/ClaudeCode/**"},
{"tool": "Write", "matcher": "C:/ProgramData/ClaudeCode/**"}
],
"ask": [
// Configuration files
{"tool": "Edit", "matcher": "**/*.json"},
{"tool": "Edit", "matcher": "**/*.yaml"},
{"tool": "Edit", "matcher": "**/*.yml"},
{"tool": "Edit", "matcher": "**/*.toml"},
{"tool": "Edit", "matcher": "**/*.ini"},
{"tool": "Edit", "matcher": "**/*.conf"},
// All bash commands require confirmation
{"tool": "Bash", "matcher": "**"},
// Critical code files
{"tool": "Edit", "matcher": "**/Dockerfile"},
{"tool": "Edit", "matcher": "**/*.Dockerfile"},
{"tool": "Edit", "matcher": "**/docker-compose*.yml"},
// CI/CD files
{"tool": "Edit", "matcher": "**/.github/workflows/**"},
{"tool": "Edit", "matcher": "**/.gitlab-ci.yml"},
{"tool": "Edit", "matcher": "**/Jenkinsfile"},
{"tool": "Edit", "matcher": "**/.circleci/**"}
],
"allow": [
// Documentation
{"tool": "Read", "matcher": "**/*.md"},
{"tool": "Read", "matcher": "**/*.txt"},
{"tool": "Read", "matcher": "**/README*"},
{"tool": "Read", "matcher": "**/CHANGELOG*"},
{"tool": "Read", "matcher": "**/LICENSE*"},
// Source code (read-only)
{"tool": "Read", "matcher": "**/*.js"},
{"tool": "Read", "matcher": "**/*.ts"},
{"tool": "Read", "matcher": "**/*.jsx"},
{"tool": "Read", "matcher": "**/*.tsx"},
{"tool": "Read", "matcher": "**/*.py"},
{"tool": "Read", "matcher": "**/*.java"},
{"tool": "Read", "matcher": "**/*.cs"},
{"tool": "Read", "matcher": "**/*.go"},
{"tool": "Read", "matcher": "**/*.rb"},
{"tool": "Read", "matcher": "**/*.php"},
{"tool": "Read", "matcher": "**/*.c"},
{"tool": "Read", "matcher": "**/*.cpp"},
{"tool": "Read", "matcher": "**/*.h"},
{"tool": "Read", "matcher": "**/*.rs"},
// Markup & styles
{"tool": "Read", "matcher": "**/*.html"},
{"tool": "Read", "matcher": "**/*.css"},
{"tool": "Read", "matcher": "**/*.scss"},
{"tool": "Read", "matcher": "**/*.less"},
{"tool": "Read", "matcher": "**/*.xml"},
// Non-sensitive edits (with user in control)
{"tool": "Edit", "matcher": "**/*.md"},
{"tool": "Edit", "matcher": "**/docs/**/*.md"},
{"tool": "Edit", "matcher": "**/README.md"}
],
"additionalDirectories": []
}
}
7.4 Matcher Pattern Syntax
Claude Code uses gitignore-style glob patterns:
Pattern | Matches | Example |
---|---|---|
* |
Any characters except / |
*.js matches file.js |
** |
Any characters including / |
*/.env matches .env at any depth |
? |
Single character | file?.js matches file1.js , fileA.js |
[abc] |
Character class | file[123].js matches file1.js , file2.js |
{a,b} |
Alternatives | *.{js,ts} matches file.js or file.ts |
!pattern |
Negation | !/test/ excludes test directories |
Path Normalization:
- Forward slashes
/
are converted to backslashes\
on Windows - Paths are case-insensitive on Windows
- Use
**
to match across directory boundaries
7.5 Tool-Specific Deny Strategies
7.5.1 Edit Tool Restrictions
{
"deny": [
// Prevent editing of files with sensitive extensions
{"tool": "Edit", "matcher": "**/*.{env,key,pem,pfx,p12,jks,credentials}"},
// Prevent editing of specific filenames
{"tool": "Edit", "matcher": "**/credentials.json"},
{"tool": "Edit", "matcher": "**/secrets.{json,yaml,yml}"},
// Prevent editing in sensitive directories
{"tool": "Edit", "matcher": "**/.ssh/**"},
{"tool": "Edit", "matcher": "**/.aws/**"},
// Prevent editing of lock files
{"tool": "Edit", "matcher": "**/*-lock.{json,yaml}"},
{"tool": "Edit", "matcher": "**/package-lock.json"},
// Prevent editing of git internals
{"tool": "Edit", "matcher": "**/.git/**"}
]
}
7.5.2 Bash Tool Restrictions
{
"deny": [
// Destructive file operations
{"tool": "Bash", "matcher": "**/rm -rf /**"},
{"tool": "Bash", "matcher": "**/del /f /**"},
{"tool": "Bash", "matcher": "**/rmdir /s /**"},
// System modifications
{"tool": "Bash", "matcher": "**/reg add**"},
{"tool": "Bash", "matcher": "**/reg delete**"},
{"tool": "Bash", "matcher": "**/sc delete**"},
{"tool": "Bash", "matcher": "**/net user**"},
// Network exfiltration
{"tool": "Bash", "matcher": "**/curl** -d **"},
{"tool": "Bash", "matcher": "**/wget** --post**"},
{"tool": "Bash", "matcher": "**/nc -l**"},
// Process injection
{"tool": "Bash", "matcher": "**/powershell** -enc**"},
{"tool": "Bash", "matcher": "**/cmd /c**"}
]
}
7.5.3 Read Tool Restrictions
{
"deny": [
// Credential files
{"tool": "Read", "matcher": "**/.env.production"},
{"tool": "Read", "matcher": "**/id_rsa"},
{"tool": "Read", "matcher": "**/.aws/credentials"},
// Windows credential stores
{"tool": "Read", "matcher": "**/AppData/Roaming/Microsoft/Crypto/**"},
{"tool": "Read", "matcher": "**/AppData/Local/Microsoft/Credentials/**"},
// System files
{"tool": "Read", "matcher": "C:/Windows/System32/config/SAM"},
{"tool": "Read", "matcher": "C:/Windows/System32/config/SECURITY"}
]
}
7.6 Dynamic Permission Evaluation
Advanced: Context-Aware Permissions via Hooks
# validate-permission.ps1
# Dynamic permission evaluation based on file content, user role, time of day, etc.
param(
[Parameter(Mandatory=$false)]
[string]$CLAUDE_HOOK_INPUT
)
$input = $CLAUDE_HOOK_INPUT | ConvertFrom-Json
# Example: Block operations during maintenance window
$maintenanceHours = 2..5 # 2 AM - 5 AM
$currentHour = (Get-Date).Hour
if ($currentHour -in $maintenanceHours) {
$blockMessage = @{
continue = $false
stopReason = "SECURITY BLOCK: Operations not permitted during maintenance window (2 AM - 5 AM)"
} | ConvertTo-Json -Compress
Write-Output $blockMessage
exit 2
}
# Example: Require additional authentication for production files
if ($input.parameters.file_path -like "*production*") {
# Check if user has production access (could query AD, database, etc.)
$userHasProductionAccess = Test-UserAccess -User $env:USERNAME -Resource "Production"
if (-not $userHasProductionAccess) {
$blockMessage = @{
continue = $false
stopReason = "SECURITY BLOCK: User does not have production file access"
} | ConvertTo-Json -Compress
Write-Output $blockMessage
exit 2
}
}
# Example: Rate limiting - block if too many operations in short time
$rateLimitFile = "C:\ProgramData\ClaudeCode\logs\rate-limit.json"
$maxOpsPerMinute = 50
if (Test-Path $rateLimitFile) {
$rateData = Get-Content $rateLimitFile -Raw | ConvertFrom-Json
$recentOps = $rateData.operations | Where-Object {
(Get-Date $_.timestamp) -gt (Get-Date).AddMinutes(-1)
}
if ($recentOps.Count -ge $maxOpsPerMinute) {
$blockMessage = @{
continue = $false
stopReason = "SECURITY BLOCK: Rate limit exceeded ($maxOpsPerMinute operations/minute)"
} | ConvertTo-Json -Compress
Write-Output $blockMessage
exit 2
}
}
# Allow operation
exit 0
7.7 Permission Testing & Validation
Test script for permission configuration:
<#
.SYNOPSIS
Validates permission configuration
#>
function Test-ClaudePermissions {
$managedSettings = "C:\ProgramData\ClaudeCode\managed-settings.json"
if (-not (Test-Path $managedSettings)) {
Write-Error "Managed settings not found"
return $false
}
$settings = Get-Content $managedSettings -Raw | ConvertFrom-Json
$issues = @()
# Test 1: Verify sensitive file protections
$requiredDenies = @(
"**/.env*",
"**/*.key",
"**/*.pem",
"**/credentials*"
)
foreach ($required in $requiredDenies) {
$found = $settings.permissions.deny | Where-Object {
$_.matcher -eq $required -and $_.tool -eq "Edit"
}
if (-not $found) {
$issues += "Missing deny rule for: $required"
}
}
# Test 2: Verify dangerous bash commands blocked
$dangerousBash = @(
"**/rm -rf*",
"**/del /f*",
"**/format*"
)
foreach ($dangerous in $dangerousBash) {
$found = $settings.permissions.deny | Where-Object {
$_.matcher -eq $dangerous -and $_.tool -eq "Bash"
}
if (-not $found) {
$issues += "Missing bash deny rule for: $dangerous"
}
}
# Test 3: Verify Windows system directories protected
$systemDirs = @(
"C:/Windows/**",
"C:/Program Files/**"
)
foreach ($dir in $systemDirs) {
$found = $settings.permissions.deny | Where-Object {
$_.matcher -eq $dir -and $_.tool -eq "Edit"
}
if (-not $found) {
$issues += "Missing system directory protection for: $dir"
}
}
# Test 4: Verify default mode is secure
if ($settings.permissions.defaultMode -notin @("plan", "default", "ask")) {
$issues += "Insecure default mode: $($settings.permissions.defaultMode)"
}
# Report results
if ($issues.Count -eq 0) {
Write-Host "✓ All permission checks passed" -ForegroundColor Green
return $true
} else {
Write-Host "✗ Permission configuration issues:" -ForegroundColor Red
$issues | ForEach-Object { Write-Host " $_" -ForegroundColor Yellow }
return $false
}
}
Test-ClaudePermissions
8. Network Security Controls
8.1 Network Access Requirements
Claude Code requires connectivity to specific endpoints:
Endpoint | Purpose | Required | Alternative |
---|---|---|---|
api.anthropic.com |
Claude API | YES | AWS Bedrock, GCP Vertex AI |
claude.ai |
Authentication, updates | YES | N/A |
statsig.anthropic.com |
Telemetry (optional) | NO | Can disable |
sentry.io |
Error reporting (optional) | NO | Can disable |
8.2 Corporate Proxy Configuration
Managed Settings with Proxy:
{
"envVars": {
"HTTP_PROXY": "http://proxy.corp.example.com:8080",
"HTTPS_PROXY": "https://proxy.corp.example.com:8080",
"NO_PROXY": "localhost,127.0.0.1,.corp.example.com,*.internal"
}
}
Proxy with Authentication:
{
"envVars": {
"HTTP_PROXY": "http://username:password@proxy.corp.example.com:8080",
"HTTPS_PROXY": "https://username:password@proxy.corp.example.com:8080"
}
}
Security Warning: Avoid hardcoding credentials in managed settings. Use Windows Credential Manager or environment variables set via Group Policy.
8.3 Firewall Rules for Claude Code
Windows Firewall Configuration:
# Allow outbound HTTPS to Anthropic API
New-NetFirewallRule -DisplayName "Claude Code - Anthropic API" `
-Direction Outbound `
-Program "C:\ProgramData\ClaudeCode\npm-global\node_modules\@anthropic-ai\claude-code\*" `
-RemoteAddress "api.anthropic.com" `
-Protocol TCP `
-RemotePort 443 `
-Action Allow
# Allow outbound to Claude.ai
New-NetFirewallRule -DisplayName "Claude Code - Claude.ai" `
-Direction Outbound `
-Program "C:\ProgramData\ClaudeCode\npm-global\node_modules\@anthropic-ai\claude-code\*" `
-RemoteAddress "claude.ai" `
-Protocol TCP `
-RemotePort 443 `
-Action Allow
# Block all other outbound connections from Claude Code
New-NetFirewallRule -DisplayName "Claude Code - Block Others" `
-Direction Outbound `
-Program "C:\ProgramData\ClaudeCode\npm-global\node_modules\@anthropic-ai\claude-code\*" `
-Action Block `
-Priority 2
8.4 TLS/SSL Configuration
Custom CA Certificate (Corporate MITM Proxies):
{
"envVars": {
"NODE_EXTRA_CA_CERTS": "C:\\ProgramData\\ClaudeCode\\certs\\corporate-ca.crt"
}
}
Deploy Corporate CA Certificate:
# Copy corporate CA cert
Copy-Item "\\fileserver\IT\certs\corporate-ca.crt" `
-Destination "C:\ProgramData\ClaudeCode\certs\corporate-ca.crt"
# Verify certificate
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2("C:\ProgramData\ClaudeCode\certs\corporate-ca.crt")
Write-Host "CA Certificate: $($cert.Subject)"
Write-Host "Valid Until: $($cert.NotAfter)"
8.5 Mutual TLS (mTLS) Authentication
For environments requiring client certificates:
{
"envVars": {
"NODE_EXTRA_CA_CERTS": "C:\\ProgramData\\ClaudeCode\\certs\\ca.crt",
"NODE_TLS_CLIENT_CERT": "C:\\ProgramData\\ClaudeCode\\certs\\client.crt",
"NODE_TLS_CLIENT_KEY": "C:\\ProgramData\\ClaudeCode\\certs\\client.key"
}
}
Note: Anthropic's API doesn't currently require mTLS, but this configuration supports future enterprise requirements or custom LLM gateways.
8.6 Disabling Non-Essential Network Traffic
Minimal Network Configuration:
{
"envVars": {
"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "true",
"HTTP_PROXY": "http://proxy.corp.example.com:8080",
"NO_PROXY": "localhost,127.0.0.1"
}
}
What This Disables:
- Telemetry to
statsig.anthropic.com
- Error reporting to
sentry.io
- Update checks (rely on managed deployment instead)
- Optional analytics
8.7 LLM Gateway Integration
Enterprise Pattern: Route Through Internal Gateway
Claude Code → Corporate LLM Gateway → Anthropic API
↓
- Rate limiting
- Content filtering
- Audit logging
- Cost tracking
Configure Gateway Proxy:
{
"envVars": {
"ANTHROPIC_API_BASE_URL": "https://llm-gateway.corp.example.com/v1",
"ANTHROPIC_API_KEY": "${GATEWAY_API_KEY}",
"HTTP_PROXY": "http://llm-gateway.corp.example.com:8080"
}
}
Benefits of LLM Gateway:
- Centralized API key management
- Cross-team cost allocation
- Content policy enforcement (PII redaction, etc.)
- Request/response logging for compliance
- Rate limiting and quota management
- Failover to alternative providers
8.8 URL Allowlist Hook
Network request validation hook:
# validate-network.ps1
param(
[Parameter(Mandatory=$false)]
[string]$CLAUDE_HOOK_INPUT
)
$input = $CLAUDE_HOOK_INPUT | ConvertFrom-Json
# Check for WebFetch tool
if ($input.tool -eq "WebFetch") {
$url = $input.parameters.url
# Allowlist of permitted domains
$allowedDomains = @(
"docs.anthropic.com",
"github.com",
"stackoverflow.com",
"developer.mozilla.org",
"*.microsoft.com",
"*.corp.example.com" # Internal domains
)
$urlHost = ([System.Uri]$url).Host
$isAllowed = $false
foreach ($allowedDomain in $allowedDomains) {
if ($allowedDomain -like "*.*") {
# Wildcard domain
$pattern = $allowedDomain -replace '\*', '.*'
if ($urlHost -match $pattern) {
$isAllowed = $true
break
}
} elseif ($urlHost -eq $allowedDomain) {
$isAllowed = $true
break
}
}
if (-not $isAllowed) {
$blockMessage = @{
continue = $false
stopReason = "SECURITY BLOCK: URL not in allowlist: $url"
systemMessage = "Only approved domains can be accessed. Contact IT to request access."
} | ConvertTo-Json -Compress
Write-Output $blockMessage
exit 2
}
}
# Allow operation
exit 0
8.9 Network Monitoring & Logging
Monitor Claude Code network connections:
# Monitor outbound connections from Claude Code
Get-NetTCPConnection | Where-Object {
$_.OwningProcess -eq (Get-Process -Name "node" | Where-Object {
$_.Path -like "*ClaudeCode*"
}).Id
} | Select-Object LocalAddress, LocalPort, RemoteAddress, RemotePort, State | Format-Table
# Log to file
Get-NetTCPConnection | Where-Object {
$_.OwningProcess -eq (Get-Process -Name "node" | Where-Object {
$_.Path -like "*ClaudeCode*"
}).Id
} | ConvertTo-Json | Out-File "C:\ProgramData\ClaudeCode\logs\network-$(Get-Date -Format 'yyyyMMdd').json"
9. DevContainer Isolation Strategy
9.1 Why DevContainers for Claude Code Security
Security Benefits:
- Process Isolation: Claude runs in container, not host OS
- Network Isolation: Firewall rules limit container connectivity
- Filesystem Isolation: Restricted access to host filesystem
- Credential Isolation: Separate credential stores per project
- Reproducibility: Consistent, auditable environments
Use Cases:
- Working with untrusted repositories
- Client project isolation (consulting firms)
- Sandbox for testing Claude Code capabilities
- Preventing cross-contamination of credentials
9.2 Secure DevContainer Configuration
.devcontainer/devcontainer.json:
{
"name": "Secure Claude Code Environment",
"image": "mcr.microsoft.com/devcontainers/base:ubuntu",
"features": {
"ghcr.io/devcontainers/features/node:1": {
"version": "lts"
}
},
"customizations": {
"vscode": {
"extensions": [
"anthropics.claude-code"
]
}
},
"postCreateCommand": "npm install -g @anthropic-ai/claude-code",
"mounts": [
"source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=cached",
"source=claude-npm-cache,target=/root/.npm,type=volume"
],
"runArgs": [
"--cap-drop=ALL",
"--cap-add=NET_BIND_SERVICE",
"--security-opt=no-new-privileges",
"--read-only",
"--tmpfs=/tmp:rw,noexec,nosuid,size=1g"
],
"containerEnv": {
"ANTHROPIC_API_KEY": "${localEnv:ANTHROPIC_API_KEY}",
"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "true"
},
"remoteUser": "vscode"
}
9.3 Network Firewall for DevContainer
Dockerfile with Network Restrictions:
FROM mcr.microsoft.com/devcontainers/base:ubuntu
# Install iptables and configure firewall
RUN apt-get update && apt-get install -y iptables iptables-persistent
# Configure firewall rules
RUN iptables -P INPUT DROP && \
iptables -P FORWARD DROP && \
iptables -P OUTPUT DROP && \
iptables -A OUTPUT -o lo -j ACCEPT && \
iptables -A INPUT -i lo -j ACCEPT && \
iptables -A OUTPUT -p tcp --dport 443 -d api.anthropic.com -j ACCEPT && \
iptables -A OUTPUT -p tcp --dport 443 -d claude.ai -j ACCEPT && \
iptables -A OUTPUT -p udp --dport 53 -j ACCEPT && \
iptables -A OUTPUT -p tcp --dport 22 -j ACCEPT && \
netfilter-persistent save
# Install Node.js and Claude Code
RUN curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - && \
apt-get install -y nodejs && \
npm install -g @anthropic-ai/claude-code
# Create non-root user
RUN useradd -m -s /bin/bash vscode && \
mkdir -p /workspace && \
chown -R vscode:vscode /workspace
USER vscode
WORKDIR /workspace
CMD ["/bin/bash"]
9.4 Read-Only Root Filesystem
Enhanced security with read-only container:
{
"runArgs": [
"--read-only",
"--tmpfs=/tmp:rw,noexec,nosuid,size=512m",
"--tmpfs=/home/vscode/.claude:rw,nosuid,size=100m",
"--tmpfs=/home/vscode/.npm:rw,nosuid,size=500m"
]
}
Benefits:
- Prevents malware persistence
- Blocks unauthorized file modifications
- Forces ephemeral changes (container restart clears tampering)
9.5 Credential Management in DevContainers
Option 1: Environment Variable Injection (Recommended)
{
"containerEnv": {
"ANTHROPIC_API_KEY": "${localEnv:ANTHROPIC_API_KEY}"
}
}
Host sets environment variable, devcontainer inherits it without storing in files.
Option 2: Secrets via Docker Secrets
# On host
echo "sk-ant-api03-..." | docker secret create anthropic_api_key -
# In devcontainer
docker run --secret anthropic_api_key ...
Option 3: Volume Mount from Secure Location
{
"mounts": [
"source=C:\\ProgramData\\ClaudeCode\\secrets,target=/secrets,type=bind,readonly"
],
"containerEnv": {
"ANTHROPIC_API_KEY": "$(cat /secrets/api_key)"
}
}
9.6 Multi-Project Isolation Pattern
Scenario: Consulting firm working on projects for multiple clients, ensuring credential separation.
Directory Structure:
C:\Projects\
├── ClientA\
│ └── .devcontainer\
│ ├── devcontainer.json
│ └── Dockerfile
├── ClientB\
│ └── .devcontainer\
│ ├── devcontainer.json
│ └── Dockerfile
└── ClientC\
└── .devcontainer\
├── devcontainer.json
└── Dockerfile
ClientA devcontainer.json:
{
"name": "Client A - Isolated Environment",
"build": {"dockerfile": "Dockerfile"},
"containerEnv": {
"ANTHROPIC_API_KEY": "${localEnv:CLIENTA_ANTHROPIC_KEY}",
"AWS_PROFILE": "clienta",
"PROJECT_NAME": "ClientA"
},
"mounts": [
"source=${localWorkspaceFolder},target=/workspace,type=bind",
"source=clienta-npm-cache,target=/root/.npm,type=volume"
]
}
Result: Each client project runs in isolated container with separate:
- API keys
- Cloud credentials
- npm caches
- Network policies
9.7 DevContainer Security Checklist
- [ ] Use official base images from Microsoft or verified sources
- [ ] Run container as non-root user
- [ ] Drop all capabilities except required ones
- [ ] Enable read-only root filesystem
- [ ] Configure network firewall (allow only required endpoints)
- [ ] Use tmpfs for writable directories
- [ ] Inject secrets via environment variables (not files)
- [ ] Enable security options (
no-new-privileges
,seccomp
) - [ ] Regularly update base images
- [ ] Scan images for vulnerabilities (Trivy, Snyk)
- [ ] Limit resource usage (CPU, memory, disk)
- [ ] Implement logging and monitoring
10. Enterprise Deployment Checklist
10.1 Pre-Deployment Preparation
Phase 1: Requirements Gathering (Week 1)
- [ ] Identify all teams/users who will use Claude Code
- [ ] Document compliance requirements (GDPR, HIPAA, SOC2, etc.)
- [ ] List sensitive file types specific to your organization
- [ ] Map Windows system directories requiring protection
- [ ] Define permission levels by user role
- [ ] Identify network proxy/firewall requirements
- [ ] Determine authentication method (Claude API, AWS Bedrock, GCP Vertex)
Phase 2: Infrastructure Setup (Week 2)
- [ ] Choose installation path (
C:\ProgramData\ClaudeCode
recommended) - [ ] Configure npm global prefix
- [ ] Set up Group Policy infrastructure for deployment
- [ ] Prepare managed policy JSON files
- [ ] Configure corporate proxy settings
- [ ] Set up audit log centralization (SIEM integration)
- [ ] Create service account for Claude Code (if needed)
Phase 3: Security Configuration (Week 2-3)
- [ ] Create
managed-settings.json
with enterprise policies - [ ] Develop security hooks (edit, bash, read validation)
- [ ] Create
sensitive-files.json
pattern database - [ ] Create
blocked-directories.json
Windows paths - [ ] Configure network firewall rules
- [ ] Set up TLS/SSL certificates (if using MITM proxy)
- [ ] Implement content scanning hook (optional)
- [ ] Configure rate limiting hooks (optional)
Phase 4: Testing (Week 3-4)
- [ ] Deploy to pilot group (5-10 users)
- [ ] Test sensitive file protection (attempt to edit
.env
) - [ ] Test dangerous command blocking (attempt
rm -rf /
) - [ ] Test Windows system directory protection
- [ ] Verify audit logging works
- [ ] Test proxy connectivity
- [ ] Validate permission rules
- [ ] Conduct security penetration testing
- [ ] Review audit logs for anomalies
Phase 5: Documentation (Week 4)
- [ ] Create user onboarding guide
- [ ] Document approved use cases
- [ ] Write incident response procedures
- [ ] Create troubleshooting runbook
- [ ] Prepare security policy documentation
- [ ] Document escalation procedures
Phase 6: Rollout (Week 5+)
- [ ] Deploy to production via Group Policy
- [ ] Conduct user training sessions
- [ ] Set up helpdesk support procedures
- [ ] Monitor audit logs daily (first week)
- [ ] Gather user feedback
- [ ] Iterate on permission policies as needed
10.2 Complete Managed Settings Template
C:\ProgramData\ClaudeCode\managed-settings.json:
{
"$schema": "https://api.claude.com/schemas/settings-v1.json",
"model": "claude-sonnet-4-5",
"permissions": {
"defaultMode": "plan",
"deny": [
{"tool": "Edit", "matcher": "**/.env*"},
{"tool": "Edit", "matcher": "**/*.key"},
{"tool": "Edit", "matcher": "**/*.pem"},
{"tool": "Edit", "matcher": "**/*.pfx"},
{"tool": "Edit", "matcher": "**/*.p12"},
{"tool": "Edit", "matcher": "**/credentials*"},
{"tool": "Edit", "matcher": "**/secrets*"},
{"tool": "Edit", "matcher": "**/*.jks"},
{"tool": "Edit", "matcher": "**/*.keystore"},
{"tool": "Read", "matcher": "**/id_rsa"},
{"tool": "Read", "matcher": "**/id_dsa"},
{"tool": "Read", "matcher": "**/.aws/credentials"},
{"tool": "Read", "matcher": "**/.ssh/id_*"},
{"tool": "Edit", "matcher": "C:/Windows/**"},
{"tool": "Edit", "matcher": "C:/Program Files/**"},
{"tool": "Edit", "matcher": "C:/Program Files (x86)/**"},
{"tool": "Read", "matcher": "C:/Windows/System32/config/**"},
{"tool": "Read", "matcher": "**/AppData/**/Crypto/**"},
{"tool": "Read", "matcher": "**/AppData/**/Credentials/**"},
{"tool": "Bash", "matcher": "**/rm -rf*"},
{"tool": "Bash", "matcher": "**/del /f*"},
{"tool": "Bash", "matcher": "**/format*"},
{"tool": "Bash", "matcher": "**/reg delete*"},
{"tool": "Bash", "matcher": "**/net user*"},
{"tool": "Edit", "matcher": "**/package-lock.json"},
{"tool": "Edit", "matcher": "**/.git/**"},
{"tool": "Edit", "matcher": "**/terraform.tfstate"},
{"tool": "Edit", "matcher": "C:/ProgramData/ClaudeCode/**"}
],
"ask": [
{"tool": "Edit", "matcher": "**/*.json"},
{"tool": "Edit", "matcher": "**/*.yaml"},
{"tool": "Edit", "matcher": "**/*.yml"},
{"tool": "Bash", "matcher": "**"},
{"tool": "Edit", "matcher": "**/Dockerfile"},
{"tool": "Edit", "matcher": "**/.github/workflows/**"}
],
"allow": [
{"tool": "Read", "matcher": "**/*.md"},
{"tool": "Read", "matcher": "**/*.js"},
{"tool": "Read", "matcher": "**/*.ts"},
{"tool": "Read", "matcher": "**/*.py"},
{"tool": "Edit", "matcher": "**/*.md"}
],
"additionalDirectories": []
},
"hooks": {
"PreToolUse": [
{
"matcher": "Edit:**",
"hooks": [
{
"type": "command",
"command": "powershell -ExecutionPolicy Bypass -File C:\\ProgramData\\ClaudeCode\\hooks\\validate-edit.ps1",
"timeout": 10000
}
]
},
{
"matcher": "Bash:**",
"hooks": [
{
"type": "command",
"command": "powershell -ExecutionPolicy Bypass -File C:\\ProgramData\\ClaudeCode\\hooks\\validate-bash.ps1",
"timeout": 10000
}
]
},
{
"matcher": "Read:**",
"hooks": [
{
"type": "command",
"command": "powershell -ExecutionPolicy Bypass -File C:\\ProgramData\\ClaudeCode\\hooks\\validate-read.ps1",
"timeout": 5000
}
]
}
],
"PostToolUse": [
{
"matcher": "**",
"hooks": [
{
"type": "command",
"command": "powershell -ExecutionPolicy Bypass -File C:\\ProgramData\\ClaudeCode\\hooks\\audit-log.ps1",
"timeout": 5000
}
]
}
],
"SessionStart": [
{
"matcher": "**",
"hooks": [
{
"type": "command",
"command": "powershell -ExecutionPolicy Bypass -File C:\\ProgramData\\ClaudeCode\\hooks\\session-start.ps1"
}
]
}
],
"SessionEnd": [
{
"matcher": "**",
"hooks": [
{
"type": "command",
"command": "powershell -ExecutionPolicy Bypass -File C:\\ProgramData\\ClaudeCode\\hooks\\session-end.ps1"
}
]
}
]
},
"envVars": {
"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "true",
"NODE_EXTRA_CA_CERTS": "C:\\ProgramData\\ClaudeCode\\certs\\corporate-ca.crt",
"HTTP_PROXY": "http://proxy.corp.example.com:8080",
"HTTPS_PROXY": "https://proxy.corp.example.com:8080",
"NO_PROXY": "localhost,127.0.0.1,.corp.example.com"
}
}
10.3 Deployment Automation Script
deploy-claude-enterprise.ps1:
<#
.SYNOPSIS
Enterprise deployment automation for Claude Code
.DESCRIPTION
Installs Claude Code, deploys managed policies, configures hooks, sets permissions
.PARAMETER SourcePath
Network path to Claude Code deployment package
.EXAMPLE
.\deploy-claude-enterprise.ps1 -SourcePath "\\fileserver\IT\ClaudeCode"
#>
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string]$SourcePath,
[string]$InstallPath = "C:\ProgramData\ClaudeCode",
[switch]$SkipInstall,
[switch]$SkipPolicies,
[switch]$SkipHooks
)
# Requires admin
if (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
Write-Error "This script requires Administrator privileges"
exit 1
}
Write-Host "=== Claude Code Enterprise Deployment ===" -ForegroundColor Cyan
Write-Host "Source: $SourcePath" -ForegroundColor Cyan
Write-Host "Install Path: $InstallPath" -ForegroundColor Cyan
Write-Host ""
# Step 1: Create directory structure
if (-not $SkipInstall) {
Write-Host "[1/6] Creating directory structure..." -ForegroundColor Yellow
$directories = @(
"$InstallPath\npm-global",
"$InstallPath\managed-policies",
"$InstallPath\hooks",
"$InstallPath\logs",
"$InstallPath\certs"
)
foreach ($dir in $directories) {
if (-not (Test-Path $dir)) {
New-Item -ItemType Directory -Force -Path $dir | Out-Null
}
}
# Set permissions
icacls $InstallPath /grant "BUILTIN\Administrators:(OI)(CI)F" /T | Out-Null
icacls $InstallPath /grant "BUILTIN\Users:(OI)(CI)RX" /T | Out-Null
Write-Host "✓ Directories created" -ForegroundColor Green
}
# Step 2: Install Claude Code
if (-not $SkipInstall) {
Write-Host "[2/6] Installing Claude Code..." -ForegroundColor Yellow
# Configure npm
npm config set prefix "$InstallPath\npm-global" --global
# Install
npm install -g @anthropic-ai/claude-code --quiet
# Add to PATH
$currentPath = [Environment]::GetEnvironmentVariable("Path", "Machine")
if ($currentPath -notlike "*$InstallPath\npm-global*") {
[Environment]::SetEnvironmentVariable(
"Path",
"$currentPath;$InstallPath\npm-global",
"Machine"
)
}
Write-Host "✓ Claude Code installed" -ForegroundColor Green
}
# Step 3: Deploy managed policies
if (-not $SkipPolicies) {
Write-Host "[3/6] Deploying managed policies..." -ForegroundColor Yellow
$managedSettingsSource = Join-Path $SourcePath "managed-settings.json"
$managedSettingsDest = Join-Path $InstallPath "managed-policies\managed-settings.json"
if (Test-Path $managedSettingsSource) {
Copy-Item $managedSettingsSource $managedSettingsDest -Force
# Make read-only
Set-ItemProperty -Path $managedSettingsDest -Name IsReadOnly -Value $true
icacls $managedSettingsDest /inheritance:r /grant "BUILTIN\Administrators:(F)" /grant "BUILTIN\Users:(R)" | Out-Null
Write-Host "✓ Managed policies deployed" -ForegroundColor Green
} else {
Write-Warning "Managed settings not found at $managedSettingsSource"
}
}
# Step 4: Deploy hooks
if (-not $SkipHooks) {
Write-Host "[4/6] Deploying security hooks..." -ForegroundColor Yellow
$hooksSource = Join-Path $SourcePath "hooks"
$hooksDest = Join-Path $InstallPath "hooks"
if (Test-Path $hooksSource) {
Copy-Item "$hooksSource\*" $hooksDest -Force -Recurse
# Make hooks read-only
Get-ChildItem $hooksDest -File | ForEach-Object {
Set-ItemProperty -Path $_.FullName -Name IsReadOnly -Value $true
}
Write-Host "✓ Security hooks deployed" -ForegroundColor Green
} else {
Write-Warning "Hooks directory not found at $hooksSource"
}
}
# Step 5: Deploy certificates
Write-Host "[5/6] Deploying certificates..." -ForegroundColor Yellow
$certSource = Join-Path $SourcePath "certs\corporate-ca.crt"
$certDest = Join-Path $InstallPath "certs\corporate-ca.crt"
if (Test-Path $certSource) {
Copy-Item $certSource $certDest -Force
Write-Host "✓ Certificates deployed" -ForegroundColor Green
} else {
Write-Warning "Certificate not found at $certSource"
}
# Step 6: Validate deployment
Write-Host "[6/6] Validating deployment..." -ForegroundColor Yellow
$validationErrors = @()
# Check installation
$claudeVersion = claude --version 2>$null
if ($LASTEXITCODE -ne 0) {
$validationErrors += "Claude Code installation failed"
}
# Check managed policy
$managedPolicy = "$InstallPath\managed-policies\managed-settings.json"
if (-not (Test-Path $managedPolicy)) {
$validationErrors += "Managed policy not found"
}
# Check hooks
$requiredHooks = @("validate-edit.ps1", "validate-bash.ps1", "audit-log.ps1")
foreach ($hook in $requiredHooks) {
if (-not (Test-Path "$InstallPath\hooks\$hook")) {
$validationErrors += "Hook not found: $hook"
}
}
if ($validationErrors.Count -eq 0) {
Write-Host ""
Write-Host "✓ Deployment completed successfully!" -ForegroundColor Green
Write-Host ""
Write-Host "Claude Code version: $claudeVersion" -ForegroundColor Cyan
Write-Host "Installation path: $InstallPath" -ForegroundColor Cyan
Write-Host ""
Write-Host "Next steps:" -ForegroundColor Yellow
Write-Host "1. Verify managed policies: $managedPolicy"
Write-Host "2. Test with pilot users"
Write-Host "3. Monitor audit logs: $InstallPath\logs\"
Write-Host "4. Run: Test-ClaudeCodeSecurity"
} else {
Write-Host ""
Write-Host "✗ Deployment completed with errors:" -ForegroundColor Red
$validationErrors | ForEach-Object { Write-Host " $_" -ForegroundColor Yellow }
exit 1
}
10.4 Post-Deployment Verification
Run comprehensive validation:
# Test 1: Verify installation
claude --version
# Test 2: Check managed policy is enforced
Get-Content "C:\ProgramData\ClaudeCode\managed-policies\managed-settings.json" | ConvertFrom-Json | Select-Object -ExpandProperty permissions
# Test 3: Attempt to edit .env file (should BLOCK)
cd C:\TestProject
echo "TEST=value" | Out-File .env
claude "Edit the .env file"
# Expected: Operation blocked by security policy
# Test 4: Attempt dangerous bash command (should BLOCK)
claude "Run: rm -rf /"
# Expected: Command blocked
# Test 5: Verify audit logging
Get-Content "C:\ProgramData\ClaudeCode\logs\audit.jsonl" | Select-Object -Last 10
# Test 6: Verify hooks execute
$env:CLAUDE_HOOK_INPUT = @{tool="Edit"; parameters=@{file_path=".env"}} | ConvertTo-Json -Compress
powershell -File "C:\ProgramData\ClaudeCode\hooks\validate-edit.ps1"
# Expected: Exit code 2 (blocked)
11. Monitoring, Audit & Compliance
11.1 Audit Logging Architecture
Centralized Audit Trail:
Claude Code → PostToolUse Hook → Local JSON Lines Log → SIEM Integration
↓
Local Archive (90 days)
↓
Cold Storage (7 years)
11.2 Audit Log Schema
Standard audit entry format:
{
"timestamp": "2025-10-07T14:23:45.123Z",
"event_type": "tool_use",
"user": "john.doe",
"computer": "DESKTOP-ABC123",
"project_dir": "C:\\Projects\\MyApp",
"tool": "Edit",
"parameters": {
"file_path": "C:\\Projects\\MyApp\\src\\index.js",
"old_string": "const API_KEY = \"test\"",
"new_string": "const API_KEY = process.env.API_KEY"
},
"result": {
"success": true,
"duration_ms": 45
},
"security": {
"hooks_executed": ["validate-edit.ps1", "audit-log.ps1"],
"blocked": false,
"reason": null
},
"session_id": "550e8400-e29b-41d4-a716-446655440000"
}
11.3 SIEM Integration
Splunk Integration:
# In audit-log.ps1, add Splunk forwarding
$splunkHEC = "https://splunk.corp.example.com:8088/services/collector/event"
$splunkToken = $env:SPLUNK_HEC_TOKEN # Set via GPO
$splunkEvent = @{
event = $auditEntry
sourcetype = "claude_code:audit"
source = "claude_code"
index = "security"
} | ConvertTo-Json -Depth 10
try {
Invoke-RestMethod -Uri $splunkHEC `
-Method Post `
-Headers @{"Authorization"="Splunk $splunkToken"} `
-Body $splunkEvent `
-ContentType "application/json" `
-TimeoutSec 5
} catch {
Write-Warning "Failed to forward to Splunk: $_"
}
Elasticsearch Integration:
# Elasticsearch ingestion
$esEndpoint = "https://elasticsearch.corp.example.com:9200/claude-audit/_doc"
$esApiKey = $env:ELASTIC_API_KEY
try {
Invoke-RestMethod -Uri $esEndpoint `
-Method Post `
-Headers @{"Authorization"="ApiKey $esApiKey"} `
-Body ($auditEntry | ConvertTo-Json -Depth 10) `
-ContentType "application/json" `
-TimeoutSec 5
} catch {
Write-Warning "Failed to index in Elasticsearch: $_"
}
11.4 Compliance Reporting
GDPR Data Access Request:
<#
.SYNOPSIS
Extract all Claude Code audit logs for specific user (GDPR/CCPA compliance)
#>
param(
[Parameter(Mandatory=$true)]
[string]$UserEmail
)
$auditLog = "C:\ProgramData\ClaudeCode\logs\audit.jsonl"
$outputReport = "C:\Temp\claude_audit_${UserEmail}_$(Get-Date -Format 'yyyyMMdd').json"
# Extract user's audit entries
Get-Content $auditLog | ForEach-Object {
$entry = $_ | ConvertFrom-Json
if ($entry.user -eq $UserEmail) {
$entry
}
} | ConvertTo-Json -Depth 10 | Out-File $outputReport
Write-Host "Audit report generated: $outputReport"
Write-Host "Entries found: $((Get-Content $outputReport | ConvertFrom-Json).Count)"
SOC 2 Compliance Report:
<#
.SYNOPSIS
Generate SOC 2 compliance report for Claude Code usage
#>
param(
[datetime]$StartDate = (Get-Date).AddDays(-30),
[datetime]$EndDate = (Get-Date)
)
$auditLog = "C:\ProgramData\ClaudeCode\logs\audit.jsonl"
# Parse audit logs
$entries = Get-Content $auditLog | ForEach-Object {
$entry = $_ | ConvertFrom-Json
if ((Get-Date $entry.timestamp) -ge $StartDate -and (Get-Date $entry.timestamp) -le $EndDate) {
$entry
}
}
# Generate report
$report = @{
report_period = @{
start = $StartDate.ToString("o")
end = $EndDate.ToString("o")
}
summary = @{
total_operations = $entries.Count
unique_users = ($entries | Select-Object -ExpandProperty user -Unique).Count
blocked_operations = ($entries | Where-Object { $_.security.blocked -eq $true }).Count
sensitive_file_access = ($entries | Where-Object {
$_.parameters.file_path -match '\.(env|key|pem|credentials)'
}).Count
}
security_events = @{
blocked_operations = $entries | Where-Object { $_.security.blocked -eq $true } | Select-Object timestamp, user, tool, @{N='reason';E={$_.security.reason}}
sensitive_access = $entries | Where-Object {
$_.parameters.file_path -match '\.(env|key|pem|credentials)'
} | Select-Object timestamp, user, tool, @{N='file';E={$_.parameters.file_path}}
}
compliance_controls = @{
managed_policies_enforced = Test-Path "C:\ProgramData\ClaudeCode\managed-policies\managed-settings.json"
hooks_active = (Get-ChildItem "C:\ProgramData\ClaudeCode\hooks" -Filter "*.ps1").Count -ge 3
audit_logging_enabled = Test-Path $auditLog
network_restrictions = $true # Based on firewall rules
}
}
$reportFile = "C:\Temp\claude_soc2_report_$(Get-Date -Format 'yyyyMMdd').json"
$report | ConvertTo-Json -Depth 10 | Out-File $reportFile
Write-Host "SOC 2 compliance report generated: $reportFile"
11.5 Real-Time Alerts
Security alert on suspicious activity:
# In audit-log.ps1, add alerting logic
$alertThresholds = @{
sensitive_file_access_per_hour = 10
blocked_operations_per_hour = 5
unusual_hours = 0..5 # 12 AM - 5 AM
}
# Check for sensitive file access spike
$recentSensitiveAccess = Get-Content $auditLog | Select-Object -Last 100 | ForEach-Object {
$entry = $_ | ConvertFrom-Json
if ((Get-Date $entry.timestamp) -gt (Get-Date).AddHours(-1) -and
$entry.parameters.file_path -match '\.(env|key|pem|credentials)') {
$entry
}
}
if ($recentSensitiveAccess.Count -gt $alertThresholds.sensitive_file_access_per_hour) {
# Send alert
$alertMessage = @{
severity = "HIGH"
title = "Claude Code: Suspicious sensitive file access detected"
description = "User $($env:USERNAME) accessed $($recentSensitiveAccess.Count) sensitive files in the last hour"
details = $recentSensitiveAccess | Select-Object timestamp, tool, @{N='file';E={$_.parameters.file_path}}
} | ConvertTo-Json -Depth 10
# Send to Microsoft Teams, Slack, or email
Invoke-RestMethod -Uri "https://outlook.office.com/webhook/..." `
-Method Post `
-Body $alertMessage `
-ContentType "application/json"
}
# Check for unusual hours activity
$currentHour = (Get-Date).Hour
if ($currentHour -in $alertThresholds.unusual_hours) {
$alertMessage = @{
severity = "MEDIUM"
title = "Claude Code: Activity detected during unusual hours"
description = "User $($env:USERNAME) is using Claude Code at $currentHour:00"
computer = $env:COMPUTERNAME
project = $env:CLAUDE_PROJECT_DIR
} | ConvertTo-Json -Depth 10
# Send alert
Invoke-RestMethod -Uri "https://outlook.office.com/webhook/..." `
-Method Post `
-Body $alertMessage `
-ContentType "application/json"
}
11.6 Dashboarding & Metrics
PowerBI / Grafana Dashboard Queries:
Query 1: Daily Active Users
// KQL query for Azure Data Explorer / Log Analytics
ClaudeAuditLogs
| where timestamp >= ago(30d)
| summarize UniqueUsers = dcount(user) by bin(timestamp, 1d)
| render timechart
Query 2: Top Blocked Operations
ClaudeAuditLogs
| where security_blocked == true
| summarize Count = count() by security_reason
| top 10 by Count desc
Query 3: Sensitive File Access by User
ClaudeAuditLogs
| where parameters_file_path matches regex @"\.(env|key|pem|credentials)"
| summarize AccessCount = count() by user, bin(timestamp, 1h)
| where AccessCount > 5
12. Windows Security Integration
12.1 AppLocker Integration
AppLocker Policy for Claude Code:
<?xml version="1.0" encoding="utf-8"?>
<AppLockerPolicy Version="1">
<RuleCollection Type="Exe" EnforcementMode="Enabled">
<!-- Allow Claude Code from approved location -->
<FilePathRule Id="claude-approved-path"
Name="Claude Code - Approved Installation"
Description="Allow Claude Code from ProgramData"
UserOrGroupSid="S-1-1-0"
Action="Allow">
<Conditions>
<FilePathCondition Path="C:\ProgramData\ClaudeCode\npm-global\*"/>
</Conditions>
</FilePathRule>
<!-- Block Claude Code from other locations -->
<FilePathRule Id="claude-block-others"
Name="Claude Code - Block Unauthorized Locations"
Description="Block Claude Code from AppData and other locations"
UserOrGroupSid="S-1-1-0"
Action="Deny">
<Conditions>
<FilePathCondition Path="%APPDATA%\npm\*claude*"/>
</Conditions>
</FilePathRule>
</RuleCollection>
<RuleCollection Type="Script" EnforcementMode="Enabled">
<!-- Allow Claude hooks from approved location -->
<FilePathRule Id="claude-hooks-approved"
Name="Claude Hooks - Approved"
Description="Allow PowerShell hooks from ProgramData"
UserOrGroupSid="S-1-1-0"
Action="Allow">
<Conditions>
<FilePathCondition Path="C:\ProgramData\ClaudeCode\hooks\*.ps1"/>
</Conditions>
</FilePathRule>
</RuleCollection>
</AppLockerPolicy>
Deploy via Group Policy:
# Export AppLocker policy
Get-AppLockerPolicy -Effective -Xml | Out-File "C:\Temp\ClaudeAppLockerPolicy.xml"
# Import to GPO
Set-AppLockerPolicy -XMLPolicy "C:\Temp\ClaudeAppLockerPolicy.xml" -Merge
12.2 WDAC (Windows Defender Application Control)
WDAC Policy XML:
<?xml version="1.0" encoding="utf-8"?>
<SiPolicy xmlns="urn:schemas-microsoft-com:sipolicy">
<VersionEx>10.0.0.0</VersionEx>
<PolicyTypeID>{A244370E-44C9-4C06-B551-F6016E563076}</PolicyTypeID>
<PlatformID>{2E07F7E4-194C-4D20-B7C9-6F44A6C5A234}</PlatformID>
<Rules>
<Rule>
<Option>Enabled:Unsigned System Integrity Policy</Option>
</Rule>
<Rule>
<Option>Enabled:Advanced Boot Options Menu</Option>
</Rule>
</Rules>
<FileRules>
<Allow ID="ID_ALLOW_CLAUDE_INSTALLATION"
FriendlyName="Claude Code - Approved Installation"
FileName="*"
FilePath="C:\ProgramData\ClaudeCode\npm-global\**"/>
<Allow ID="ID_ALLOW_NODE_FOR_CLAUDE"
FriendlyName="Node.js for Claude Code"
FileName="node.exe"
MinimumFileVersion="18.0.0.0"/>
<Deny ID="ID_DENY_CLAUDE_APPDATA"
FriendlyName="Block Claude from AppData"
FilePath="%APPDATA%\npm\**\claude*"/>
</FileRules>
<Signers />
</SiPolicy>
Convert to binary and deploy:
# Convert XML to binary
ConvertFrom-CIPolicy -XmlFilePath "C:\Temp\ClaudeWDACPolicy.xml" `
-BinaryFilePath "C:\Temp\ClaudeWDACPolicy.bin"
# Copy to system directory
Copy-Item "C:\Temp\ClaudeWDACPolicy.bin" `
-Destination "C:\Windows\System32\CodeIntegrity\SIPolicy.p7b"
# Activate policy (requires reboot)
Invoke-CimMethod -Namespace "root\Microsoft\Windows\CI" `
-ClassName "PS_UpdateAndCompareCIPolicy" `
-MethodName "Update" `
-Arguments @{FilePath="C:\Temp\ClaudeWDACPolicy.bin"}
12.3 Controlled Folder Access
Protect sensitive folders from Claude Code:
# Enable Controlled Folder Access
Set-MpPreference -EnableControlledFolderAccess Enabled
# Add protected folders
Add-MpPreference -ControlledFolderAccessProtectedFolders "C:\SensitiveData"
Add-MpPreference -ControlledFolderAccessProtectedFolders "C:\Projects\ProductionCode"
Add-MpPreference -ControlledFolderAccessProtectedFolders "C:\Users\$env:USERNAME\.ssh"
Add-MpPreference -ControlledFolderAccessProtectedFolders "C:\Users\$env:USERNAME\.aws"
# Verify configuration
Get-MpPreference | Select-Object -ExpandProperty ControlledFolderAccessProtectedFolders
Allow Claude Code (if needed for legitimate access):
# Add Claude Code to allowed applications (use cautiously)
Add-MpPreference -ControlledFolderAccessAllowedApplications "C:\ProgramData\ClaudeCode\npm-global\claude.cmd"
12.4 Windows Event Log Integration
Log Claude Code security events to Windows Event Log:
# Create custom event log source
New-EventLog -LogName "Application" -Source "ClaudeCodeSecurity"
# In hooks, write to Event Log
Write-EventLog -LogName "Application" `
-Source "ClaudeCodeSecurity" `
-EventId 1000 `
-EntryType Warning `
-Message "Blocked edit operation on sensitive file: $filePath by user $env:USERNAME"
# Query Claude Code events
Get-EventLog -LogName "Application" -Source "ClaudeCodeSecurity" -Newest 100
12.5 BitLocker Integration
Ensure sensitive data at rest is encrypted:
# Check if Claude Code installation drive is encrypted
$drive = "C:"
$bitLockerStatus = Get-BitLockerVolume -MountPoint $drive
if ($bitLockerStatus.ProtectionStatus -ne "On") {
Write-Warning "Drive $drive is not protected by BitLocker"
Write-Warning "Claude Code logs and policies contain sensitive data - encryption recommended"
# Optionally enable BitLocker (requires TPM or password)
# Enable-BitLocker -MountPoint $drive -EncryptionMethod XtsAes256 -UsedSpaceOnly
}
12.6 Windows Firewall Advanced Configuration
Create dedicated firewall profile for Claude Code:
# Create new firewall rule with application filtering
New-NetFirewallRule -DisplayName "Claude Code - Outbound HTTPS" `
-Direction Outbound `
-Program "C:\Program Files\nodejs\node.exe" `
-Action Allow `
-Protocol TCP `
-RemotePort 443 `
-RemoteAddress "api.anthropic.com","claude.ai" `
-Profile Domain,Private `
-Enabled True
# Block all other outbound from Node.js (when used by Claude)
New-NetFirewallRule -DisplayName "Claude Code - Block Unauthorized Outbound" `
-Direction Outbound `
-Program "C:\Program Files\nodejs\node.exe" `
-Action Block `
-Enabled True
# Log blocked connections
Set-NetFirewallProfile -Profile Domain,Private,Public -LogBlocked True -LogAllowed False
auditpol /set /subcategory:"Filtering Platform Connection" /success:enable /failure:enable
12.7 Integration Summary Checklist
- [ ] AppLocker policy deployed via GPO
- [ ] WDAC policy configured and active
- [ ] Controlled Folder Access enabled for sensitive directories
- [ ] Windows Event Log source created for Claude Code
- [ ] BitLocker encryption verified on installation drive
- [ ] Windows Firewall rules applied
- [ ] Audit policies configured
- [ ] Security baselines applied (CIS, DISA STIG)
- [ ] Defender ATP / MDI integration configured
- [ ] Conditional Access policies applied (if using Azure AD)
13. Preventing Shadow Installations and Local Bypasses
13.1 The Shadow Installation Threat
Even with comprehensive enterprise controls in place, a sophisticated threat vector remains: developers installing Claude Code locally to bypass centralized security policies.
Attack Scenario:
Developer Workstation:
1. Enterprise installation: C:\ProgramData\ClaudeCode (locked down, managed policies)
2. Shadow installation: C:\Users\john.doe\AppData\Roaming\npm\claude (user-controlled)
3. Developer runs: npx @anthropic-ai/claude-code (bypasses all controls)
4. Or installs locally: npm install -g @anthropic-ai/claude-code --prefix=%LOCALAPPDATA%\npm
Why This is Critical:
Enterprise Control | Shadow Install Bypasses |
---|---|
Managed policies | ✗ Not loaded from ProgramData |
Security hooks | ✗ Hooks not configured |
Audit logging | ✗ No PostToolUse hooks active |
Permission restrictions | ✗ User can set permissive settings |
File protection | ✗ Can read .env, keys, credentials |
Network controls | ✗ Direct API access without proxy |
Compliance | ✗ No audit trail for regulators |
Real-World Risk Examples:
- Credential Theft: Developer uses local Claude to read
.env
files, extract API keys, exfiltrate to personal account - Code Leakage: Proprietary code sent to Anthropic API without corporate proxy/filtering
- Compliance Violation: HIPAA/PCI data processed by unaudited AI tool
- Shadow IT Sprawl: Multiple versions with different security postures across organization
- Incident Response Blind Spot: Security team unaware of tool usage
13.2 Multi-Layer Prevention Strategy
Defense-in-depth approach with 7 security layers:
Layer 1: npm Configuration Lockdown
Lock npm prefix system-wide (read-only):
<#
.SYNOPSIS
Locks npm configuration to prevent local Claude Code installations
#>
# Step 1: Set system-wide npm prefix
$globalNpmRc = "C:\Program Files\nodejs\npmrc"
$lockedPrefix = "C:\ProgramData\ClaudeCode\npm-global"
# Configure global npmrc
$npmConfig = @"
prefix=$lockedPrefix
cache=C:\ProgramData\ClaudeCode\npm-cache
"@
Set-Content -Path $globalNpmRc -Value $npmConfig -Force
# Step 2: Make npmrc read-only
Set-ItemProperty -Path $globalNpmRc -Name IsReadOnly -Value $true
icacls $globalNpmRc /inheritance:r /grant "BUILTIN\Administrators:(F)" /grant "BUILTIN\Users:(R)" | Out-Null
Write-Host "✓ npm configuration locked to enterprise location" -ForegroundColor Green
# Step 3: Block user-level npmrc creation via registry
$registryPath = "HKLM:\SOFTWARE\Policies\npm"
if (-not (Test-Path $registryPath)) {
New-Item -Path $registryPath -Force | Out-Null
}
# Prevent npm from reading user .npmrc
Set-ItemProperty -Path $registryPath -Name "DisableUserConfig" -Value 1 -Type DWord
Write-Host "✓ User-level npm configuration blocked" -ForegroundColor Green
Deploy via Group Policy:
# Create GPO for npm lockdown
$gpoName = "Claude Code - npm Configuration Lockdown"
New-GPO -Name $gpoName
# Add registry policy
# Computer Configuration > Preferences > Windows Settings > Registry
# Key: HKLM\SOFTWARE\Policies\npm
# Value: DisableUserConfig = 1 (REG_DWORD)
# Add file deployment for global npmrc
# Computer Configuration > Preferences > Windows Settings > Files
# Source: \\fileserver\IT\ClaudeCode\npmrc
# Destination: C:\Program Files\nodejs\npmrc
# Action: Replace
Layer 2: AppLocker Advanced Rules
Block execution from all user-writable locations:
<?xml version="1.0" encoding="utf-8"?>
<AppLockerPolicy Version="1">
<!-- Executable Rules -->
<RuleCollection Type="Exe" EnforcementMode="Enabled">
<!-- Allow Claude from approved location ONLY -->
<FilePathRule Id="allow-claude-approved"
Name="Allow Claude - Approved Location"
Action="Allow"
UserOrGroupSid="S-1-1-0">
<Conditions>
<FilePathCondition Path="C:\ProgramData\ClaudeCode\npm-global\*"/>
</Conditions>
</FilePathRule>
<!-- BLOCK AppData npm installations -->
<FilePathRule Id="block-appdata-roaming-npm"
Name="Block Claude - AppData Roaming npm"
Action="Deny"
UserOrGroupSid="S-1-1-0">
<Conditions>
<FilePathCondition Path="%APPDATA%\npm\*"/>
</Conditions>
</FilePathRule>
<FilePathRule Id="block-appdata-local-npm"
Name="Block Claude - AppData Local npm"
Action="Deny"
UserOrGroupSid="S-1-1-0">
<Conditions>
<FilePathCondition Path="%LOCALAPPDATA%\npm\*"/>
</Conditions>
</FilePathRule>
<!-- Block node_modules in user directories -->
<FilePathRule Id="block-userprofile-node-modules"
Name="Block Claude - User node_modules"
Action="Deny"
UserOrGroupSid="S-1-1-0">
<Conditions>
<FilePathCondition Path="%USERPROFILE%\*\node_modules\*claude*"/>
</Conditions>
</FilePathRule>
<!-- Block any claude.exe or claude.cmd outside approved path -->
<FilePublisherRule Id="block-claude-unauthorized"
Name="Block Unauthorized Claude Executable"
Action="Deny"
UserOrGroupSid="S-1-1-0">
<Conditions>
<FilePublisherCondition PublisherName="*" ProductName="*claude*" BinaryName="*">
<BinaryVersionRange LowSection="*" HighSection="*" />
</FilePublisherCondition>
</Conditions>
<Exceptions>
<FilePathCondition Path="C:\ProgramData\ClaudeCode\npm-global\*"/>
</Exceptions>
</FilePublisherRule>
</RuleCollection>
<!-- Script Rules (for .js, .cmd, .ps1 in npm) -->
<RuleCollection Type="Script" EnforcementMode="Enabled">
<!-- Block scripts in AppData npm -->
<FilePathRule Id="block-appdata-npm-scripts"
Name="Block npm Scripts - AppData"
Action="Deny"
UserOrGroupSid="S-1-1-0">
<Conditions>
<FilePathCondition Path="%APPDATA%\npm\*.cmd"/>
<FilePathCondition Path="%APPDATA%\npm\*.js"/>
<FilePathCondition Path="%LOCALAPPDATA%\npm\*.cmd"/>
<FilePathCondition Path="%LOCALAPPDATA%\npm\*.js"/>
</Conditions>
</FilePathRule>
<!-- Allow only approved Claude scripts -->
<FilePathRule Id="allow-claude-scripts-approved"
Name="Allow Claude Scripts - Approved"
Action="Allow"
UserOrGroupSid="S-1-1-0">
<Conditions>
<FilePathCondition Path="C:\ProgramData\ClaudeCode\npm-global\*.cmd"/>
<FilePathCondition Path="C:\ProgramData\ClaudeCode\npm-global\*.js"/>
</Conditions>
</FilePathRule>
</RuleCollection>
<!-- DLL Rules (prevent loading of Claude modules from unauthorized paths) -->
<RuleCollection Type="Dll" EnforcementMode="Enabled">
<FilePathRule Id="block-claude-dlls-appdata"
Name="Block Claude DLLs - AppData"
Action="Deny"
UserOrGroupSid="S-1-1-0">
<Conditions>
<FilePathCondition Path="%APPDATA%\npm\*\node_modules\@anthropic*\*.dll"/>
<FilePathCondition Path="%LOCALAPPDATA%\npm\*\node_modules\@anthropic*\*.dll"/>
</Conditions>
</FilePathRule>
</RuleCollection>
</AppLockerPolicy>
Deploy AppLocker Policy:
# Import AppLocker policy
Set-AppLockerPolicy -XMLPolicy "C:\Temp\ClaudeAppLockerPolicy.xml" -Merge
# Enable Application Identity service (required for AppLocker)
Set-Service -Name AppIDSvc -StartupType Automatic
Start-Service -Name AppIDSvc
# Verify policy
Get-AppLockerPolicy -Effective | Format-List
Layer 3: File System Auditing
Enable auditing for shadow installations:
<#
.SYNOPSIS
Configures file system auditing to detect Claude Code installations in user directories
#>
# Enable file auditing via auditpol
auditpol /set /subcategory:"File System" /success:enable /failure:enable
# Configure audit ACLs on common npm install locations
$auditPaths = @(
"$env:APPDATA\npm",
"$env:LOCALAPPDATA\npm",
"$env:USERPROFILE\node_modules",
"$env:USERPROFILE\.npm"
)
foreach ($path in $auditPaths) {
if (Test-Path $path) {
# Add audit rule: Everyone, CreateFiles/Write, Success
$acl = Get-Acl $path
$auditRule = New-Object System.Security.AccessControl.FileSystemAuditRule(
"Everyone",
"CreateFiles,Write,Delete",
"ContainerInherit,ObjectInherit",
"None",
"Success"
)
$acl.AddAuditRule($auditRule)
Set-Acl $path $acl
Write-Host "✓ Audit configured for: $path" -ForegroundColor Green
}
}
# Forward events to centralized log
# Event ID 4663 = File System: Object Access
# Filter for npm-related file operations
Automated Detection Script:
<#
.SYNOPSIS
Scans for unauthorized Claude Code installations
.DESCRIPTION
Detects shadow Claude installations in user directories and reports to security team
#>
function Find-ShadowClaudeInstallations {
[CmdletBinding()]
param(
[switch]$RemoveUnauthorized,
[switch]$AlertSecurity
)
Write-Host "Scanning for shadow Claude Code installations..." -ForegroundColor Yellow
$shadowInstalls = @()
# Scan common npm locations
$scanPaths = @(
"$env:APPDATA\npm\node_modules\@anthropic-ai\claude-code",
"$env:LOCALAPPDATA\npm\node_modules\@anthropic-ai\claude-code",
"$env:USERPROFILE\node_modules\@anthropic-ai\claude-code",
"$env:USERPROFILE\.npm\@anthropic-ai\claude-code"
)
# Also scan all user profiles
$allUsers = Get-ChildItem "C:\Users" -Directory
foreach ($userProfile in $allUsers) {
$scanPaths += @(
"$($userProfile.FullName)\AppData\Roaming\npm\node_modules\@anthropic-ai\claude-code",
"$($userProfile.FullName)\AppData\Local\npm\node_modules\@anthropic-ai\claude-code",
"$($userProfile.FullName)\node_modules\@anthropic-ai\claude-code"
)
}
foreach ($path in $scanPaths) {
if (Test-Path $path) {
$packageJson = Join-Path $path "package.json"
if (Test-Path $packageJson) {
$package = Get-Content $packageJson -Raw | ConvertFrom-Json
$install = @{
Path = $path
Version = $package.version
User = ($path -replace '^C:\\Users\\([^\\]+)\\.*', '$1')
Size = (Get-ChildItem $path -Recurse | Measure-Object -Property Length -Sum).Sum / 1MB
CreatedDate = (Get-Item $path).CreationTime
}
$shadowInstalls += $install
Write-Host "✗ UNAUTHORIZED INSTALLATION DETECTED!" -ForegroundColor Red
Write-Host " Path: $($install.Path)" -ForegroundColor Red
Write-Host " User: $($install.User)" -ForegroundColor Red
Write-Host " Version: $($install.Version)" -ForegroundColor Red
Write-Host " Created: $($install.CreatedDate)" -ForegroundColor Red
Write-Host ""
}
}
}
# Check for npm configuration overrides
$userNpmRc = "$env:USERPROFILE\.npmrc"
if (Test-Path $userNpmRc) {
$npmConfig = Get-Content $userNpmRc -Raw
if ($npmConfig -match "prefix\s*=") {
Write-Host "✗ WARNING: User has custom npm prefix configuration" -ForegroundColor Red
Write-Host " File: $userNpmRc" -ForegroundColor Red
Write-Host " This may indicate attempt to bypass enterprise controls" -ForegroundColor Red
Write-Host ""
}
}
# Generate report
if ($shadowInstalls.Count -gt 0) {
$reportPath = "C:\ProgramData\ClaudeCode\logs\shadow-installations-$(Get-Date -Format 'yyyyMMdd-HHmmss').json"
$shadowInstalls | ConvertTo-Json -Depth 10 | Out-File $reportPath
Write-Host "Found $($shadowInstalls.Count) unauthorized installation(s)" -ForegroundColor Red
Write-Host "Report saved: $reportPath" -ForegroundColor Cyan
# Alert security team
if ($AlertSecurity) {
$alertMessage = @{
severity = "HIGH"
title = "Shadow Claude Code Installations Detected"
count = $shadowInstalls.Count
installations = $shadowInstalls
timestamp = (Get-Date).ToUniversalTime().ToString("o")
computer = $env:COMPUTERNAME
} | ConvertTo-Json -Depth 10
# Send to SIEM/Security Operations
try {
Invoke-RestMethod -Uri "https://siem.corp.example.com/api/alerts" `
-Method Post `
-Body $alertMessage `
-ContentType "application/json" `
-TimeoutSec 10
} catch {
Write-Warning "Failed to send security alert: $_"
}
}
# Remove unauthorized installations
if ($RemoveUnauthorized) {
Write-Host "Removing unauthorized installations..." -ForegroundColor Yellow
foreach ($install in $shadowInstalls) {
try {
Remove-Item $install.Path -Recurse -Force -ErrorAction Stop
Write-Host "✓ Removed: $($install.Path)" -ForegroundColor Green
# Log remediation action
Write-EventLog -LogName "Application" `
-Source "ClaudeCodeSecurity" `
-EventId 2000 `
-EntryType Warning `
-Message "Removed unauthorized Claude Code installation: $($install.Path) (User: $($install.User))"
} catch {
Write-Host "✗ Failed to remove: $($install.Path) - $_" -ForegroundColor Red
}
}
}
} else {
Write-Host "✓ No shadow installations detected" -ForegroundColor Green
}
return $shadowInstalls
}
# Run scan
Find-ShadowClaudeInstallations -AlertSecurity
Scheduled Task for Continuous Monitoring:
# Create scheduled task to run daily
$action = New-ScheduledTaskAction -Execute "powershell.exe" `
-Argument "-ExecutionPolicy Bypass -File C:\ProgramData\ClaudeCode\scripts\Find-ShadowClaudeInstallations.ps1 -AlertSecurity -RemoveUnauthorized"
$trigger = New-ScheduledTaskTrigger -Daily -At "3:00AM"
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest
$settings = New-ScheduledTaskSettingsSet -StartWhenAvailable -RunOnlyIfNetworkAvailable
Register-ScheduledTask -TaskName "Claude Code - Shadow Installation Detection" `
-Action $action `
-Trigger $trigger `
-Principal $principal `
-Settings $settings `
-Description "Detects and removes unauthorized Claude Code installations"
Write-Host "✓ Scheduled task created for daily shadow installation scans" -ForegroundColor Green
Layer 4: Process Monitoring
Detect node.exe running Claude from unauthorized paths:
<#
.SYNOPSIS
Monitors for Claude Code processes running from unauthorized locations
#>
function Monitor-ClaudeProcesses {
[CmdletBinding()]
param(
[switch]$KillUnauthorized,
[int]$MonitorIntervalSeconds = 60
)
$approvedPath = "C:\ProgramData\ClaudeCode\npm-global"
Write-Host "Monitoring Claude Code processes (Interval: $MonitorIntervalSeconds seconds)..." -ForegroundColor Cyan
Write-Host "Press Ctrl+C to stop" -ForegroundColor Gray
while ($true) {
# Find all node.exe processes
$nodeProcesses = Get-Process -Name "node" -ErrorAction SilentlyContinue
foreach ($proc in $nodeProcesses) {
try {
$commandLine = (Get-CimInstance Win32_Process -Filter "ProcessId = $($proc.Id)").CommandLine
# Check if running Claude Code
if ($commandLine -match "claude-code|@anthropic-ai") {
$executablePath = $proc.Path
# Check if from approved location
if ($executablePath -notlike "$approvedPath\*" -and
$commandLine -notlike "*$approvedPath*") {
Write-Host "✗ UNAUTHORIZED CLAUDE PROCESS DETECTED!" -ForegroundColor Red
Write-Host " PID: $($proc.Id)" -ForegroundColor Red
Write-Host " User: $($proc.StartInfo.UserName)" -ForegroundColor Red
Write-Host " Path: $executablePath" -ForegroundColor Red
Write-Host " Command: $commandLine" -ForegroundColor Red
Write-Host ""
# Log to Event Log
Write-EventLog -LogName "Application" `
-Source "ClaudeCodeSecurity" `
-EventId 3000 `
-EntryType Warning `
-Message "Unauthorized Claude Code process detected: PID $($proc.Id), Path: $executablePath, Command: $commandLine"
# Alert security
$alertMessage = @{
severity = "CRITICAL"
title = "Unauthorized Claude Code Process Running"
pid = $proc.Id
user = $env:USERNAME
computer = $env:COMPUTERNAME
path = $executablePath
command = $commandLine
timestamp = (Get-Date).ToUniversalTime().ToString("o")
} | ConvertTo-Json -Depth 10
try {
Invoke-RestMethod -Uri "https://siem.corp.example.com/api/alerts" `
-Method Post `
-Body $alertMessage `
-ContentType "application/json" `
-TimeoutSec 5
} catch {
Write-Warning "Failed to send alert: $_"
}
# Kill process if requested
if ($KillUnauthorized) {
Write-Host " Terminating unauthorized process..." -ForegroundColor Yellow
Stop-Process -Id $proc.Id -Force -ErrorAction SilentlyContinue
Write-Host " ✓ Process terminated" -ForegroundColor Green
}
}
}
} catch {
# Process may have exited, continue
}
}
Start-Sleep -Seconds $MonitorIntervalSeconds
}
}
# Run in background
Monitor-ClaudeProcesses -KillUnauthorized
Sysmon Configuration for Advanced Monitoring:
<Sysmon schemaversion="4.90">
<EventFiltering>
<!-- Monitor process creation for Claude Code -->
<ProcessCreate onmatch="include">
<Rule groupRelation="and">
<CommandLine condition="contains">claude-code</CommandLine>
<Image condition="excludes">C:\ProgramData\ClaudeCode\npm-global\</Image>
</Rule>
<Rule groupRelation="and">
<CommandLine condition="contains">@anthropic-ai</CommandLine>
<Image condition="excludes">C:\ProgramData\ClaudeCode\npm-global\</Image>
</Rule>
</ProcessCreate>
<!-- Monitor file creation in npm directories -->
<FileCreate onmatch="include">
<Rule groupRelation="or">
<TargetFilename condition="contains">\AppData\Roaming\npm\node_modules\@anthropic-ai\</TargetFilename>
<TargetFilename condition="contains">\AppData\Local\npm\node_modules\@anthropic-ai\</TargetFilename>
</Rule>
</FileCreate>
<!-- Monitor registry changes for npm config -->
<RegistryEvent onmatch="include">
<Rule groupRelation="or">
<TargetObject condition="contains">SOFTWARE\npm</TargetObject>
<TargetObject condition="contains">SOFTWARE\Node.js</TargetObject>
</Rule>
</RegistryEvent>
</EventFiltering>
</Sysmon>
Install Sysmon:
# Download Sysmon
# https://learn.microsoft.com/en-us/sysinternals/downloads/sysmon
# Install with configuration
Sysmon64.exe -accepteula -i C:\ProgramData\ClaudeCode\config\sysmon-claude-monitoring.xml
# Verify installation
Get-Service Sysmon64
Layer 5: Defender ATP Custom Detection Rules
Advanced hunting queries for shadow installations:
// Query 1: Detect npm install of Claude Code in user directories
DeviceProcessEvents
| where Timestamp > ago(24h)
| where ProcessCommandLine has "npm install"
and ProcessCommandLine has_any ("@anthropic-ai/claude-code", "claude-code")
| where FolderPath !startswith "C:\\ProgramData\\ClaudeCode"
| where FolderPath has_any ("AppData\\Roaming", "AppData\\Local", "Users")
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, FolderPath, InitiatingProcessCommandLine
| order by Timestamp desc
// Query 2: Detect Claude Code execution from unauthorized paths
DeviceProcessEvents
| where Timestamp > ago(1h)
| where ProcessCommandLine has_any ("claude-code", "@anthropic-ai")
| where FolderPath !startswith "C:\\ProgramData\\ClaudeCode"
| extend PathType = case(
FolderPath contains "AppData\\Roaming", "AppData Roaming",
FolderPath contains "AppData\\Local", "AppData Local",
FolderPath contains "\\Users\\", "User Directory",
"Other"
)
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, FolderPath, PathType
| order by Timestamp desc
// Query 3: Detect changes to npm configuration files
DeviceFileEvents
| where Timestamp > ago(7d)
| where FileName in~ (".npmrc", "npmrc")
| where FolderPath has_any ("AppData\\Roaming", "Users")
| where ActionType in ("FileCreated", "FileModified")
| project Timestamp, DeviceName, AccountName, FileName, FolderPath, ActionType
| order by Timestamp desc
Create Custom Detection Rule in Defender ATP:
// Custom Detection: Shadow Claude Code Installation
DeviceProcessEvents
| where Timestamp > ago(1h)
| where ProcessCommandLine has "npm install" and ProcessCommandLine has "@anthropic-ai/claude-code"
| where FolderPath !startswith "C:\\ProgramData\\ClaudeCode"
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, FolderPath
Alert Configuration:
- Severity: High
- Category: Unauthorized Software
- Recommended Action: Investigate immediately, terminate process, remove installation
- Notify: Security Operations Center
Layer 6: Network-Level Enforcement
Even local installations can be blocked via network controls:
<#
.SYNOPSIS
Network-level enforcement to block Claude API access from unauthorized installations
#>
# Approach 1: Client Certificate Requirement
# Configure corporate proxy to require client certificates for api.anthropic.com
# Only processes running from C:\ProgramData\ClaudeCode have access to certificate
# Certificate deployment script
$certPath = "C:\ProgramData\ClaudeCode\certs\claude-client.pfx"
$certPassword = (Get-Content "C:\ProgramData\ClaudeCode\secrets\cert-password.txt" | ConvertTo-SecureString -AsPlainText -Force)
# Install certificate to Computer store (not User store)
Import-PfxCertificate -FilePath $certPath -CertStoreLocation Cert:\LocalMachine\My -Password $certPassword
# Bind certificate to approved Claude installation
# Only node.exe from approved path can access certificate
# Approach 2: Host-based Firewall with Path Filtering
# Block outbound connections to api.anthropic.com except from approved path
# Create firewall rule with program path restriction
New-NetFirewallRule -DisplayName "Claude Code - API Access (Approved)" `
-Direction Outbound `
-Program "C:\Program Files\nodejs\node.exe" `
-Action Allow `
-RemoteAddress "api.anthropic.com" `
-Protocol TCP `
-RemotePort 443 `
-Service "*" `
-Description "Allow Claude API access only via approved installation"
# Note: This allows ALL node.exe processes, but combined with AppLocker
# preventing node.exe from running Claude except in approved path, provides defense-in-depth
# Approach 3: Proxy Authentication Tied to Installation Path
# Corporate proxy configuration example (pseudo-code)
<#
Proxy Rule:
IF (destination == api.anthropic.com) THEN
IF (source_process_path == "C:\ProgramData\ClaudeCode\npm-global\*") THEN
ALLOW with authentication
ELSE
DENY with message "Unauthorized Claude Code installation"
END IF
END IF
#>
DNS Filtering:
# Block api.anthropic.com at DNS level
# Allow only from approved IP addresses / MAC addresses of managed systems
# Example: Windows DNS Server configuration
Add-DnsServerQueryResolutionPolicy -Name "Block-Claude-Unauthorized" `
-Action DENY `
-Fqdn "EQ,*.anthropic.com" `
-ClientSubnet "NE,192.168.100.0/24" # Enterprise network
Layer 7: Controlled Folder Access
Windows Defender protection against AppData writes:
# Enable Controlled Folder Access
Set-MpPreference -EnableControlledFolderAccess Enabled
# Protect npm directories from unauthorized writes
Add-MpPreference -ControlledFolderAccessProtectedFolders "$env:APPDATA\npm"
Add-MpPreference -ControlledFolderAccessProtectedFolders "$env:LOCALAPPDATA\npm"
Add-MpPreference -ControlledFolderAccessProtectedFolders "$env:USERPROFILE\.npm"
# Allow only approved installers
# Windows Installer, System processes automatically allowed
# This prevents npm.exe from writing to protected folders unless whitelisted
# Verify configuration
Get-MpPreference | Select-Object EnableControlledFolderAccess, ControlledFolderAccessProtectedFolders
13.3 Automated Detection & Remediation
Comprehensive Security Script:
<#
.SYNOPSIS
Complete security enforcement for Claude Code shadow installations
.DESCRIPTION
Combines detection, alerting, remediation, and reporting
.EXAMPLE
.\Enforce-ClaudeCodeSecurity.ps1 -Remediate -Alert
#>
[CmdletBinding()]
param(
[switch]$Remediate,
[switch]$Alert,
[switch]$GenerateReport
)
# Configuration
$config = @{
ApprovedPath = "C:\ProgramData\ClaudeCode\npm-global"
LogPath = "C:\ProgramData\ClaudeCode\logs"
SiemEndpoint = "https://siem.corp.example.com/api/alerts"
}
function Test-NpmConfiguration {
Write-Host "[1/5] Checking npm configuration..." -ForegroundColor Cyan
$issues = @()
# Check global npmrc
$globalNpmRc = "C:\Program Files\nodejs\npmrc"
if (Test-Path $globalNpmRc) {
$content = Get-Content $globalNpmRc -Raw
if ($content -notmatch "prefix=.*ClaudeCode") {
$issues += "Global npmrc does not point to enterprise location"
}
$isReadOnly = (Get-ItemProperty $globalNpmRc).IsReadOnly
if (-not $isReadOnly) {
$issues += "Global npmrc is not read-only"
}
} else {
$issues += "Global npmrc not found"
}
# Check for user npmrc overrides
if (Test-Path "$env:USERPROFILE\.npmrc") {
$issues += "User has custom .npmrc (may override global settings)"
}
return $issues
}
function Find-ShadowInstallations {
Write-Host "[2/5] Scanning for shadow installations..." -ForegroundColor Cyan
$shadowInstalls = @()
$scanPaths = @(
"$env:APPDATA\npm\node_modules\@anthropic-ai\claude-code",
"$env:LOCALAPPDATA\npm\node_modules\@anthropic-ai\claude-code"
)
foreach ($path in $scanPaths) {
if (Test-Path $path) {
$shadowInstalls += @{
Path = $path
User = $env:USERNAME
Computer = $env:COMPUTERNAME
Detected = (Get-Date).ToUniversalTime()
}
}
}
return $shadowInstalls
}
function Find-UnauthorizedProcesses {
Write-Host "[3/5] Checking for unauthorized processes..." -ForegroundColor Cyan
$unauthorized = @()
$nodeProcesses = Get-Process -Name "node" -ErrorAction SilentlyContinue
foreach ($proc in $nodeProcesses) {
try {
$commandLine = (Get-CimInstance Win32_Process -Filter "ProcessId = $($proc.Id)").CommandLine
if ($commandLine -match "claude-code|@anthropic-ai" -and
$commandLine -notmatch $config.ApprovedPath) {
$unauthorized += @{
PID = $proc.Id
CommandLine = $commandLine
Path = $proc.Path
}
}
} catch {
# Process may have exited
}
}
return $unauthorized
}
function Send-SecurityAlert {
param($AlertData)
if (-not $Alert) { return }
Write-Host "[4/5] Sending security alert..." -ForegroundColor Cyan
$alertPayload = $AlertData | ConvertTo-Json -Depth 10
try {
Invoke-RestMethod -Uri $config.SiemEndpoint `
-Method Post `
-Body $alertPayload `
-ContentType "application/json" `
-TimeoutSec 10
Write-Host "✓ Alert sent to SIEM" -ForegroundColor Green
} catch {
Write-Warning "Failed to send alert: $_"
}
}
function Invoke-Remediation {
param($ShadowInstalls, $UnauthorizedProcesses)
if (-not $Remediate) { return }
Write-Host "[5/5] Performing remediation..." -ForegroundColor Cyan
# Remove shadow installations
foreach ($install in $ShadowInstalls) {
try {
Remove-Item $install.Path -Recurse -Force -ErrorAction Stop
Write-Host "✓ Removed: $($install.Path)" -ForegroundColor Green
} catch {
Write-Host "✗ Failed to remove: $($install.Path)" -ForegroundColor Red
}
}
# Kill unauthorized processes
foreach ($proc in $UnauthorizedProcesses) {
try {
Stop-Process -Id $proc.PID -Force -ErrorAction Stop
Write-Host "✓ Terminated process: PID $($proc.PID)" -ForegroundColor Green
} catch {
Write-Host "✗ Failed to terminate: PID $($proc.PID)" -ForegroundColor Red
}
}
}
# Main execution
Write-Host "=== Claude Code Security Enforcement ===" -ForegroundColor Yellow
Write-Host ""
# Run checks
$npmIssues = Test-NpmConfiguration
$shadowInstalls = Find-ShadowInstallations
$unauthorizedProcs = Find-UnauthorizedProcesses
# Generate report
$report = @{
Timestamp = (Get-Date).ToUniversalTime().ToString("o")
Computer = $env:COMPUTERNAME
User = $env:USERNAME
NpmConfigurationIssues = $npmIssues
ShadowInstallations = $shadowInstalls
UnauthorizedProcesses = $unauthorizedProcs
RemediationPerformed = $Remediate
}
# Display results
Write-Host ""
Write-Host "=== Results ===" -ForegroundColor Yellow
Write-Host "npm Configuration Issues: $($npmIssues.Count)" -ForegroundColor $(if ($npmIssues.Count -eq 0) { "Green" } else { "Red" })
Write-Host "Shadow Installations: $($shadowInstalls.Count)" -ForegroundColor $(if ($shadowInstalls.Count -eq 0) { "Green" } else { "Red" })
Write-Host "Unauthorized Processes: $($unauthorizedProcs.Count)" -ForegroundColor $(if ($unauthorizedProcs.Count -eq 0) { "Green" } else { "Red" })
# Alert if violations found
if ($shadowInstalls.Count -gt 0 -or $unauthorizedProcs.Count -gt 0) {
Send-SecurityAlert -AlertData $report
}
# Remediate if requested
Invoke-Remediation -ShadowInstalls $shadowInstalls -UnauthorizedProcesses $unauthorizedProcs
# Save report
if ($GenerateReport) {
$reportPath = "$($config.LogPath)\security-enforcement-$(Get-Date -Format 'yyyyMMdd-HHmmss').json"
$report | ConvertTo-Json -Depth 10 | Out-File $reportPath
Write-Host ""
Write-Host "Report saved: $reportPath" -ForegroundColor Cyan
}
Write-Host ""
Write-Host "=== Enforcement Complete ===" -ForegroundColor Green
13.4 User Education & Policy
Acceptable Use Policy Template:
# Claude Code Acceptable Use Policy
## 1. Authorized Installation
- Claude Code MUST be installed via enterprise-managed deployment only
- Installation location: C:\ProgramData\ClaudeCode
- Local installations (AppData, user directories) are STRICTLY PROHIBITED
## 2. Prohibited Actions
The following actions are violations of corporate security policy:
❌ Installing Claude Code via `npm install -g` in user directories
❌ Running Claude Code from any location other than C:\ProgramData\ClaudeCode
❌ Modifying npm configuration to change global prefix
❌ Creating local .npmrc files to override enterprise settings
❌ Using `npx` to run Claude Code without enterprise controls
❌ Installing alternative versions or forks of Claude Code
## 3. Consequences
Violations will result in:
1. **First Offense**: Written warning, mandatory security training
2. **Second Offense**: Suspension of development tool access
3. **Third Offense**: Disciplinary action up to and including termination
4. **Data Breach**: Legal action, financial liability
## 4. Reporting
If you discover unauthorized Claude Code installations:
- Report to: security@company.com
- Incident hotline: x5555
- Anonymous reporting: https://ethics.company.com
## 5. Exceptions
Exceptions to this policy require:
- Written approval from CISO
- Business justification
- Risk assessment
- Documented security controls
## Acknowledgment
I have read and understand this policy. I agree to comply with all requirements.
Name: ________________ Date: __________ Signature: __________________
Training Presentation Outline:
- Why Shadow IT is Dangerous
- Real-world breach examples
- Cost of non-compliance (GDPR fines, SOC2 violations)
- Career impact
- How Enterprise Controls Protect You
- Audit trails for accountability
- Preventing accidental credential leaks
- Compliance with regulations
- How to Use Claude Code Correctly
- Verifying approved installation
- Accessing enterprise support
- Reporting issues
- What Happens If You Violate Policy
- Detection mechanisms (they WILL find out)
- HR consequences
- Legal implications
13.5 Testing & Validation
Penetration Test Scenarios:
<#
.SYNOPSIS
Penetration test for Claude Code shadow installation controls
.DESCRIPTION
Simulates attacker trying to bypass enterprise controls
RUN IN ISOLATED TEST ENVIRONMENT ONLY
#>
function Test-ShadowInstallationControls {
Write-Host "=== Claude Code Security Penetration Test ===" -ForegroundColor Yellow
Write-Host "WARNING: This test attempts to bypass security controls" -ForegroundColor Red
Write-Host "Run only in authorized test environment" -ForegroundColor Red
Write-Host ""
$results = @()
# Test 1: Attempt local npm install
Write-Host "[Test 1] Attempting npm install in AppData..." -ForegroundColor Cyan
try {
$output = npm install -g @anthropic-ai/claude-code --prefix=$env:APPDATA\npm 2>&1
if ($LASTEXITCODE -eq 0) {
$results += @{Test="Local npm install"; Result="VULNERABLE"; Details="Installation succeeded"}
} else {
$results += @{Test="Local npm install"; Result="PROTECTED"; Details="Installation blocked"}
}
} catch {
$results += @{Test="Local npm install"; Result="PROTECTED"; Details="Installation blocked: $_"}
}
# Test 2: Attempt to modify npm config
Write-Host "[Test 2] Attempting to modify npm prefix..." -ForegroundColor Cyan
try {
npm config set prefix "$env:LOCALAPPDATA\npm"
if ($LASTEXITCODE -eq 0) {
$results += @{Test="npm config modification"; Result="VULNERABLE"; Details="Config change succeeded"}
} else {
$results += @{Test="npm config modification"; Result="PROTECTED"; Details="Config change blocked"}
}
} catch {
$results += @{Test="npm config modification"; Result="PROTECTED"; Details="Config change blocked: $_"}
}
# Test 3: Attempt to run from unauthorized location
Write-Host "[Test 3] Attempting execution from AppData..." -ForegroundColor Cyan
$testPath = "$env:APPDATA\npm\claude.cmd"
if (Test-Path $testPath) {
try {
& $testPath --version 2>&1 | Out-Null
if ($LASTEXITCODE -eq 0) {
$results += @{Test="Unauthorized execution"; Result="VULNERABLE"; Details="Execution succeeded"}
} else {
$results += @{Test="Unauthorized execution"; Result="PROTECTED"; Details="Execution blocked by AppLocker"}
}
} catch {
$results += @{Test="Unauthorized execution"; Result="PROTECTED"; Details="Execution blocked: $_"}
}
} else {
$results += @{Test="Unauthorized execution"; Result="N/A"; Details="Test file not found"}
}
# Test 4: Detection capability
Write-Host "[Test 4] Testing detection mechanisms..." -ForegroundColor Cyan
$detectionWorks = $false
# Check if security script would detect test artifacts
# (Implementation depends on detection script)
$results += @{Test="Detection capability"; Result="CHECK LOGS"; Details="Review SIEM for alerts"}
# Display results
Write-Host ""
Write-Host "=== Test Results ===" -ForegroundColor Yellow
$results | Format-Table -AutoSize
# Overall assessment
$vulnerable = ($results | Where-Object { $_.Result -eq "VULNERABLE" }).Count
if ($vulnerable -eq 0) {
Write-Host "✓ All tests passed - Controls are effective" -ForegroundColor Green
} else {
Write-Host "✗ $vulnerable vulnerabilities found - Review controls" -ForegroundColor Red
}
}
# Run test
Test-ShadowInstallationControls
13.6 Deployment Checklist
- [ ] npm Configuration Lockdown
- [ ] Global npmrc configured and read-only
- [ ] Registry policy to disable user npmrc
- [ ] Deployed via Group Policy
- [ ] AppLocker Rules
- [ ] Deny rules for AppData\npm
- [ ] Deny rules for LocalAppData\npm
- [ ] Allow rules only for ProgramData\ClaudeCode
- [ ] Application Identity service enabled
- [ ] Policy deployed and enforced
- [ ] File System Auditing
- [ ] Audit policies enabled (auditpol)
- [ ] Audit ACLs configured on npm directories
- [ ] Event log forwarding to SIEM configured
- [ ] Process Monitoring
- [ ] Detection script deployed
- [ ] Scheduled task created (daily scan)
- [ ] Sysmon installed and configured
- [ ] Monitoring service running
- [ ] Defender ATP
- [ ] Custom detection rules created
- [ ] Alert notifications configured
- [ ] Automated response actions enabled
- [ ] Network Controls
- [ ] Firewall rules deployed
- [ ] Proxy authentication configured (if applicable)
- [ ] DNS filtering enabled (if applicable)
- [ ] Controlled Folder Access
- [ ] Feature enabled
- [ ] Protected folders configured
- [ ] Allowed applications verified
- [ ] User Education
- [ ] Acceptable Use Policy distributed
- [ ] Training sessions conducted
- [ ] Policy acknowledgment collected
- [ ] Testing
- [ ] Penetration test performed
- [ ] All controls verified effective
- [ ] Detection mechanisms validated
- [ ] Remediation tested
Conclusion
This comprehensive guide provides enterprise organizations with a complete security framework for deploying Claude Code on Windows. By implementing:
✅ Secure Installation in non-writable paths (ProgramData)
✅ Managed Policies with immutable, centrally-controlled configurations
✅ Hooks-Based Controls for runtime security enforcement
✅ Sensitive File Protection blocking access to credentials and keys
✅ Windows System Protection preventing unauthorized system modifications
✅ Network Security via proxy, firewall, and TLS configurations
✅ DevContainer Isolation for untrusted code environments
✅ Comprehensive Auditing with SIEM integration and compliance reporting
✅ Windows Security Integration leveraging AppLocker, WDAC, and Defender
✅ Shadow Installation Prevention with 7-layer detection and blocking strategy
Organizations can confidently deploy AI-powered development tools while maintaining zero-trust security posture, regulatory compliance, and full audit visibility.
Critical Security Achievement
This guide addresses the most sophisticated threat vector: developers attempting to bypass enterprise controls through local installations. The multi-layer prevention strategy ensures that even determined users cannot circumvent security policies through:
- npm configuration lockdown
- AppLocker path-based execution blocking
- File system auditing and monitoring
- Real-time process detection and termination
- Defender ATP behavioral analysis
- Network-level access control
- Controlled Folder Access protection
By implementing these controls, organizations achieve defense-in-depth where bypassing one layer triggers detection and remediation at other layers, creating a comprehensive security mesh that protects against both accidental misuse and intentional policy violations.
Additional Resources
Official Documentation:
Microsoft Security:
Compliance Frameworks:
Enterprise AI Security:
Leave a Comment