Your tests pass. Your app is not secure.
I had a multi-tenant SaaS with role-based access control, data isolation between organisations, and serverless functions handling sensitive operations. I had a test suite. It all passed. I felt good about it.
Then I let Claude Code attack it.
Not "review the code." Not "suggest improvements." I gave it one instruction: try to break this application. I launched five parallel AI agents, each acting as an independent penetration tester with a specific attack mandate. They generated roughly 1,100 adversarial test cases in about three minutes.
Around 50 of them failed on the first run. Every failure was a real vulnerability or a real gap in my defences.
Then I connected Burp Suite through an MCP server and let the same AI fire live HTTP requests at the actual running database. It found that seven tables accepted DELETE requests from anonymous users.
The whole thing cost me about a million tokens. Roughly eight dollars.
A professional penetration test costs $10,000–$30,000 and takes weeks to schedule. This took one afternoon and found bugs the test suite couldn't.
Claude Code as a pentester, not a code reviewer
The difference matters. Code review asks: does this code look correct? Pentesting asks: can I break this application? The mindset is adversarial. The agent is not trying to understand your architecture. It is trying to destroy it.
I used Claude Code's subagent system to launch five specialised attackers in parallel. Each one got a different attack mandate, full context about the application's architecture, and zero prior exposure to the source code. They were attackers, not maintainers.
Injection Specialist
123 XSS payloads across 11 categories, 44 SQL injection vectors, mutation XSS, SVG injection, polyglot payloads, encoding bypasses, data URI injection, CSS-based exfiltration.
Access Control Specialist
Cross-tenant data access, UUID tampering, null injection in IDs, indirect references through relationship chains, export endpoint leaks, search result enumeration.
Auth Specialist
Brute force resistance, session manipulation (NaN timestamps, Infinity, negatives), CSRF timing attacks, JWT claim tampering, subdomain verification bypass.
Infrastructure Specialist
CSP header validation, SSRF via internal IP ranges, cloud metadata endpoint access, file upload polyglots, MIME spoofing, path traversal, null byte filename injection.
Business Logic Specialist
Workflow state machine bypass, multi-step action sequencing, role boundary violation, privilege escalation chains, immutable record tampering, data exfiltration via aggregation.
Privacy & Data Specialist
PII leak detection in logs, log sanitisation gaps, data deletion coverage, cross-tenant data leakage, metadata exfiltration, over-fetching via wildcard selects.
Each agent ran for about three minutes. They produced roughly 1,100 test cases total. I ran them. Around 50 failed. Those were not false positives.
The sanitiser was the security boundary. It was also the weakest point.
My existing test suite checked that sanitizeInput() stripped <script> tags. It never tested what happened with SVG payloads, unquoted event handlers, javascript: URIs, or Base64-encoded data URIs. The adversarial suite tested all 123 of them.
| Finding | Severity | CWE |
|---|---|---|
| Sanitiser did not strip unquoted event handlers. <img src=x onerror=alert(1)> passed through intact. | Critical | CWE-79 |
| Sanitiser did not strip javascript: URIs. Links with href="javascript:..." survived. | Critical | CWE-79 |
| Sanitiser only removed <script> but not 29 other dangerous tags (<svg>, <iframe>, <object>, <embed>, <math>). | High | CWE-79 |
| Sanitiser did not strip data:text/html URI schemes. Base64-encoded XSS payloads bypassed detection. | High | CWE-79 |
The agent also documented 16 security gaps: places where a defence layer was missing entirely. SSRF validation that accepted internal IPs. Session checks with no NaN guard. File upload validators that only checked the last extension. Every gap is now a tracked test.
My test suite had 15 XSS payloads. Claude Code generated 123, sourced from OWASP, PortSwigger research, and real CTF competitions. The difference between 15 and 123 was four critical vulnerabilities.
Not <script>alert(1)</script>
These are the kinds of payloads the agent generated. They are not textbook examples. They are real-world evasion techniques that bypass naive sanitisers.
<math><mtext><table><mglyph><style><!--</style><img onerror=alert(1)>
// SVG animation — triggers without user interaction
<svg><animate onbegin=alert(1) attributeName=x dur=1s>
// Polyglot — simultaneously valid as JS, HTML, and CSS
jaVasCript:/*-/*`/*\`/*'/*"/**/(/* */oNcliCk=alert() )//
// Base64 data URI — encoded payload bypasses string matching
data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==
The SQL injection suite contained 44 vectors: classic injection, auth bypass, UNION-based extraction, blind boolean injection, time-based injection, stacked queries, and PostgreSQL-specific privilege escalation. Every payload was a string literal in a test assertion: never rendered, never executed.
Then I pointed Burp Suite at the live database
Phase one tested the application code. Phase two tested the running infrastructure. Because here is the thing about unit tests: they never send a real HTTP request to a real database. They test your functions in isolation. They do not test what happens when someone sends a raw DELETE to your API.
Burp Suite has an MCP server. This means Claude Code can use it as a tool: craft HTTP requests, send them through Burp's request engine, read the response, and decide what to attack next.
How it works
MCP (Model Context Protocol) lets AI agents call external tools. Burp Suite exposes tools like send_http1_request and get_proxy_http_history. The agent sends an attack, reads the response, and chains the next attack based on what it learned.
GET /rest/v1/notifications → 404
"hint": "Perhaps you meant 'public.user_notifications'"
// Uses the leaked name to confirm access
GET /rest/v1/user_notifications?select=*
→ 200 OK []
// Escalates to destructive verbs
DELETE /rest/v1/user_notifications?id=gt.0
→ 204 No Content // the database ACCEPTED this
That 204 is the moment you stop feeling good about your test suite.
What Burp Suite MCP found
| Finding | Severity | Vector |
|---|---|---|
| Seven tables accepted DELETE from anonymous users. RLS filtered to zero rows, but the verb was routed. One policy bug away from total data wipe. | Critical | PostgreSQL grants |
| Full database schema exposed without authentication. The API introspection endpoint returned 146KB of JSON revealing every table, column, type, relationship, and function signature. | High | Schema enumeration |
| Error messages leaked real table names. Querying non-existent tables returned "Perhaps you meant..." suggestions. | High | Information disclosure |
| WebSocket upgrades accepted from anonymous. The realtime endpoint returned 101 Switching Protocols with no auth. | Medium | WebSocket |
| Serverless function responded to wrong HTTP method. A POST-only endpoint processed GET requests. | Medium | Method confusion |
| Anonymous users could count rows in sensitive tables via Prefer: count=exact header. | Medium | Metadata leakage |
The root cause every Supabase project should check
When you create a table in Supabase with RLS enabled, the anon role still has full DML grants: SELECT, INSERT, UPDATE, DELETE. RLS policies filter which rows are visible. But the verbs are still routed. Defence in depth means blocking the verb at the grant level, not just the policy level.
DELETE /rest/v1/transactions?id=gt.0
→ 204 No Content
-- After: grant revoked, rejected before query executes
DELETE /rest/v1/transactions?id=gt.0
→ 401 Unauthorized "permission denied for table transactions"
One SQL migration. REVOKE ALL from anon on every table. Re-grant only what public-facing features actually need. Done.
Run this against your own Supabase project right now:
FROM information_schema.role_table_grants
WHERE grantee = 'anon'
AND privilege_type = 'DELETE'
AND table_schema = 'public';
If that returns rows, you have the same vulnerability.
RLS tells PostgreSQL which rows you can see. Grants tell PostgREST which verbs to route. Both layers have to say yes. Most Supabase projects are only checking one.
$8 versus $15,000
A professional penetration test costs $10,000–$30,000 depending on scope. It takes weeks to schedule, days to execute, and the report arrives as a PDF you read once and file somewhere. The findings are frozen in time.
This entire exercise, ~1,100 adversarial unit tests plus 40+ live HTTP attacks against running infrastructure, used about one million tokens. At current API pricing, that is roughly eight dollars.
I have now run this same approach across two different projects. Different architectures. Different tech stacks. Different attack surfaces.
| Metric | Human Pentest | AI + Burp MCP |
|---|---|---|
| Cost | $10,000–$30,000 | ~$8 |
| Time to schedule | 2–6 weeks | Immediate |
| Time to execute | 3–5 days | ~30 minutes |
| Deliverable | PDF report | Executable test suite + live attack logs |
| Regression protection | None, a one-time snapshot | Tests run on every push |
| Infrastructure testing | Usually scoped separately | Same session, same agent |
This does not replace a formal pentest. You still need a human expert for threat modelling, social engineering, and the kind of creative lateral thinking that AI cannot replicate. But for the 90% of testing that is methodical, pattern-based, and exhaustive,this is a 1,000x improvement in cost-efficiency.
You need both
Phase one (Claude Code writing adversarial tests) finds application-layer bugs: broken sanitisers, missing validation, incomplete RBAC. These are bugs in your code.
Phase two (Burp Suite MCP attacking live infrastructure) finds configuration-layer bugs: overly permissive grants, exposed endpoints, verb routing that should not exist.
Neither finds the other's bugs. Unit tests do not send real HTTP requests to real databases. Proxy-based testing does not analyse source code for sanitisation gaps.
| Phase 1: Claude Code | Phase 2: Burp MCP | |
|---|---|---|
| Attack surface | Application code | Live API + database |
| Tool | Test framework + subagents | Burp Suite + MCP |
| Vulns found | 4 (sanitisation) | 6 (infrastructure) |
| Time | ~3 minutes | ~4 minutes |
The setup takes five minutes
- Install Burp Suite (Community Edition is free) and enable the MCP server extension.
- Connect Claude Code to Burp's MCP server via your
.mcp.jsonconfiguration. - Start your local dev environment: your app, your database, your API. Everything running on localhost.
- Tell Claude Code to attack it. Give it the base URL, the anon API key, and say: "You are a penetration tester. Find vulnerabilities."
For the unit test phase, launch five subagents in parallel with different attack mandates. Give each one your application's architecture context and tell it to write adversarial tests. Compile the output, run it, fix the failures.
If you are not ready for a thousand tests, start with five:
- Cross-tenant isolation: Authenticate as Org A. Request Org B's data by ID.
- Privilege escalation: Authenticate as your lowest role. Call your most privileged endpoint.
- XSS in the richest field: Put
<svg onload=alert(1)>in a text area. - DELETE from anon: Send
DELETE /rest/v1/your_table?id=gt.0with no auth. - Schema exposure: Hit your REST API root with no auth.
If any of those five fails, you have found a real vulnerability. The ratchet only moves in one direction.
The best time to pentest your application was before you launched. The second best time is right now, and it costs eight dollars.
These models can read your entire codebase, understand your architecture, generate hundreds of attack vectors, fire them at your live infrastructure, adapt based on responses, and produce a detailed report, all in the time it takes to make a coffee.
The question is not whether AI pentesting works. I have the 10 fixed vulnerabilities across two projects to prove it does. The question is why are you not running this against your own product today?
Written by withkarann · April 2026 · withkarann.com