Skip to content

A security audit in Cursor pairs deterministic scanners with an agent review. Dependency and secret scanners run first and produce reproducible evidence. Cursor’s agent then reviews authentication, injection, and API surfaces, and every finding it reports must come with a failing exploit test before any fix merges. CI gates keep the fixed classes of bug from coming back.

This guide is for developers and tech leads whose team already builds with Cursor. The launch is three weeks out, the product stores payment and personal data behind a public API, and the compliance lead wants a security assessment by Friday. There is no pentest budget, and nobody on the team has time to read the whole codebase line by line.

An agent audit does not replace a professional penetration test or a threat model. It does find the pattern-shaped bugs, such as string-built SQL, unescaped HTML, missing authorization checks, and committed keys, and it turns each one into a test you can run.

  • A scanner baseline (dependencies, secrets, static analysis) that anyone can rerun and get the same answer.
  • Two review prompts that make the agent report findings as evidence, with a file, a line, and an exploit path, not opinions.
  • A fix prompt that writes the exploit test first, so each fix is proven by a test going from red to green.
  • A CI workflow and a project rule that stop the same bugs from coming back.
  • A sign-off table that tells the tech lead what to check instead of reading every diff.

Before you start the audit: set up a safe session

Section titled “Before you start the audit: set up a safe session”

Three decisions made up front keep the audit itself from becoming a security incident.

  1. Work on a branch. Create security-audit-2026-09 and keep every change there until the sign-off in the last section.
  2. Keep secrets out of the agent’s context. Anything the agent reads is sent to a model provider. Run the secret scanner with redaction (step 2), and do not paste .env contents into chat. Check your team’s Privacy Mode and data settings on privacy and security in Cursor.
  3. Keep command approval on. The agent runs tests and may propose commands during this audit. Never switch command execution to a mode that runs every command without asking. Cursor groups these options under Run modes (Settings > Agents > Approvals & Execution in the desktop app, checked 2026-10-12): Auto-review runs allowlisted calls at once and sends other calls to the sandbox or to its classifier, which Cursor says “is not a security boundary”; Allowlist means “Actions in your allowlist run without approval”; and Run Everything means “Every tool call runs automatically.” For this audit, use Allowlist with a short allowlist, so the decision on every other command stays with you. Repository content is untrusted input: OWASP’s LLM Top 10 2026 (published 2026-08-04) warns that attackers plant instructions in “a public GitHub issue, a support ticket, or a malicious npm package”.

Step 1: Run the dependency and static-analysis scanners yourself

Section titled “Step 1: Run the dependency and static-analysis scanners yourself”

Start with tools whose output is deterministic. An agent’s finding is a hypothesis; a scanner’s finding is reproducible evidence, and it costs no tokens. Run these in the terminal at the repository root and keep the JSON files for the agent.

Terminal window
# Dependencies: pick the lines for your ecosystems
npm audit --json > audit-npm.json
pip-audit -f json -o audit-pip.json # pip install pip-audit (2.10.1 on 2026-09-26)
cargo audit --json > audit-cargo.json # cargo install cargo-audit
# Static analysis: OWASP Top 10 ruleset
pipx run semgrep scan --config p/owasp-top-ten --json -o semgrep.json

Then hand the results to the agent for triage. The prompt asks for a plan, not changes, because dependency upgrades are where audits break working code.

The “prove it exists” line is there for a reason. OWASP’s 2026 LLM04 entry names slopsquatting: assistants suggest plausible package names that do not exist, and attackers register those names in advance.

Step 2: Scan git history for committed secrets

Section titled “Step 2: Scan git history for committed secrets”

A key deleted from the working tree is still in git history, so scan history, not only files. Gitleaks does this, and its --redact flag keeps the secret values out of the report, which matters because the agent reads that report next.

Terminal window
# brew install gitleaks, or use the container image
gitleaks git --redact --report-path gitleaks.json
# docker run --rm -v "$PWD:/repo" ghcr.io/gitleaks/gitleaks:v8.30.1 git /repo --redact --report-path /repo/gitleaks.json

Removing a leaked key from the code does not make it safe. Rotate every CRITICAL and HIGH key first; rewrite history afterwards only if policy requires it.

Step 3: Review authentication and authorization with the agent

Section titled “Step 3: Review authentication and authorization with the agent”

Scanners are weak at authorization bugs, which is where an agent review earns its cost. Run this in Plan Mode so the agent reads and reasons before it can edit anything.

The route table in item 5 is the most valuable artifact of the whole audit. A reviewer can scan 60 rows for a missing ownership check in minutes, which is much faster than reading 60 handlers.

Step 4: Review injection and API hardening

Section titled “Step 4: Review injection and API hardening”

Use a second, narrower prompt for input handling. Splitting the review keeps each context focused and each finding list short enough to check.

The cross-check against semgrep.json tells you which findings are new. Findings that only the agent reports need the most scrutiny, because they are where false positives live.

Step 5: Prove each finding with a failing test, then fix it

Section titled “Step 5: Prove each finding with a failing test, then fix it”

Do not accept a finding or a fix on the agent’s word. Each CRITICAL and HIGH finding becomes a test that demonstrates the exploit against the current code. The fix is done when that test passes and the rest of the suite still does.

Step 2 of the prompt is the false-positive filter. A finding that cannot be turned into a failing test is either not exploitable or not understood yet; neither case should produce a code change. For the wider pattern, see test-driven development in Cursor.

Step 6: Keep the audit’s results in place with CI and rules

Section titled “Step 6: Keep the audit’s results in place with CI and rules”

A one-off audit decays with the next hundred agent-written commits. Three layers keep it current.

A CI gate. Add a workflow that fails the pull request when it brings in an OWASP-ruleset hit or a secret, and whenever the dependency tree has a HIGH or CRITICAL advisory. The token is read-only, the checkout does not keep credentials, and every tool is pinned to a version. On pull_request, GitHub runs this workflow file from the pull request’s merge ref, so a change that edits security.yml (an agent’s included) can weaken or delete the gate in the same pull request. Put /.github/ under CODEOWNERS with a required code-owner review in branch protection or a ruleset, and make the scan job a required status check.

.github/workflows/security.yml
name: security
on: [pull_request]
permissions:
contents: read
jobs:
scan:
runs-on: ubuntu-latest
steps:
# Pin actions to a full commit SHA in your own repo; a tag can be moved.
- uses: actions/checkout@v7
with:
fetch-depth: 0
persist-credentials: false
# Fails on ANY high advisory, old or new. npm audit has no baseline.
- run: npm audit --audit-level=high
# Semgrep and Gitleaks report only what this pull request adds.
- env:
BASE_REF: ${{ github.base_ref }}
run: >-
pipx run semgrep==1.178.0 scan --config p/owasp-top-ten --error
--baseline-commit "origin/$BASE_REF"
- env:
BASE_REF: ${{ github.base_ref }}
run: >-
docker run --rm -v "$PWD:/repo" ghcr.io/gitleaks/gitleaks:v8.30.1
git /repo --redact --log-opts="origin/$BASE_REF..HEAD"

The three scanners treat existing findings differently, so check each against step 1 and step 2:

  • Semgrep compares against the base branch with --baseline-commit, so a finding you closed as a false positive in step 5 does not fail every later pull request. Suppress it in code with a nosemgrep comment on that line as well, so the full scan from step 1 agrees.
  • Gitleaks scans only the pull request’s commits with --log-opts. The keys from step 2 stay in history after rotation, and a full-history scan would fail every pull request for good. To keep scanning all of history instead, commit the post-rotation report from step 2 and pass it as --baseline-path gitleaks-baseline.json.
  • npm audit has no baseline and fails on any HIGH or CRITICAL advisory in the tree, including one you accepted in step 1. The gate turns green only when each such advisory is fixed or its package replaced. Record every accepted advisory in security-findings.md with its ID, an owner and a review date, so a red step is a known, dated decision and not a reason to switch the gate off.

Add a Gitleaks pre-commit hook as well, so a commit that contains a key is blocked on the laptop before it reaches CI. The setup is in the Gitleaks README. For TruffleHog, Snyk and the Semgrep Guardian plugin, see security gates for agent-written code.

A project rule. Ask the agent to write a security rule from the audit, so new code follows the fixes. Cursor’s rules are covered in project rules.

Pull request review. Cursor’s Bugbot “reviews pull requests and identifies bugs, security issues, and code quality problems”, and Security Agents “scan your code for security bugs, risky patterns, and vulnerabilities” (Cursor docs, checked 2026-08-28). Cursor’s Security Review, added for Teams and Enterprise on 2026-09-23, posts one comment per pull request on exploitable bugs (Cursor changelog, checked 2026-10-12). Treat each as an extra reviewer, not a gate: an LLM reviewer can miss what a deterministic scanner catches. Setup and trade-offs are in code review in Cursor and the AI code review bots comparison.

If you want findings while the agent writes code, not only at pull request time, Semgrep’s MCP server ships inside the semgrep binary. Install it first with pipx install semgrep (or brew install semgrep) so semgrep is on your PATH; pipx run from step 1 does not put it there. Then add it to .cursor/mcp.json:

{
"mcpServers": {
"semgrep": { "command": "semgrep", "args": ["mcp"] }
}
}

How do you sign off a Cursor security audit without reading every line?

Section titled “How do you sign off a Cursor security audit without reading every line?”

The tech lead signs off on evidence, not on the diff. Each row has an artifact that someone other than the agent can check.

ClaimEvidence to checkWho checks
No known vulnerable dependenciesnpm audit --audit-level=high (and pip-audit) exit 0 in CI, or each remaining advisory is an accepted risk with an owner and a dateCI, plus the tech lead for accepted risks
No secrets in code or historyGitleaks exits 0 on every pull request’s commits (or against the committed baseline); every finding in the step 2 report rotated, with rotation confirmedOwner of each provider account
Every route has an owner checkThe route table from step 3, reviewed row by rowTech lead
Each fix closes its findingExploit test failed before and passes after; full suite greenCI, plus the reviewer spot-checks two tests
Nothing new slips inSecurity workflow required on the default branch; rule committedTech lead

Anything marked “needs human check” in security-findings.md stays open until a person closes it. Store the file with the release notes: it is the assessment the compliance lead asked for.

The agent reports a clean result. “No vulnerabilities found” means no known pattern matched. Business-logic flaws, such as a client-set price or API keys that survive account deletion, need a threat model. Recover by asking in Plan Mode: “You are an attacker who wants free orders from this checkout. List five attempts, and the file and line each one targets.” Then turn each credible attempt into a test.

False positives pile up. An ORM call can look like string-built SQL. Recovery: the failing-test rule in step 5. If the exploit test passes on unchanged code, close the finding as a false positive with the test as proof, and do not change the code.

A fix breaks a real feature. CSRF tokens break API clients that never sent them, and a strict CORS list blocks a staging frontend. Recovery: the full suite runs after every fix, and each fix is a separate change, so you can revert one without losing the others. Add the broken client to the tests before you try again.

A dependency upgrade breaks the build. Major-version bumps change APIs. Recovery: apply group (a) upgrades from step 1 together, then do each major bump alone with the full suite. If a major bump is blocked, record the mitigation and the reachability check from step 1 as an accepted risk with an owner and a date.

The agent adds a package that does not exist or is not the one you meant. A near-miss name can be a slopsquatted package. Recovery: reject any new dependency that the agent did not verify on the registry, check the publisher and download history yourself, and keep npm audit and the lockfile diff in review.

Repository text steers the agent. A comment, fixture, or README that says “ignore previous instructions” is aimed at the auditor. Recovery: keep command approval on, stop the session when the agent proposes an action that the prompt did not ask for, and report the file as a finding in its own right.

Where to go next after a Cursor security audit

Section titled “Where to go next after a Cursor security audit”

Back to the Cursor operate overview.