| name | pentest-webapp |
|---|---|
| description | Crystal-box web application security audit. Use when the user asks to find, identify, or document security vulnerabilities in web application source code; when performing a penetration test, code review, or security audit focused on reporting (not remediating) flaws; or when the user references OWASP Top 10, CWE findings, or vulnerability hunting in a webapp codebase. Covers injection, auth/session, access control, XSS, CSRF, crypto, deserialization, file upload, API, business logic, SSRF, XXE, and information disclosure across common server stacks (Python, Node.js, Java, Go, PHP, Ruby, .NET). |
You are assisting with a crystal-box security audit of a target web application. Your goal is to identify and document security vulnerabilities in the source code, not to remediate them. Focus on objective, verifiable flaws that could be exploited.
Do not propose fixes, refactor code, or sanitize findings into recommendations. The deliverable is a vulnerability report with evidence.
- DO NOT suggest fixes, patches, or remediation
- DO NOT modify the application source code
- DO NOT report style issues or general best-practice deviations unless they have a concrete security impact
- DO include file paths and line numbers for every finding
- DO include the vulnerable code snippet as evidence
- DO describe a realistic attack scenario and the impact
- DO prioritize findings by severity and exploitability
- DO validate findings against the live target described in the user's
CLAUDE.md/AGENTS.mdor project context (typically a local Docker container, dev server, or staging URL). If no target is described, ask the user where to send requests before sending any. Never scan or send payloads to a host you have not been told is in scope. - DO include the validation command or short Python script in each finding so it can be re-run
- DO label each finding with severity using the rubric below: Critical | High | Medium | Low
Before searching for specific bug classes, build a map of the target:
- Stack identification — language(s), framework(s), database, ORM, templating engine, package manager. Read the dependency manifests for whatever language(s) you find.
- Entry points — route definitions, controllers, API handlers, GraphQL resolvers, WebSocket handlers, message queue consumers, scheduled jobs.
- Auth model — how users authenticate, where sessions/tokens are issued and validated, what authorization checks gate sensitive routes. Note the tenant boundary if multi-tenant.
- Data flow — where untrusted input enters (request params, headers, cookies, file uploads, webhooks), how it flows through validation, persistence, and rendering.
- External integrations — third-party APIs, OAuth providers, payment/shipping/email/SMS services, internal microservices, S3/blob storage.
- Configuration & secrets — env files, config files, CI/CD definitions, deployment manifests.
- Live target — confirm the URL, test credentials at multiple privilege levels if available, and any auth flow needed to reach authenticated routes. Record this so every validation script can re-use it.
Record this map in report/recon.md before drilling into specific vulnerability classes, so every later validation script can re-use the target URL, credentials, and auth flow.
Use the categories below as a checklist of areas to investigate, not a complete pattern list. Apply your own knowledge of each bug class to the stack from Phase 1, and pay particular attention to the non-obvious nudges called out under each.
- SQL — raw query APIs with concatenated/interpolated user input; ORM "raw" escape hatches (Django
raw()/extra(), Sequelizeliteral(), ActiveRecord interpolation, JPAcreateNativeQuery); dynamic ORM filters built from request data. - Command/OS — code that shells out for PDF/media/archive/DNS work;
shell=True,child_process.exec,Runtime.exec, backticks; user-controlled file paths fed toffmpeg/convert/tar/unzip. - SSTI — user input passed as the template (not just template data) to Jinja2, Twig, Freemarker, ERB, Handlebars, Razor, EJS, Thymeleaf.
- NoSQL / LDAP / XPath — Mongo
$whereand operator injection from raw request bodies are easy to miss; LDAP filters built via concatenation. - Header / log injection —
\r\nin user input written to response headers (response splitting) or log lines (log forging).
- Hardcoded credentials, dev backdoors, missing rate limiting/lockout on login.
- Insecure password storage: plaintext, MD5/SHA1, unsalted, low-cost KDF parameters, custom hashing.
- Password reset tokens that are predictable, long-lived, reusable, or not invalidated after use.
- JWTs accepting
alg: none, weak HMAC secrets, missing signature verification, missingexp/aud/isschecks, HS/RS key confusion. - Session fixation (session ID not rotated on login).
- Cookies missing
HttpOnly/Secure/SameSite; overly broadDomain/Path. - Tokens stored in
localStoragefor sensitive contexts (XSS exfiltration risk).
- Routes that read an ID from URL/body and load the record without an ownership/role check.
- Authenticated ≠ authorized — middleware/decorators that gate auth but skip authz; admin-only routes lacking role checks.
- Mass assignment letting users set fields like
is_admin,role,tenant_id,user_id. - Path traversal in file-serving endpoints; forced browsing to undocumented endpoints.
- Role checks performed only client-side.
- Queries filtered by user but not by
tenant_id/org_id/workspace_id. - Tenant ID sourced from request body/header (forgeable) instead of session/JWT.
- Cross-tenant foreign-key references accepted without validation.
- Shared caches, queues, or background jobs keyed without tenant scoping (cross-tenant data bleed).
- Bulk operations (export, search, reindex) iterating without a tenant filter.
- Admin/impersonation flows that don't re-scope queries to the impersonated tenant.
- Templates emitting user input with auto-escape disabled (
|safe,{{{ x }}},v-html,dangerouslySetInnerHTML,@Html.Raw,th:utext). - DOM sinks fed user input:
innerHTML,document.write,eval,Function(),setTimeout(string),srcdoc. - Attribute injection in
href/src/style; JSON in<script>blocks without proper encoding. - HTML email and PDF generators that interpolate user input.
- Missing/permissive
Content-Security-Policy.
- State-changing routes without CSRF tokens or equivalent (SameSite + custom header).
- State changes accessible via GET.
- CORS with
Allow-Origin: *+Allow-Credentials: true, or reflective origin echoing. - Anti-CSRF middleware globally disabled or bypassed for specific routes.
- Weak primitives in security contexts (MD5/SHA1 for integrity, DES/3DES/RC4, ECB mode).
- Static/zero IVs; reused nonces (especially GCM/ChaCha20-Poly1305).
- HMAC verification using
==instead of constant-time comparison (timing oracle). - Non-CSPRNG (
Math.random,rand(),random.random()) used for tokens/IDs/keys. - Custom-rolled crypto; hardcoded keys, IVs, or salts.
- Secrets committed in source, configs, fixtures, CI files, Dockerfiles; secrets logged or returned in error responses;
.env,.git/, source maps shipped to production.
- Untrusted data fed to
pickle,yaml.loadwithoutSafeLoader,BinaryFormatter,ObjectInputStream, PHPunserialize, RubyMarshal.load/YAML.load,Newtonsoft.JsonwithTypeNameHandling != None. - XML deserialization with type metadata enabled.
- Cookie or token payloads deserialized without integrity validation.
- Extension-only or MIME-from-request validation; missing magic-byte check; missing size limits.
- User-controlled filename joined into storage path (traversal/overwrite).
- Uploaded content served from same origin where it can be interpreted (PHP/JSP in upload dir,
.svgwith embedded scripts). - ZIP / archive extraction without path or size limits (zip-slip, zip-bomb).
- Image/PDF processors invoked on untrusted input without sandboxing.
- Missing authentication on internal/admin endpoints.
- BOLA / BFLA — broken object/function level authorization.
- No rate limiting on auth, password reset, or expensive endpoints.
- Mass assignment / over-posting (request body bound directly to model).
- Excessive data exposure (returning full objects when only a subset is needed).
- Verbose errors leaking stack traces, SQL, file paths.
- GraphQL: missing depth/complexity limits, introspection enabled in production, field-level auth missing.
- Workflow bypass — state machines that can be skipped (ship without payment, refund without return, verify-email step omitted).
- Numeric edge cases — negative quantities, integer overflow, decimal precision, currency rounding in price/quantity calculations.
- Replay & stacking — coupons, discounts, refunds, gift cards applied twice or combined when policy forbids it.
- Race condition sinks (search for these specifically):
- Check-then-act on uniqueness:
exists()/find_by()followed byinsert()/create()without a unique constraint or transaction. - Balance / inventory read-modify-write:
SELECT balance→ app-side subtract →UPDATE(should be atomicUPDATE ... WHERE balance >= amountorSELECT ... FOR UPDATE). - Voucher/coupon redemption that increments a counter without atomic compare-and-set.
- Missing idempotency keys on POST endpoints that mutate money, inventory, or external side effects.
- Check-then-act on uniqueness:
- SSRF — HTTP clients fetching user-supplied URLs without allow-listing (webhooks, image preview, URL importers, OAuth callback fetchers, PDF/HTML-from-URL features). Hostname-only validation misses DNS rebinding. No block on
169.254.169.254,metadata.google.internal, link-local, RFC1918, loopback. Redirect following enabled with no re-validation of the redirected target. - XXE — XML parsers with DTD/external-entity resolution enabled by default and not disabled. Watch SOAP, SAML, SVG, Office document, and XML import features.
- Open redirect — login/logout/post-action redirect parameters not validated against an allow-list;
Locationheader built from user input.
- Stack traces and framework debug pages reachable in production.
/debug,/actuator,/metrics,/.git,/swagger,/graphql(with introspection) exposed unauthenticated.- Comments containing credentials, internal URLs, or TODO/FIXME flagging known security gaps.
- Source maps,
.bak,.swp, editor temp files served by static handler.
- Manifest pinned to versions with known CVEs.
- Direct loading of remote scripts/binaries at build or runtime.
- Lockfile drift / missing lockfile.
These don't fit a single category — sweep for them across the codebase:
- Disabled framework defaults in deployed configs: CSRF off, CORS wide-open,
DEBUG=True,app.debug = true,ASPNETCORE_ENVIRONMENT=Development. - Reflection / dynamic dispatch driven by user input:
getattr,Method.invoke,eval, dynamicrequire/import. - Comment markers:
TODO security,FIXME,HACK,XXX,temporary,disable.*auth— frequently flag known gaps the developers never closed.
Use ripgrep (rg) to triage candidates against the stack from Phase 1, then trace each hit's data flow back to the entry point before writing it up. Do not treat regex hits as findings.
For every candidate, attempt to falsify it before writing it up:
- Trace the input back to the entry point. Is there validation, a type cast, an auth check, a tenant scope, or a framework default that neutralizes the path?
- Look for upstream middleware, decorators, gateway rules, or schema constraints that may mitigate it.
- If you can run a request against the live target, do so. Confirmed exploitation is the strongest evidence.
Only after attempting falsification, assign confidence:
- HIGH — exploitation confirmed against the live target, OR the source path is unambiguous and no mitigating control was found.
- MEDIUM — likely vulnerable, but exploitability depends on runtime conditions you cannot fully verify (specific config, gateway rules, WAF). State the assumption explicitly in the finding.
- LOW — pattern matches but you could not trace a clean exploit path. Include only if the impact would be high enough to warrant follow-up.
De-duplication: if the same root cause appears at multiple endpoints/files (one missing decorator across 14 routes, one unsafe helper called from many call sites), write one finding with the root cause and list all affected locations. Do not file 14 near-identical findings.
Save the findings in a report/ directory. If report/ already contains findings from a prior run, don't overwrite or renumber them silently — ask the user whether to start fresh (a new directory or a cleared one) or add to the existing set, and if adding, continue the numbering from the highest existing finding.
Name each finding file NN-severity-slug.md, where NN is a two-digit sequence number assigned in final report order (Critical/High first, then by confidence within a severity tier), severity is the lowercase tier, and slug is a short kebab-case label. For example: 01-critical-sqli-login.md, 02-high-idor-invoices.md. Save each finding's shell or Python validation script in the same directory using the matching stem (01-critical-sqli-login.sh, 01-critical-sqli-login.py).
For every finding, create a Markdown file using this format:
# [SEVERITY] Short Title
**Severity**: Critical | High | Medium | Low
**Confidence**: High | Medium | Low
**Category**: short label (e.g., SQL Injection, IDOR, SSRF)
**Affected locations**:
- `relative/path/to/file.ext:LINE` — short note (e.g., "missing tenant scope")
- `relative/path/to/other.ext:LINE`
**Preconditions**: auth state required (unauth / any user / specific role), feature flags, config, tenant context, etc.
### Description
What the vulnerability is, in plain language.
### Vulnerable Code
\`\`\`<lang>
// minimal snippet showing the flaw, with surrounding context if needed
// Add comments with file name(s) and line number(s) at the beginning of each piece of code
\`\`\`
### Impact
What an attacker achieves: data exfiltration, account takeover, RCE, privilege escalation, denial of service, financial loss, etc. Be concrete.
### Attack Scenario
Step-by-step: starting position, input, observed behavior, end state. Include an example payload when meaningful.
### Validation
Command or short Python script that reproduces the issue against the in-scope target. Include observed response / expected output.
\`\`\`bash
# or python — for Python, declare uv deps in the script header
\`\`\`
### References (optional)
- OWASP / CWE / CVE — only when accurate; omit if unsure.Write a report/SUMMARY.md that indexes every finding in final report order (Critical/High first, then by confidence within each severity tier). One row per finding, linking to its file:
# Findings Summary
| # | Severity | Confidence | Category | Title | Finding |
|---|----------|------------|----------|-------|---------|
| 01 | Critical | High | SQL Injection | Unauthenticated SQLi on login | [01-critical-sqli-login.md](01-critical-sqli-login.md) |
| 02 | High | Medium | IDOR | Cross-tenant invoice access | [02-high-idor-invoices.md](02-high-idor-invoices.md) |Regenerate SUMMARY.md after adding or renumbering findings so its order and numbering stay in sync with the finding files.
- Critical — unauthenticated RCE, unauthenticated full data exfiltration, auth bypass to admin, hardcoded production credentials in source
- High — authenticated RCE, IDOR exposing other users' or tenants' sensitive data, SQLi with data read/write, stored XSS in authenticated context, SSRF reaching cloud metadata or internal services
- Medium — reflected XSS, CSRF on sensitive actions, weak crypto on stored secrets, missing rate limiting on auth, verbose error leakage of internals
- Low — missing security headers, verbose banner, info disclosure of non-sensitive details, weak password policy without other compounding factors
Workflow: Phase 1 recon → confirm the live target and test credentials → sweep Phase 2 categories using Phase 3 cross-cutting patterns → run each candidate through the Triage Gate → write findings in the Reporting Format → regenerate report/SUMMARY.md. Number findings and order SUMMARY.md by severity (Critical/High first), then by confidence within each severity tier.