agentsclimarketplace

Code reaper

Skill mlvpatel/sentinel-ai-offensive/skills/code-reaper

Static Application Security Testing (SAST) — deep source code analysis for vulnerability detection. Covers 12 languages (JavaScript/TypeScript, Python, PHP, Go, Ruby, Rust, Java, C#, Solidity, Kotlin, Swift, Dart). Automated scanning (Semgrep rulesets, Gitleaks, Trufflehog), manual pattern hunting (dangerous sinks, auth inconsistency, injection vectors), taint analysis (source→sink tracing), auth architecture audit (middleware gaps, IDOR patterns, privilege escalation), secret detection (API keys, tokens, credentials), dependency analysis (known CVEs in packages), and framework-specific vulnerability patterns. Generates prioritized findings with exploitability assessment. Use when asked to "review this code", "audit source code", "find vulnerabilities in code", "static analysis", "code security review", "SAST scan", or when source code is available for a target.From its SKILL.md

Install
npx -y skills add mlvpatel/sentinel-ai-offensive --skill code-reaper

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

28.6 KB, as published. Nobody here has run it

SAST — Static Application Security Testing

Deep source code analysis. Find bugs that scanners miss by understanding code flow, auth patterns, and developer psychology.


THE SAST MINDSET

SAST is not "run a tool and read output." It's understanding HOW the code works, WHERE trust boundaries exist, and WHAT happens when assumptions break.

Three Questions Before Every SAST Engagement

  1. "Where does user input enter the system?" → Sources (HTTP params, headers, files, WebSocket, env vars)
  2. "Where does dangerous processing happen?" → Sinks (SQL queries, exec(), template rendering, file I/O)
  3. "What stands between source and sink?" → Sanitizers (validation, encoding, parameterization)

If source reaches sink without adequate sanitizer → VULNERABILITY.


PHASE 1: CODEBASE RECONNAISSANCE (5 min)

1A: Technology Stack & Architecture

# Detect languages & frameworks
find . -type f \( -name "*.py" -o -name "*.js" -o -name "*.ts" -o -name "*.go" \
  -o -name "*.rb" -o -name "*.php" -o -name "*.java" -o -name "*.rs" \
  -o -name "*.cs" -o -name "*.kt" -o -name "*.sol" -o -name "*.swift" \) \
  | grep -v node_modules | grep -v vendor | grep -v __pycache__ | grep -v .git \
  | sed 's/.*\.//' | sort | uniq -c | sort -rn

# Count lines of code per language
find . -type f -name "*.py" | grep -v node_modules | xargs wc -l 2>/dev/null | tail -1
find . -type f -name "*.js" -o -name "*.ts" | grep -v node_modules | xargs wc -l 2>/dev/null | tail -1

# Detect package manager & dependencies
ls package.json requirements.txt Pipfile Gemfile go.mod Cargo.toml composer.json pom.xml build.gradle 2>/dev/null

# Detect framework
grep -l "express\|koa\|fastify\|hapi" package.json 2>/dev/null && echo "Node.js framework detected"
grep -l "django\|flask\|fastapi\|tornado" requirements.txt Pipfile 2>/dev/null && echo "Python framework detected"
grep -l "laravel\|symfony\|slim" composer.json 2>/dev/null && echo "PHP framework detected"
grep -l "gin\|echo\|fiber\|chi" go.mod 2>/dev/null && echo "Go framework detected"
grep -l "rails\|sinatra" Gemfile 2>/dev/null && echo "Ruby framework detected"
grep -l "spring" pom.xml build.gradle 2>/dev/null && echo "Java/Spring detected"

1B: Security-Relevant File Discovery

# Configuration files (often contain secrets or misconfigs)
find . -name ".env*" -o -name "config*.json" -o -name "config*.yml" \
  -o -name "secrets*" -o -name "credentials*" -o -name "*.pem" -o -name "*.key" \
  2>/dev/null | grep -v node_modules

# Auth-related files
find . -type f | grep -viE "node_modules|vendor|test|spec|__pycache__|\.git" \
  | xargs grep -liE "authenticate|authorize|login|session|jwt|oauth|token|permission|role|acl" 2>/dev/null \
  | head -30

# Route definitions
find . -type f | grep -viE "node_modules|vendor|test|spec|__pycache__|\.git" \
  | xargs grep -liE "router\.|app\.(get|post|put|delete)|@app\.route|Route::|@GetMapping|@PostMapping" 2>/dev/null \
  | head -30

# Database interaction files
find . -type f | grep -viE "node_modules|vendor|test|spec|__pycache__|\.git" \
  | xargs grep -liE "query|execute|find|select|insert|update|delete|\.where|\.raw" 2>/dev/null \
  | head -30

1C: Git History Security Audit

# Security-related commits
git log --oneline --all -30 | grep -iE "security|fix|CVE|vuln|patch|auth|xss|sqli|ssrf|csrf|inject"

# Recently changed auth/security files
git log --name-only --since="30 days ago" --pretty=format:"" \
  | sort -u | grep -iE "auth|security|middleware|permission|token|session" | head -20

# Developer breadcrumbs in commit messages
git log --oneline -50 | grep -iE "TODO|FIXME|HACK|temp|workaround|disable|skip"

# Secrets that were committed then removed
git log --all --diff-filter=D -- "*.env" "*.pem" "*.key" "credentials*" "secrets*" 2>/dev/null | head -10

PHASE 2: AUTOMATED SCANNING (15 min)

2A: Semgrep — Rule-Based Static Analysis

# Install
pip3 install semgrep 2>/dev/null

# ═══════════════════════════════════════════
# TIER 1: Broad Security Audit (run first)
# ═══════════════════════════════════════════
semgrep --config=p/security-audit ./ --json -o /tmp/sast-security-audit.json 2>/dev/null
semgrep --config=p/owasp-top-ten ./ --json -o /tmp/sast-owasp.json 2>/dev/null

# ═══════════════════════════════════════════
# TIER 2: Language-Specific (run for detected languages)
# ═══════════════════════════════════════════

# JavaScript / TypeScript
semgrep --config=p/javascript ./ --json -o /tmp/sast-js.json 2>/dev/null
semgrep --config=p/typescript ./ --json -o /tmp/sast-ts.json 2>/dev/null
semgrep --config=p/nodejs ./ --json -o /tmp/sast-node.json 2>/dev/null
semgrep --config=p/react ./ --json -o /tmp/sast-react.json 2>/dev/null
semgrep --config=p/nextjs ./ --json -o /tmp/sast-nextjs.json 2>/dev/null

# Python
semgrep --config=p/python ./ --json -o /tmp/sast-python.json 2>/dev/null
semgrep --config=p/django ./ --json -o /tmp/sast-django.json 2>/dev/null
semgrep --config=p/flask ./ --json -o /tmp/sast-flask.json 2>/dev/null

# PHP
semgrep --config=p/php ./ --json -o /tmp/sast-php.json 2>/dev/null
semgrep --config=p/laravel ./ --json -o /tmp/sast-laravel.json 2>/dev/null

# Go
semgrep --config=p/golang ./ --json -o /tmp/sast-go.json 2>/dev/null

# Java
semgrep --config=p/java ./ --json -o /tmp/sast-java.json 2>/dev/null
semgrep --config=p/spring ./ --json -o /tmp/sast-spring.json 2>/dev/null

# Ruby
semgrep --config=p/ruby ./ --json -o /tmp/sast-ruby.json 2>/dev/null
semgrep --config=p/rails ./ --json -o /tmp/sast-rails.json 2>/dev/null

# Rust
semgrep --config=p/rust ./ --json -o /tmp/sast-rust.json 2>/dev/null

# Solidity (Web3)
semgrep --config=p/solidity ./ --json -o /tmp/sast-sol.json 2>/dev/null

# ═══════════════════════════════════════════
# TIER 3: Targeted Vulnerability Rules
# ═══════════════════════════════════════════
semgrep --config=p/sql-injection ./ --json -o /tmp/sast-sqli.json 2>/dev/null
semgrep --config=p/xss ./ --json -o /tmp/sast-xss.json 2>/dev/null
semgrep --config=p/jwt ./ --json -o /tmp/sast-jwt.json 2>/dev/null
semgrep --config=p/command-injection ./ --json -o /tmp/sast-cmdi.json 2>/dev/null
semgrep --config=p/insecure-transport ./ --json -o /tmp/sast-transport.json 2>/dev/null
semgrep --config=p/secrets ./ --json -o /tmp/sast-secrets.json 2>/dev/null

# ═══════════════════════════════════════════
# PARSE RESULTS — Extract critical findings
# ═══════════════════════════════════════════
for f in /tmp/sast-*.json; do
  COUNT=$(cat "$f" 2>/dev/null | jq '.results | length' 2>/dev/null)
  [ "$COUNT" -gt 0 ] 2>/dev/null && echo "$(basename $f .json): $COUNT findings"
done

# Show critical + high severity findings
for f in /tmp/sast-*.json; do
  cat "$f" 2>/dev/null | jq -r '.results[] | select(.extra.severity == "ERROR") |
    "\(.extra.severity) | \(.path):\(.start.line) | \(.check_id) | \(.extra.message)"' 2>/dev/null
done | sort -u | head -50

2B: Custom Semgrep Patterns (High-Signal)

# SQL string concatenation in Python
semgrep --pattern 'cursor.execute("..." + $X)' --lang python . 2>/dev/null
semgrep --pattern 'cursor.execute(f"...$X...")' --lang python . 2>/dev/null

# Unsanitized template rendering
semgrep --pattern 'render_template_string($X)' --lang python . 2>/dev/null
semgrep --pattern 'res.render($TEMPLATE, {..., $KEY: req.$SOURCE})' --lang javascript . 2>/dev/null

# Dangerous deserialization
semgrep --pattern 'pickle.loads($X)' --lang python . 2>/dev/null
semgrep --pattern 'yaml.load($X)' --lang python . 2>/dev/null
semgrep --pattern 'JSON.parse(req.$X)' --lang javascript . 2>/dev/null

# Missing auth check pattern (Express)
semgrep --pattern 'router.$METHOD($PATH, $HANDLER)' --lang javascript . 2>/dev/null \
  | grep -v "auth\|middleware\|protect\|verify"

# eval() with user input
semgrep --pattern 'eval($X)' --lang javascript . 2>/dev/null
semgrep --pattern 'eval($X)' --lang python . 2>/dev/null

2C: Secret Scanning (Automated)

# Trufflehog — ONLY verified secrets (no false positives)
trufflehog filesystem --only-verified ./ 2>/dev/null | tee /tmp/sast-verified-secrets.txt

# Trufflehog — Git history (finds deleted secrets)
trufflehog git file://. --only-verified 2>/dev/null | tee /tmp/sast-git-secrets.txt

# Gitleaks — comprehensive pattern matching
gitleaks detect --source . --report-path /tmp/sast-gitleaks.json --report-format json 2>/dev/null

# Manual high-value secret patterns
echo "=== AWS Keys ==="
grep -rn "AKIA[0-9A-Z]\{16\}" . --include="*.js" --include="*.ts" --include="*.py" --include="*.env" --include="*.json" | grep -v node_modules | grep -v ".git"

echo "=== Private Keys ==="
grep -rn "BEGIN.*PRIVATE KEY" . | grep -v node_modules | grep -v ".git" | grep -v test

echo "=== API Keys / Tokens ==="
grep -rn "sk-[a-zA-Z0-9]\{20,\}" . | grep -v node_modules | grep -v ".git"  # OpenAI
grep -rn "ghp_[a-zA-Z0-9]\{36\}" . | grep -v node_modules | grep -v ".git"  # GitHub PAT
grep -rn "xox[bps]-[a-zA-Z0-9-]\{10,\}" . | grep -v node_modules | grep -v ".git"  # Slack

echo "=== Hardcoded Passwords ==="
grep -rn "password\s*[:=]\s*['\"][^'\"]\{4,\}['\"]" . \
  --include="*.py" --include="*.js" --include="*.ts" --include="*.go" --include="*.rb" --include="*.php" \
  | grep -viE "test|example|placeholder|PASSWORD_HERE|changeme|node_modules|\.git" | head -20

echo "=== Database Connection Strings ==="
grep -rn "mongodb://\|postgres://\|mysql://\|redis://\|amqp://" . \
  | grep -v node_modules | grep -v ".git" | grep -v test | head -10

2D: Dependency Vulnerability Scan

# npm / Node.js
npm audit --json 2>/dev/null | jq '.vulnerabilities | to_entries[] | select(.value.severity == "critical" or .value.severity == "high") | {name:.key, severity:.value.severity, via:.value.via[0]}' 2>/dev/null

# Python (pip-audit)
pip-audit --format json 2>/dev/null | jq '.[] | select(.fix_versions != []) | {name:.name, version:.version, vuln:.id}' 2>/dev/null

# Go
govulncheck ./... 2>/dev/null

# Ruby
bundle audit check 2>/dev/null

# PHP
composer audit --format=json 2>/dev/null

PHASE 3: MANUAL PATTERN HUNTING (20 min)

This is where human skill beats automation. Scanners find syntax patterns. Humans find logic bugs, auth gaps, and developer shortcuts.

3A: Injection Sinks — Language by Language

JavaScript / TypeScript

# XSS sinks
grep -rn "innerHTML\|outerHTML\|document\.write\|dangerouslySetInnerHTML" \
  --include="*.js" --include="*.ts" --include="*.jsx" --include="*.tsx" \
  | grep -v node_modules | grep -v test

# DOM XSS — user-controlled data flowing to dangerous APIs
grep -rn "location\.hash\|location\.search\|document\.referrer\|window\.name" \
  --include="*.js" --include="*.ts" | grep -v node_modules

# Command injection
grep -rn "child_process\|execSync\|exec(\|spawn(\|execFile(" \
  --include="*.js" --include="*.ts" | grep -v node_modules | grep -v test

# Prototype pollution
grep -rn "__proto__\|constructor\[" --include="*.js" --include="*.ts" | grep -v node_modules
grep -rn "Object\.assign\|\.extend(\|merge(\|deepMerge\|lodash\.merge\|_.merge" \
  --include="*.js" --include="*.ts" | grep -v node_modules

# SQL injection (string concatenation in queries)
grep -rn "\.query(\|\.execute(\|\.raw(" --include="*.js" --include="*.ts" \
  | grep -v node_modules | grep -E "\+|template|concat|\$\{"

# SSRF — fetching user-supplied URLs
grep -rn "fetch(\|axios\.\(get\|post\)\|request(\|got(\|urllib" \
  --include="*.js" --include="*.ts" | grep -v node_modules \
  | grep "req\.\|params\.\|query\.\|body\.\|args\.\|input"

# PostMessage (DOM-based attacks)
grep -rn "postMessage\|addEventListener.*message" --include="*.js" --include="*.ts" \
  | grep -v node_modules

# Insecure randomness
grep -rn "Math\.random\(\)" --include="*.js" --include="*.ts" \
  | grep -vi "test\|node_modules" \
  | grep -i "token\|key\|secret\|password\|nonce\|salt\|id\|session"

Python

# SQL injection
grep -rn "execute(\|executemany(\|raw(" --include="*.py" \
  | grep -v test | grep -v __pycache__ \
  | grep -E "\+|format\(|f\"|%s|%d|\.format"

# Command injection
grep -rn "subprocess\|os\.system\|os\.popen\|commands\.getoutput" --include="*.py" \
  | grep -v test | grep -v __pycache__

# Dangerous deserialization
grep -rn "pickle\.loads\|pickle\.load\|yaml\.load[^_]\|yaml\.unsafe_load" --include="*.py" \
  | grep -v test
grep -rn "marshal\.loads\|shelve\.open" --include="*.py" | grep -v test

# SSTI
grep -rn "render_template_string\|Template(\|Markup(\|mark_safe\|safe" --include="*.py" \
  | grep -v test

# Path traversal
grep -rn "open(\|os\.path\.join\|send_file\|send_from_directory" --include="*.py" \
  | grep -v test | grep "request\.\|params\.\|args\.\|input"

# eval/exec
grep -rn "eval(\|exec(\|compile(\|__import__(" --include="*.py" | grep -v test

# Weak crypto
grep -rn "md5\|sha1\|DES\|RC4\|ECB" --include="*.py" \
  | grep -vi "test\|checksum\|etag\|hash_name\|spec"

PHP

# SQL injection
grep -rn "mysql_query\|mysqli_query\|->query(" --include="*.php" \
  | grep "\$_\|\..*\$\|concat\|\".*\.\""

# Type juggling (== instead of ===)
grep -rn "\s==\s" --include="*.php" | grep -i "password\|token\|hash\|secret\|key\|admin"

# Deserialization
grep -rn "unserialize(\|json_decode(" --include="*.php" | grep "\$_"

# LFI / Path traversal
grep -rn "include(\|require(\|include_once(\|require_once(\|file_get_contents(" \
  --include="*.php" | grep "\$_GET\|\$_POST\|\$_REQUEST"

# Command injection
grep -rn "system(\|exec(\|shell_exec(\|passthru(\|popen(\|proc_open(" \
  --include="*.php" | grep "\$_"

# eval
grep -rn "eval(\|assert(\|preg_replace.*e'" --include="*.php" | grep "\$_"

Go

# Template injection (unescaped output)
grep -rn "template\.HTML\|template\.JS\|template\.URL\|template\.CSS" --include="*.go"

# SQL injection
grep -rn "fmt\.Sprintf.*SELECT\|fmt\.Sprintf.*INSERT\|fmt\.Sprintf.*UPDATE\|fmt\.Sprintf.*DELETE" \
  --include="*.go"
grep -rn "db\.Query(\|db\.Exec(\|db\.QueryRow(" --include="*.go" | grep "fmt\.\|Sprintf\|\+"

# Command injection
grep -rn "exec\.Command(\|os\.StartProcess" --include="*.go" | grep -v test

# Race conditions
grep -rn "go func\|sync\.Mutex\|sync\.RWMutex\|atomic\." --include="*.go" | head -20

# Path traversal
grep -rn "filepath\.Join\|os\.Open\|os\.ReadFile\|ioutil\.ReadFile" --include="*.go" \
  | grep "r\.URL\|r\.Form\|mux\.Vars\|c\.Param\|c\.Query"

Ruby

# Mass assignment
grep -rn "attr_accessible\|permit(\|params\[" --include="*.rb" | grep -v test

# Deserialization
grep -rn "YAML\.load[^_]\|Marshal\.load\|JSON\.parse" --include="*.rb" | grep -v test

# Command injection
grep -rn "system(\|exec(\|`.*#\{" --include="*.rb" | grep -v test

# SQL injection
grep -rn "\.where(\|\.find_by_sql(\|\.execute(" --include="*.rb" | grep -E "#\{|concat|\+"

# Unsafe redirect
grep -rn "redirect_to.*params\[" --include="*.rb"

Rust

# Panic on network input (DoS)
grep -rn "\.unwrap()\|\.expect(" --include="*.rs" \
  | grep -v "test\|encode\|to_bytes\|serialize\|Display\|format"

# Unsafe blocks near network handling
grep -rn "unsafe {" --include="*.rs" -A10 | grep "read\|recv\|parse\|decode\|deserialize"

# Integer overflow (unchecked casts)
grep -rn "as u8\|as u16\|as u32\|as u64\|as usize\|as i8\|as i16\|as i32" --include="*.rs" \
  | grep -v "checked\|saturating\|wrapping\|test"

# Skipped verification
grep -rn "if let Ok\|let _ =" --include="*.rs" \
  | grep -i "verify\|sign\|cert\|auth\|validate\|check"

# TODO/FIXME near security code
grep -rn "TODO\|FIXME\|not signed\|not verified\|for now\|temporary" --include="*.rs" \
  | grep -i "sign\|verify\|cert\|auth\|encrypt\|token"

Java / Kotlin

# SQL injection
grep -rn "Statement\|createStatement\|executeQuery\|executeUpdate" --include="*.java" --include="*.kt" \
  | grep -E "\+|concat|String\.format|\".*\+.*\"" | grep -v test

# Deserialization
grep -rn "ObjectInputStream\|readObject\|XMLDecoder\|XStream\|SnakeYAML\|Gson\.fromJson" \
  --include="*.java" --include="*.kt" | grep -v test

# Command injection
grep -rn "Runtime\.getRuntime\|ProcessBuilder\|exec(" \
  --include="*.java" --include="*.kt" | grep -v test

# SSTI / Expression Language
grep -rn "SpEL\|ExpressionParser\|parseExpression\|StandardEvaluationContext" \
  --include="*.java" --include="*.kt"

# XXE
grep -rn "XMLInputFactory\|DocumentBuilder\|SAXParser\|TransformerFactory" \
  --include="*.java" --include="*.kt" | grep -v "setFeature.*disallow\|setFeature.*false"

# Weak crypto
grep -rn "MD5\|SHA-1\|DESede\|\"DES\"\|\"RC4\"\|ECB" --include="*.java" --include="*.kt" \
  | grep -v test | grep -v "MessageDigest.*SHA-256\|SHA-384\|SHA-512"

Solidity (Smart Contracts)

# Reentrancy
grep -rn "\.call\{value\|\.call\.value\|\.transfer(\|\.send(" --include="*.sol"

# tx.origin (auth bypass)
grep -rn "tx\.origin" --include="*.sol"

# Delegatecall to untrusted contract
grep -rn "delegatecall\|staticcall" --include="*.sol"

# Unchecked math (pre-0.8.0)
grep -rn "pragma solidity" --include="*.sol" | grep -v "0\.8\.\|>=.*0\.8\."

# Selfdestruct
grep -rn "selfdestruct\|suicide" --include="*.sol"

# Block timestamp dependency
grep -rn "block\.timestamp\|block\.number\|block\.difficulty\|block\.prevrandao" --include="*.sol"

3B: Auth Architecture Audit (The Sibling Rule)

# THE SIBLING RULE: If 9 endpoints have auth middleware and the 10th doesn't, that's your bug.

# Step 1: Find all route definitions
grep -rn "app\.\(get\|post\|put\|delete\|patch\)\|router\.\(get\|post\|put\|delete\|patch\)" \
  --include="*.js" --include="*.ts" | grep -v node_modules | grep -v test \
  > /tmp/sast-all-routes.txt

# Step 2: Find authenticated routes
grep "auth\|middleware\|protect\|verify\|requireAuth\|isAuthenticated\|isAdmin\|checkRole" \
  /tmp/sast-all-routes.txt > /tmp/sast-auth-routes.txt

# Step 3: Find UNAUTHENTICATED routes (the gap)
grep -v "auth\|middleware\|protect\|verify\|requireAuth\|isAuthenticated\|isAdmin\|checkRole" \
  /tmp/sast-all-routes.txt > /tmp/sast-unauth-routes.txt

echo "=== ROUTES WITHOUT AUTH MIDDLEWARE ==="
cat /tmp/sast-unauth-routes.txt

echo ""
echo "=== COMPARE: Auth routes vs Unauth routes ==="
echo "Authenticated: $(wc -l < /tmp/sast-auth-routes.txt)"
echo "Unauthenticated: $(wc -l < /tmp/sast-unauth-routes.txt)"

3C: Timing Side-Channel Detection

# Timing-unsafe comparison (=== or == for secrets instead of timingSafeEqual)
grep -rn "===.*token\|===.*secret\|===.*hash\|===.*password\|===.*key\|===.*api_key" \
  --include="*.js" --include="*.ts" | grep -v node_modules | grep -v test

# Python: timing-unsafe comparison
grep -rn "==.*token\|==.*secret\|==.*hash\|==.*password" \
  --include="*.py" | grep -v test | grep -v "timingSafeEqual\|hmac\.compare_digest\|secrets\.compare_digest"

# Developer inconsistency: timingSafeEqual in ONE place, === elsewhere
echo "=== timingSafeEqual usage ==="
grep -rnl "timingSafeEqual\|hmac\.compare_digest\|constantTimeCompare\|MessageDigest\.isEqual" \
  . --include="*.js" --include="*.ts" --include="*.py" --include="*.go" --include="*.java" \
  | grep -v node_modules 2>/dev/null

echo "=== Plain comparison near security code ==="
grep -rn "===\|==" --include="*.js" --include="*.ts" | grep -v node_modules \
  | grep -i "token\|secret\|hash\|hmac\|signature\|apikey\|api_key" | head -20

3D: Data Flow Tracing (Source → Sink Analysis)

SOURCES (where user input enters):
├── HTTP Request
│   ├── req.params / req.query / req.body
│   ├── req.headers (Host, X-Forwarded-For, Referer)
│   ├── req.cookies
│   └── req.files (uploaded files)
├── WebSocket messages
├── Environment variables (process.env)
├── Database reads (if data was tainted at write)
├── File reads (if user controls path)
└── External API responses (if attacker controls upstream)

SINKS (where dangerous processing happens):
├── DATABASE: cursor.execute(), db.query(), Model.where()
├── COMMAND: exec(), system(), spawn(), popen()
├── TEMPLATE: render_template_string(), innerHTML, dangerouslySetInnerHTML
├── FILE: open(), readFile(), writeFile(), sendFile()
├── NETWORK: fetch(), axios(), request(), http.get()
├── EVAL: eval(), Function(), setTimeout(string)
├── DESERIALIZE: pickle.loads(), yaml.load(), unserialize()
├── REDIRECT: res.redirect(), header("Location:")
└── CRYPTO: md5(), sha1(), Math.random() for secrets

SANITIZERS (what should stand between source and sink):
├── PARAMETERIZATION: prepared statements, ? placeholders
├── ENCODING: htmlEncode(), encodeURI(), escapeshellarg()
├── VALIDATION: joi.validate(), zod.parse(), regex whitelist
├── TYPE CHECKING: parseInt(), Boolean(), Number()
└── FRAMEWORK: ORM (prevents raw SQL), CSRF tokens, CSP headers

Tracing Protocol

1. Find a SINK (dangerous function call)
2. Trace the variable BACKWARD to its SOURCE
3. Check every function in the path for SANITIZATION
4. If no sanitizer between source and sink → FINDING
5. Verify: can the attacker control the source value?
6. Rate: What's the worst outcome? (RCE > SQLi > XSS > Info Disclosure)

PHASE 4: FRAMEWORK-SPECIFIC VULNERABILITY PATTERNS

Express / Node.js

PatternRiskGrep
Missing auth middleware on routeAuth bypassRoutes without authenticate middleware
body-parser without size limitDoSapp.use(bodyParser.json()) without {limit: '1mb'}
res.redirect(req.query.url)Open redirectredirect.*req\.\(query|params|body\)
Template rendering with user inputSSTIrender.*req\.
Missing CSRF on state-changing routesCSRFPOST/PUT/DELETE routes without CSRF token check

Django / Flask

PatternRiskGrep
mark_safe() with user dataXSSmark_safe.*request\.|mark_safe.*user
Missing @login_requiredAuth bypassView functions without decorator
render_template_string()SSTIrender_template_string
Raw SQL queriesSQLi.raw(|.extra(|cursor\.execute.*%s + no parameterization
DEBUG = True in productionInfo leakDEBUG\s*=\s*True

Laravel / PHP

PatternRiskGrep
Missing $fillable or $guardedMass assignmentModels without $fillable or $guarded
DB::raw() with concatenationSQLiDB::raw.*\$|DB::raw.*concat
Missing middleware on routesAuth bypassRoutes without middleware in routes/web.php
unserialize() with user inputRCEunserialize.*\$_
Loose comparison == for authAuth bypass==.*password|==.*token

Spring Boot / Java

PatternRiskGrep
Exposed actuator endpointsInfo leak/RCEmanagement.endpoints.web.exposure.include=*
Missing @PreAuthorizeAuth bypassController methods without auth annotation
SpEL injectionRCEparseExpression.*request|StandardEvaluationContext
XXE in XML processingFile read/SSRFDocumentBuilder without DTD disabled
Thymeleaf SSTIRCE__${...}__:: patterns

Ruby on Rails

PatternRiskGrep
Open permit! or over-permissive paramsMass assignparams\.permit!|permit(.*:role|:admin)
Missing before_action :authenticateAuth bypassController actions without auth
YAML.load() (not safe_load)RCEYAML\.load[^_]
Unsafe redirectOpen redirectredirect_to.*params\[
render inline: with user dataSSTIrender.*inline.*params

PHASE 5: FINDING PRIORITIZATION

Severity Classification

SeverityCriteriaExamples
CriticalRCE, full DB access, admin bypass, mass ATOeval() with user input, hardcoded admin creds, SQLi with no WAF
HighPII exfil, privilege escalation, partial RCEIDOR on all users, SSTI in non-sandboxed template, verified leaked secrets
MediumAuth gap on specific endpoint, limited data leakMissing auth on one route, XSS in low-traffic page, weak crypto for tokens
LowInfo disclosure, best practice violationDebug mode in non-prod config, verbose error messages, GET request with sensitive data
InfoNo exploitable impact, defense-in-depthMissing security headers, TODO comments, hardcoded test credentials

Exploitability Assessment

For each finding, answer:

1. Can an external attacker reach this code? [ ] Yes [ ] No (internal only) [ ] Maybe
2. Is user input the source?               [ ] Yes [ ] No (hardcoded/env) [ ] Indirect
3. Is there a sanitizer in the path?        [ ] None [ ] Weak [ ] Strong (bypass needed)
4. Is this the only control?               [ ] Yes [ ] No (defense in depth)
5. What's the worst outcome?               [ ] RCE [ ] Data leak [ ] Auth bypass [ ] DoS [ ] Info

Output Template — SAST Finding

## SAST-001: [Finding Title]

**Severity:** Critical / High / Medium / Low
**Category:** SQLi / XSS / RCE / Auth Bypass / IDOR / SSRF / Secrets / Crypto
**File:** path/to/file.py
**Line:** 42-48
**CWE:** CWE-XXX

### Description
[One paragraph: what the code does wrong, why it's vulnerable]

### Vulnerable Code
```language
// Line 42-48
vulnerable_code_here

Data Flow

SOURCE: req.params.userId (line 38)
  → passes through function validateInput() (line 40) — NO SANITIZATION
  → reaches SINK: db.query("SELECT * FROM users WHERE id = " + userId) (line 45)

Impact

[What can an attacker do? Be specific.]

Proof of Concept

[Exact request or input that triggers the vulnerability]

Recommended Fix

// SECURE version
fixed_code_here

---

## SAST REPORT TEMPLATE

```markdown
# Static Application Security Testing Report

**Target:** [Application Name]
**Date:** [YYYY-MM-DD]
**Analyst:** [Name]
**Scope:** [Source code directories analyzed]

## Executive Summary

| Severity | Count |
|----------|-------|
| Critical | X |
| High | X |
| Medium | X |
| Low | X |
| Info | X |

## Key Findings

### Finding 1: [Title]
[Use SAST Finding Template above]

### Finding 2: [Title]
[Use SAST Finding Template above]

## Methodology
- Automated scanning: Semgrep (security-audit, owasp-top-ten, language-specific)
- Secret scanning: Trufflehog (verified), Gitleaks
- Manual review: Injection sinks, auth architecture, data flow tracing
- Dependency audit: npm audit / pip-audit / govulncheck

## Recommendations
1. [Priority 1 fix]
2. [Priority 2 fix]
3. [Priority 3 fix]

ANTI-PATTERNS — DON'T DO THESE

Running semgrep and reporting raw output without analysis
Flagging "missing security header" as a real finding
Reporting TODO comments as vulnerabilities
Ignoring context (eval() in test files is not exploitable)
Marking everything as "Critical" (lose credibility)
Skipping manual review because scanner found nothing
Ignoring the auth architecture (biggest source of real bugs)
Not tracing data flow from source to sink
Reporting hardcoded test credentials as real secrets

INSTALLATION

# Prerequisites
pip3 install semgrep 2>/dev/null
brew install trufflehog gitleaks 2>/dev/null

# Add to Claude Code skills
mkdir -p ~/.claude/skills/code-reaper
cp SKILL.md ~/.claude/skills/code-reaper/SKILL.md

Then use in Claude Code: ask about "SAST", "static analysis", "code review", "audit this source code", or "find vulnerabilities in code".

Keep looking

Skills are one crate of 628,853. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.