Generic Sensitive Data Leakage Detection
Core Principle
IF data MATCHES sensitive_pattern
AND data FLOWS TO output_sink
THEN potential_leak
This skill detects credentials, secrets, and sensitive data flowing to logging, error messages, HTTP responses, or any other output - regardless of which libraries the codebase uses.
Phase 1: Identify Sensitive Data (Sources)
1.1 Sensitive Naming Patterns
Search for variables, fields, parameters with these patterns:
Go:
grep -rniE "(secret|password|passwd|pwd|apikey|api_key|token|credential|private.?key|access.?key|auth.?token|bearer|encryption.?key|signing.?key|client.?secret|consumer.?secret|conn.?str|connection.?string)" --include="*.go" | grep -v "_test\.go"
Python:
grep -rniE "(secret|password|passwd|pwd|apikey|api_key|token|credential|private.?key|access.?key|auth.?token|bearer)" --include="*.py" | grep -v "test_"
Java:
grep -rniE "(secret|password|passwd|pwd|apiKey|api_key|token|credential|privateKey|accessKey|authToken|bearer)" --include="*.java" | grep -v "Test\.java"
JavaScript/TypeScript:
grep -rniE "(secret|password|passwd|pwd|apiKey|api_key|token|credential|privateKey|accessKey|authToken|bearer)" --include="*.js" --include="*.ts" | grep -v "\.test\." | grep -v "\.spec\."
1.2 Sensitive Function Returns
# Functions that return/fetch secrets (Go)
grep -rniE "func.*(Get|Read|Fetch|Load|Decrypt|Retrieve).*(Secret|Password|Key|Token|Cred)" --include="*.go"
# Assignments from credential functions
grep -rniE "(secret|password|key|token|cred).*:?=.*(Get|Read|Fetch|Load|Decrypt|Retrieve)" --include="*.go"
1.3 Sensitive Struct/Class Fields
# Go struct fields
grep -rniE "^\s+(Secret|Password|Key|Token|Credential|ApiKey|PrivateKey|AccessKey)\s+\S+" --include="*.go"
# Python class attributes
grep -rniE "self\.(secret|password|key|token|credential|api_key)" --include="*.py"
# Java fields
grep -rniE "(private|protected|public)\s+\S+\s+(secret|password|key|token|credential)" --include="*.java"
1.4 Environment Variables
# Go
grep -rniE "os\.Getenv\([\"'].*?(SECRET|PASSWORD|KEY|TOKEN|CREDENTIAL|API_KEY)" --include="*.go"
# Python
grep -rniE "os\.environ\.get\([\"'].*?(SECRET|PASSWORD|KEY|TOKEN|CREDENTIAL|API_KEY)" --include="*.py"
# Node.js
grep -rniE "process\.env\.(SECRET|PASSWORD|KEY|TOKEN|CREDENTIAL|API_KEY)" --include="*.js" --include="*.ts"
Phase 2: Identify Output Sinks
2.1 Discover Logging Library Used
# Go - find log imports
grep -rniE "^import|^\t\"" --include="*.go" | grep -iE "log|zap|logrus|zerolog|klog|glog|slog" | head -10
# Find actual log function calls
grep -rhoE "\w+\.(Error|Info|Debug|Warn|Fatal|Print|Log|Msg)(f|ln|w|Context)?\s*\(" --include="*.go" | sort | uniq -c | sort -rn | head -20
2.2 All Logging Calls (Generic)
# Matches ANY logging library
grep -rniE "\.(log|print|error|warn|info|debug|fatal|trace|notice|output|write|emit|send|record)(f|ln|w)?\s*\(" --include="*.go" --include="*.py" --include="*.java" --include="*.js"
2.3 Error Creation/Wrapping
# Go
grep -rniE "(fmt\.Errorf|errors\.New|errors\.Wrap|errors\.Wrapf|fmt\.Sprintf.*[Ee]rr)" --include="*.go"
# Python
grep -rniE "(raise\s+\w+Exception|raise\s+\w+Error)" --include="*.py"
# Java
grep -rniE "throw\s+new\s+\w+Exception" --include="*.java"
2.4 HTTP Responses
# Go
grep -rniE "(\.Write\(|\.WriteString\(|json\.Encode|\.JSON\(|c\.String\(|w\.Write)" --include="*.go"
# Python (Flask/Django)
grep -rniE "(jsonify|JsonResponse|Response\(|return.*json)" --include="*.py"
# Node.js
grep -rniE "(res\.send|res\.json|res\.write|response\.send)" --include="*.js" --include="*.ts"
Phase 3: Find Dangerous Intersections
3.1 Sensitive Variable in Log Call
# Direct pattern - sensitive var name in log arguments
grep -rniE "(log|print|error|warn|info|debug|fatal)\w*\(.*\b(secret|password|key|token|cred|apikey)\w*\b" --include="*.go" | grep -v "_test\.go"
3.2 Format String Struct Dumps (%v, %+v, %#v)
# These format verbs dump ALL struct fields including secrets
grep -rniE "%[+#]?v" --include="*.go" | grep -v "_test\.go"
# More specific - %v with config/options types
grep -rniE "(Error|Info|Debug|Warn|Print|Log)(f|w)?\(.*%[+#]?v.*(config|option|session|setting|client|request)" --include="*.go"
3.3 Sensitive Data Passed to Format Functions
# Sensitive variable as argument to printf-style function
grep -rniE "(printf|errorf|sprintf|infof|debugf|warnf|fatalf)\([^)]+,\s*\w*(secret|password|key|token|cred)" --include="*.go"
3.4 Error Returns Containing Secrets
# Functions returning errors with sensitive data
grep -rniE "return.*(fmt\.Errorf|errors\.).*%(v|s|w).*\w*(secret|password|key|token|config|opt)" --include="*.go"
Phase 4: Contextual Analysis (Critical for Avoiding False Positives)
For each finding, verify:
| Check | Question | How to Verify |
|---|---|---|
| Is it actually sensitive? | Not a map key, keyboard key, or generic "key" | Check variable usage context |
| Does it reach output? | Trace variable through code to log/response | Follow data flow |
| Has safe String() method? | Struct implements fmt.Stringer that redacts secrets? | grep -A10 "func (.*TypeName) String()" |
| Format verb? | Using %+v/%#v? (these bypass String() methods) | Check format string - %s and %v use String() |
| Is it SDK error? | SDK errors rarely contain config structs | Don't flag SDK error logging by default |
| Log level? | Debug logs may be disabled in prod | Lower severity for debug-only |
Critical: Always Check for String() Method
Before flagging any struct being logged:
# For a struct named "Server" or "Config":
grep -rn "func (.*Server) String()" --include="*.go"
grep -rn "func (.*Config) String()" --include="*.go"
If a safe String() method exists that omits credentials → NOT a vulnerability (unless %+v or %#v is used)
Phase 5: Common Vulnerable Patterns
Pattern 1: Direct Struct Dump with Credentials
// VULNERABLE: struct with credentials logged directly
type Config struct {
Region string
SecretKey string // Sensitive!
}
log.Warnf("Config issue: %+v", config) // Dumps ALL fields including SecretKey
log.Warnf("Server error: %s", server) // ONLY vulnerable if Server lacks safe String() method
IMPORTANT: Before flagging struct logging, check if the struct has a custom String() method:
# Check for safe String() implementation
grep -A5 "func (.*TypeName) String()" --include="*.go"
If the struct has a String() method that omits sensitive fields, logging with %s or %v is SAFE.
Pattern 2: Config Struct Dump
// VULNERABLE: config.SecretKey exposed
log.Debugf("Using config: %+v", config)
Pattern 3: Request Logging
// VULNERABLE: Authorization header exposed
log.Infof("Request: %+v", req)
log.Infof("Headers: %v", req.Header)
Pattern 4: Error Chain Propagation
// VULNERABLE: secret propagates up call stack
err := connectWithSecret(secretKey)
return fmt.Errorf("connection failed: %w", err) // wraps error containing secret
Pattern 5: Response Body Logging
// VULNERABLE: response may contain tokens
body, _ := ioutil.ReadAll(resp.Body)
log.Debugf("Response: %s", body) // May contain access_token, refresh_token
Quick Scan Commands
All-in-One Scan (Go)
#!/bin/bash
echo "=== Sensitive Data Leakage Scan ==="
echo -e "\n[1] Sensitive identifiers in log calls:"
grep -rniE "(log|print|error|warn|info|debug|fatal)\w*\([^)]*\b(secret|password|key|token|cred|apikey)\w*" --include="*.go" | grep -v "_test\.go" | head -20
echo -e "\n[2] Struct dumps with %v/%+v:"
grep -rniE "(Error|Info|Debug|Warn|Print)(f)?\([^)]*%[+#]?v" --include="*.go" | grep -v "_test\.go" | he