Verified against Cursor · 2026-08-08
Run a security and secrets pass with Cursor's Agent mode before a PR goes up, not after
A pre-PR security review brief that has Agent mode check a diff for hardcoded secrets, injection risk, and unsafe trust of client input, with concrete severity classification and an explicit rule against writing a fix that just silences a warning without addressing the underlying risk.
The prompt
Ready to copy — highlighted parts are example details you can swap.
Review the changes in this session for security issues before this goes into a PR. Treat this as a dedicated security pass, not a general code review — you're looking specifically for the categories below, not restating style feedback. FILES CHANGED THIS SESSION app/api/reports/export/route.ts, lib/reports/buildQuery.ts, lib/reports/generatePdf.ts SPECIFIC RISK CATEGORIES TO CHECK SQL injection risk in buildQuery.ts since it assembles a query from report filter params; path traversal risk in generatePdf.ts since it writes a file using a user-supplied report name. TRUST BOUNDARY FOR THIS CHANGE This endpoint is reachable by any authenticated user, including the lowest-privilege role — report filter params and the report name are both fully attacker-controlled from an authenticated session. CHECK FOR, SPECIFICALLY 1. Any hardcoded secret, API key, token, or credential — including one that looks like a placeholder but would still work if accidentally left in (a real-format key with a name like "test" or "example" is still a real credential if it's actually valid). 2. Any place user-supplied input reaches a database query, shell command, file path, or template render without validation or parameterization — name the specific input and the specific sink it reaches. 3. Any place this change trusts data from the client (a request body, a query param, a cookie) to make an authorization decision, rather than re-deriving that decision server-side from something the client can't forge. 4. Any new dependency added in this change, and whether it's from a source you'd expect for this ecosystem or something unusual worth a second look before it's trusted with the access this code gives it. For each finding: cite the file and line, classify it as Critical / High / Medium / Low based on This endpoint is reachable by any authenticated user, including the lowest-privilege role — report filter params and the report name are both fully attacker-controlled from an authenticated session. — the same pattern is a different severity depending on whether the input crossing it is actually attacker-controlled here — and state the specific fix, not just the problem. Do not propose a fix that merely suppresses the symptom — escaping a string to stop a linter warning without actually parameterizing the underlying query is not a fix, it's the same vulnerability with a quieter warning. If you're not sure a proposed fix actually closes the risk rather than just changing its shape, say so explicitly rather than presenting it with unearned confidence.
Customize
Optional — swap in your own details for the highlighted parts above.
Why this works
A general "review this for issues" pass reliably surfaces style and correctness feedback because those are the most statistically common things to find in a diff, which means security-specific risks compete for attention against a much larger volume of lower-stakes observations and often lose, not because the model can't find them but because nothing in a generic prompt tells it these categories deserve to be checked specifically and exhaustively rather than opportunistically. Naming the exact RISK_CATEGORIES relevant to what this change actually touches — SQL construction in one file, path handling in another — concentrates the check the same way a scoped code-review rubric does anywhere else, and it matters more here than in a general review because a missed security finding has a categorically different cost than a missed style nit. TRUST_BOUNDARY stated explicitly is what makes severity classification meaningful rather than arbitrary: the exact same unparameterized string-concatenation pattern is a Critical finding when the input reaching it is fully attacker-controlled from an authenticated low-privilege session, and a Low or even non-finding when the same code path is only ever reachable from a trusted internal service with no user input in the chain — a model asked to rate severity without being told who can actually reach the code will either rate everything as maximally severe out of caution, which trains reviewers to stop trusting the ratings, or rate based on how dangerous the pattern looks in isolation, which misses that context entirely. The explicit rule against fixes that suppress the symptom rather than close the risk targets a specific and common failure in AI-assisted security fixes: escaping a string, adding a try/catch around a failing query, or widening a type to make a linter warning disappear are all changes that make the finding go quiet without making the underlying trust violation stop existing, and a model asked generically to "fix" a flagged issue will reach for whichever change makes the warning disappear fastest unless explicitly told that disappearing the warning and closing the risk are not the same success condition.
What you get back
1. lib/reports/buildQuery.ts:22 — Critical (attacker-controlled input, authenticated session, any role). Report filter values are concatenated directly into the SQL string via template literal, not parameterized. Fix: use the existing parameterized query builder (db.query with $1/$2 placeholders) already used elsewhere in this file for the date-range filter — the status filter on line 22 is the one place that skipped it.
2. lib/reports/generatePdf.ts:8 — High (attacker-controlled input, authenticated session). The report name is used directly in a file path passed to fs.writeFile with no sanitization — a name containing "../" could write outside the intended reports directory. Fix: sanitize with a strict allowlist regex on the name, or better, generate the filename server-side from the report's id rather than its user-editable name.
3. New dependency pdf-lite@0.9.2 added for generatePdf.ts — Medium, not a vulnerability but flagging: this is a low-download-count package for a task (PDF generation) that this repo's package.json already has a more established library available for elsewhere (pdfkit, used in lib/invoices/). Worth confirming this wasn't added just because it was the first result, before trusting it with file-write access.
Verified against
Cursor 2.2 · 2026-08-08
Changelog
- 2026-08-08 — Initial publish, verified against Cursor 2.2 Agent mode as a pre-PR security review pass.
Need this built into your business?
If a prompt isn't enough — custom software, built and maintained for you — that's Scult's day job.
EXPLORE CUSTOM SOFTWARE
