Is Cursor's AI Output Secure? We Scanned a Seeded Diff
Short answer: "Cursor security review" now means two things: Cursor's own beta Security Reviewer agent, which comments on every PR, and checking the code Cursor writes. In a seeded, representative diff — a new admin API route with no auth guard plus a hardcoded OpenAI key — a real ShipGuard scan returned four high-severity findings and a DO_NOT_SHIP verdict.
A seeded Cursor-style diff went through shipguard scan on the current public binary and came back with four high-severity findings and a DO_NOT_SHIP verdict: two hardcoded copies of the same OpenAI key, a missing middleware.ts, and an admin API route with no session check. Capture ai-tool-harness-vuln, @agenticcli/[email protected], 2026-07-28. The terminal output is further down, and so is the fixture it read.
This scene is assembled, not reported: a solo founder asks Cursor to add an admin settings panel — a small API route so the team can flip a feature flag without redeploying. Cursor writes the route, wires it to a config object, and the panel works on the first try. The founder clicks "Accept All," the way Cursor is built to be used, and moves on to the next feature.
Nobody asked whether that route checks who is calling it. It doesn't. And the OpenAI key the config object needed for a separate feature is now sitting in the file too, in plain text.
That is not a story about Cursor being a bad tool. It's a story about a question nobody asked, because the panel worked.
Since 30 April 2026 the phrase "Cursor security review" has had two owners. Cursor ships a feature by that name: a Security Reviewer agent of its own.
The other owner is the question a developer actually types it for: whether the code Cursor just wrote is safe to ship. The change we seeded is representative rather than a captured session, and it ran through ShipGuard's free local CLI. We ran the same experiment against Claude Code's output in a separate piece, and the shape of the failure was the same.
At a glance
- Cursor Security Review is a real, shipped Cursor feature: a Security Reviewer on every pull request, plus a scheduled Vulnerability Scanner, in beta on Teams and Enterprise plans.
- Nothing in Cursor's own security agents reads the diff at the moment you accept it in the editor.
- Andrej Karpathy's own post coining "vibe coding" names Cursor Composer directly and describes accepting every diff without reading it.
- We ran a real scan, not a mockup:
shipguard scanagainst a seeded fixture, captured with a real terminal renderer, with the fixture itself shown below. - The scan caught all four findings and returned DO_NOT_SHIP on the current public ShipGuard binary. One of the four is a structural Next.js check that would fire on an empty repo of that shape, so three of them are evidence about the diff itself.
What does a Cursor security review actually mean?
Search the phrase on 2026-07-29 and page one is mostly Cursor's own product: cursor.com/docs/security-review, cursor.com/docs/security-agents, cursor.com/security, and the changelog entry dated 30 April 2026. Snyk's teardown of the prompts behind those agents sits alongside them. Two of those Cursor pages document the feature and the changelog announces it. None of them is about the code Cursor writes for you.
That is a good result for a search engine and an awkward one for a developer who typed the phrase with an unreviewed diff still open, because the feature and the question share a name and do not share a scope. The rest of that page is independent explainers — TrueFoundry, Reco, Endor Labs, MintMCP, a Medium post — and they are the real competition for anyone typing the phrase.
Is the question about Cursor the editor, or the code Cursor writes?
Tool security is about Cursor the editor: what it is allowed to do on your machine, how it stores your code, whether its own agent can be talked into something. Output security is about the code that came out of it. Does the route Cursor just wrote check who is calling it? Does the config file it touched hold a live key? A perfectly audited editor can still hand you an admin route with no auth check. The editor did exactly what it was asked to do. The feature it produced is still broken.
The output half is the one a scan can reproduce, so that is the half measured here. We did not test Cursor's own editor, its account security or its telemetry. Cursor publishes its own security documentation for that, and independent researchers have published findings against the editor itself.
What is Cursor Security Review, the feature?
Cursor's changelog entry dated 30 April 2026 is unambiguous about the tier:
Cursor Security Review is now in beta on Teams and Enterprise plans.
— Cursor (Anysphere) · Cursor Security Review changelog, April 30, 2026
It ships two agents. The first is a reviewer:
Security Reviewer checks every PR for security vulnerabilities, auth regressions, privacy and data-handling risks, agent tool auto-approvals, and prompt injection attacks.
— Cursor (Anysphere) · Cursor Security Review changelog, April 30, 2026
The second runs on a schedule rather than on an event. Per Cursor's Security Agents documentation, "Vulnerability Scanner scans your codebase at rest. Use it to find pre-existing vulnerabilities, long-standing issues, and problems missed during PR review." Both are genuinely useful and neither is marketing: prompt injection and agent tool auto-approvals are two of the harder things to check for, and Cursor put them in the reviewer's remit by name.
How does a per-PR LLM reviewer differ from a local pre-ship scan?
Three ways that matter, and none of them is about quality. It runs later, at pull-request time rather than at accept-diff time. It runs on a tier, so a solo developer on a personal plan does not have it. And it is a language model reading a diff, which means its findings carry a model's judgment rather than a rule's.
Randall Degges of Snyk, who read the published prompts behind Cursor's security agents and rated them well, named the consequence directly in I Read Cursor's Security Agent Prompts, So You Don't Have To: "the agent cannot mark its own homework. You need an independent validation layer confirming what the LLM found." Snyk sells a deterministic scanner, so read that as an interested party making a point that happens to be right.
Why does Cursor's fast-accept workflow make this worth checking?
Cursor is built around speed: inline suggestions, a chat-driven Composer mode, and a workflow that assumes you will accept most of what it proposes rather than hand-edit each line. That design choice is why Andrej Karpathy picked Cursor as his example when he coined the term that would define this entire category.
In the post that introduced vibe coding to general use, he named the tool directly: "It's possible because the LLMs (e.g. Cursor Composer w Sonnet) are getting too good." He then described his own habit in the same post, plainly:
I "Accept All" always, I don't read the diffs anymore.
— Andrej Karpathy, independent AI researcher/educator (post-OpenAI, post-Tesla) · original "vibe coding" post, February 2, 2025
This is a description of a workflow, not an accusation against a product. Plenty of features shipped this way turn out fine. A review step that is optional gets skipped under time pressure far more often than a review step that is mandatory.
Cursor's own Security Reviewer is mandatory — on the plans that have it.
What did we actually run, and against which version?
The fixture is ours and we wrote it to contain these findings. It plants a new Next.js API route at app/api/admin/config with no auth check, alongside a config module and an OpenAI client file that both hardcode the same key — styled as the "quick testing" shortcut a fast-accept session plausibly leaves behind.
A scan of it could not have come back clean, and saying otherwise would be theatre. Which of the seeded problems the free tier actually names, where it names them, and what verdict it reaches are not settled by how the fixture was written.

The seeded fixture, read out of the same bind-mounted directory the scan reads, inside the same container image. Capture ai-tool-harness-fixture, @agenticcli/[email protected], 2026-07-28.
Which binary produced the numbers below?
The Docker image this pipeline uses for terminal captures had @agenticcli/[email protected] baked in from an earlier build. The current public release, confirmed against the npm registry on 2026-07-28, is @agenticcli/[email protected]. A capture on the stale image would have been a claim about a tool that no longer exists in that exact form, so the image was rebuilt against 0.5.1 first.
Every result below reflects the binary a developer running npx @agenticcli/shipguard scan actually gets today. The scan itself is the same one anyone can run for free, no account required:
npx @agenticcli/shipguard scan --json
What did the real scan find?
DO_NOT_SHIP. Four findings, all high severity, captured directly from the running container:

Real scan, capture ai-tool-harness-vuln, @agenticcli/[email protected], 2026-07-28.
| Finding | File | Severity |
|---|---|---|
| Hardcoded OpenAI API key | lib/config.ts:3 |
High |
| Hardcoded OpenAI API key | lib/openai.ts:4 |
High |
| No Next.js middleware.ts found | middleware.ts |
High |
| Unprotected API route handler | app/api/admin/config/route.ts |
High |
We ran the scan twice against a live binary rather than a static fixture. Capture ai-tool-harness-vuln stamps the two runs at 14:55:42 and 14:55:54 UTC on 2026-07-28, and their payloads are byte-identical once the timestamp is removed, which a hand-written transcript cannot produce.
Why is the same key two findings?
Two of these are the same underlying mistake in two places: the same OpenAI key, hardcoded once in the config module and again in the client file that reads it. That duplication happens naturally when one file gets built and a second file writes the value out again instead of importing it from the first.
If your stack is Next.js and Supabase specifically, our secret scanner walkthrough for that stack runs the same category of mistake end to end, and our teardown of a hardcoded Stripe key shipped by an AI coding tool explains why an agent writes the literal in the first place and why a linter stays quiet about it.
GitHub runs its own secret scanning over pushed repositories, which sits at a different point on the same timeline: after the key is in git history rather than before. Our comparison of GitHub's native secret scanner against dedicated CLI tools walks through what each side of that line is good for.
What does the missing guard actually expose?
The third finding is structural, checked on every Next.js project regardless of which feature prompted the scan: no middleware.ts covering the sensitive routes. The fourth is the one that matters most for this feature. The admin config route has no session check in either its GET or its POST handler, so anyone who finds the URL can read the current config or overwrite it.
That failure rhymes with two others AI-built apps hit constantly: a Supabase table shipped without Row Level Security and a Firebase project left in test-mode rules. Different mechanism, same missing guard.
Is this a Cursor-specific problem, or a fast-AI-coding problem?
Nothing about this failure pattern is unique to Cursor's model or its implementation. It is a consequence of how a fast-accept workflow meets an ordinary feature request, and it shows up across tools built the same way. OpenAI's own Codex Code Review team makes almost exactly this point about where a fixed rule helps and where it can't:
"Tests and linters work well for checks you can express deterministically; repository rules help capture the judgment that is harder to encode."
— OpenAI Developers · Custom Code Review rules for Codex
An auth check on an admin route is expressible deterministically: either a session gets verified before the handler runs, or it doesn't. It's precisely the class of check a fixed rule catches every time, regardless of which model or which editor wrote the route.
Does anyone have numbers on rules versus judgment?
OpenAI does, for its own product. Its own writeup, Custom Code Review rules for Codex, measures what happens when Codex's reviewer is taught specific repository rules instead of relying on general judgment, and reports: "In the primary suite, rule-guided variants recovered 98% of the required custom findings, compared with 58.3% in the baseline control."
That figure describes Codex Code Review, a different product measuring a different thing, and it is not a claim about ShipGuard or about Cursor. A fixed, deterministic check catches a category of mistake that general judgment, human or model, catches inconsistently.
If the tool writing your code is an autonomous agent rather than an editor you are driving, the same argument extends from code to permissions, which our piece on AI agent security covers.
What does ShipGuard check about Cursor, and what can't it check?
| Tool security | Output security | |
|---|---|---|
| Covers | What Cursor is allowed to do on your machine while it works | What the feature it just wrote actually contains |
| Evidence in this piece | Not measured here. Three third-party findings are cited below | A real ShipGuard scan: four findings, DO_NOT_SHIP, shown above |
| Where to look instead | Cursor's own security documentation, and the advisory databases | A deterministic pre-ship gate, run the same way on every diff |
What is already on the public record about Cursor the editor?
The tool-security half has a real public record and it is not ours. GitHub's Advisory Database carries GHSA-4cxx-hrm3-49rm against Cursor: "Cursor allows writing in-workspace files with no user approval." It was fixed in Cursor 1.3.9. Aim Labs, who found it, described the consequence in their own CurXecute writeup: "Cursor instantly executes any new entry added to ~/.cursor/mcp.json. No confirmation is required."
The same flaw is CVE-2025-54135 in the National Vulnerability Database, where NIST's own CVSS v3.1 base score is 9.8, critical. GitHub, as the reporting authority, scored the same entry 8.5, high. Which record you open changes how bad it looks.
The Register reported in April 2026 that a Cursor agent destroyed a startup's production database, quoting founder Jer Crane: "It took 9 seconds." The advisory, the Aim Labs disclosure and this incident are all about the editor and its agent, not about the code they produce.
Why doesn't ShipGuard integrate with Cursor at all?
It scans your working directory the same way regardless of which tool, or which human, wrote the code sitting in it. That is a deliberate design choice rather than a missing feature: a scan that only worked on one editor's output would be useless the moment a team used two tools in one repository, which is common.
The free tier covers secrets, auth gaps, database risk, payment-handler mistakes and deployment configuration. It runs entirely locally, with no code leaving your machine. It does not evaluate Cursor's own account security or its telemetry practices, and none of that is in scope for a scan that only ever sees your files.
The secrets check that caught both hardcoded keys above runs on RE2JS, a linear-time regex engine chosen so a crafted or unusually large file can't hang the scan through catastrophic backtracking. A scanner that can hang on one crafted file gets skipped under deadline pressure the same way an optional code review does, and a skipped scanner catches nothing. The same engine backs both the free-tier patterns and the broader corpus on paid plans.
How does this compare with the Claude Code output audit?
We ran a similar audit against Claude Code's output in a separate piece, with an honest caveat: different fixtures, different methodologies, not a head-to-head verdict on which tool is safer.
| Audit | What was tested | Real ShipGuard verdict |
|---|---|---|
| This piece (Cursor) | One seeded change: a new admin API route plus a hardcoded key, styled after Cursor's fast-accept workflow | DO_NOT_SHIP, 4 of 4 findings high |
| Claude Code output audit | Four independent feature prompts: an auth-gated dashboard, a Stripe webhook, a Supabase CRUD API, a file upload endpoint | 1 generation clean, 3 of 4 DO_NOT_SHIP |
The same two ingredients keep turning up in different combinations: authentication that exists somewhere in the app but not on the route that needed it, and a credential hardcoded as a "temporary" convenience that outlived its reason. The Claude Code run produced them in separate generations; this fixture holds both at once, because that is how we built it.
A pre-merge checklist before you accept a Cursor diff
None of this requires reading every character of every diff. Our rundown of twelve vibe-coding security checks is the longer list; these five are the ones this fixture actually exercised.
- Check whether the new route derives its caller from a verified session, not from a query parameter or a request body field the caller supplied.
- Check whether any config or client file hardcodes a key instead of reading it from an environment variable, including a fallback constant meant only "for local dev."
- Check whether
middleware.tsexists and its matcher actually covers the new route, not just the page that links to it. - Run an automated scan on the diff before merging, not only on the diffs that felt risky while you were reading them.
- Check whether the route or feature actually needed the credential it was given at all, since a scoped-down key does less damage than a broad one if it ever does leak.
Does Cursor's own reviewer cover any of these?
The last two are the ones a fast-accept workflow structurally can't give you on its own. If your team is on Teams or Enterprise, the two checks overlap and that is fine. If you are one person on a personal plan, the pull-request reviewer is not there at all, and the working directory is the only place left to look.
Where to check this yourself
Run the exact free-tier command behind every finding above, no account, no code leaving your machine:
npx @agenticcli/shipguard scan --json
Add --strict if you want a CI step that actually fails the build on a finding like the ones above, rather than only reporting them:
npx @agenticcli/shipguard scan --changed --strict --json
A hardcoded key is the same failure whether it shipped from Cursor, Claude Code, Codex, or a human typing directly into the file. Our look at what a real secret scan found in a vibe-coded app covers that half of the problem in more depth than fits here.
See what ShipGuard actually checks for the full rule set behind today's four findings, including the categories that live beyond the free tier.
What does a clean scan actually prove?
A clean scan is not proof of security. It is proof that the specific patterns a scanner knows to check weren't present in that run. This one wasn't clean on the current public binary — and the fixture was built to look like an ordinary Tuesday feature request. That is exactly the case a deterministic gate exists for: not the diff that feels risky, but the one that doesn't.
Published by AgenticCLI — developer tools for teams shipping AI-assisted code. ShipGuard is a deterministic, CLI-first release gate that runs locally, with no code leaving your machine on the free tier. See all checks at agenticcli.dev/shipguard.
FAQ
Is Cursor safe to use?
Does Cursor check its own code for security bugs?
What did the seeded Cursor-style diff we scanned contain?
How is Cursor's security model different from a deterministic pre-ship scan?
Should I review every diff Cursor generates before merging it?
Can a clean ShipGuard scan prove Cursor's output is secure?
────────[ ▮ gate ]────────
Don't ship the next one.
Free, local, no account. Catches this exact bug class before deploy.
$ npx @agenticcli/shipguard scan