Node2Shell (CVE-2026-111497) affects Node.js: read how to fix it

Guides

Is my Cursor app secure?

Written by our founder, a cybersecurity architect · Updated

Short answer

Only as secure as the code you accept. Cursor is an editor, so it makes no security decisions for you, and the mistakes it makes most often are the ones every AI tool makes: secret keys in frontend code, API routes that don’t check who is calling, and database tables without access rules.

Because a Cursor app lives in a Git repository, you can check it in two places: the code, before each change ships, and the live site, after it does.

What does Cursor get wrong most often?

  • Secrets in public variables: NEXT_PUBLIC_ in Next.js, VITE_ in Vite.
  • API routes and server actions that take an id from the request and return the record without checking it belongs to the signed-in user.
  • Prices, credits or limits read from the request body instead of computed on the server.
  • Supabase tables created by a migration without Row Level Security.
  • Packages added at versions the model remembers, which may have known vulnerabilities.

1. Does any secret reach the browser?

In Next.js, every variable starting with NEXT_PUBLIC_ is copied into the JavaScript the browser downloads. Secrets must use other names and be read only in server code: route handlers, server actions and server components.

To check, build the app and search .next/static/ for your key prefixes, or search the live site from the browser’s developer tools. Our guide on finding API keys in your JavaScript has the commands and the list of keys that are safe to be public.

2. Does every API route check the user?

Each route that reads or changes data should check who is calling and whether that record is theirs. Filter by the owner in the query itself:

app/api/orders/[id]/route.ts
export async function GET(req: Request, { params }: { params: Promise<{ id: string }> }) {
  const user = await getUser(req); // from your auth library
  if (!user) return new Response('Unauthorized', { status: 401 });
  const { id } = await params;
  const order = await db.order.findFirst({ where: { id, userId: user.id } });
  if (!order) return new Response('Not found', { status: 404 });
  return Response.json(order);
}

Server actions need the same check. Next.js exposes each one as an endpoint that anyone can call with any arguments, whether or not your page shows the button.

3. Are your database rules set?

If the browser talks to Supabase or Firebase directly, the database rules are your only protection. In Supabase, every table in public needs Row Level Security and policies that check auth.uid(); our Supabase RLS guide shows how to check and test them. In Firebase, look for rules like allow read, write: if true, which let anyone in.

4. Can I make Cursor write safer code?

A project rule set to always apply is added to every request Cursor makes in your repository. A short security rule makes the safe version more likely:

.cursor/rules/security.mdc
---
description: Security rules
alwaysApply: true
---

- Never put secrets in NEXT_PUBLIC_ or VITE_ variables; read them only in server code.
- Every API route and server action checks the signed-in user and that they own the record.
- Compute prices, credits and limits on the server, never from the request.
- Every new Supabase table gets row level security and policies in the same migration.

Rules make mistakes less likely, but they don’t prevent them. Read every change that touches sign-in, payments or the database before you accept it.

5. How do I check every pull request automatically?

Our code scan runs as a GitHub Action on every push and pull request, free for public repositories and 2 private ones a month. It looks for secrets with Gitleaks, vulnerable dependencies with OSV, and risky patterns in JavaScript and TypeScript with our own Semgrep rules (injection, XSS, SSRF, unverified JWTs, Stripe amounts taken from the request, Supabase tables without RLS). An AI review of changed API routes and Supabase policies is added and labelled as such; it never changes your grade. Results come back as a check and a comment on the pull request, and your code is scanned in a sandbox with no network access and never stored.

Our free scan checks the live site for leaked keys, exposed files and missing security headers. Neither scan signs in, so test by hand that a second account can’t read the first one’s data.

Checklist

  • No secret is in a NEXT_PUBLIC_ or VITE_ variable
  • Every API route and server action checks the user and their ownership of the record
  • Prices and limits are computed on the server
  • Every Supabase table has RLS; no Firebase rule allows everyone
  • Dependencies have no known vulnerabilities
  • A security rules file is in .cursor/rules
  • A second test account can’t read the first one’s data

Scan your site for free

Leaked API keys, exposed files and missing security headers, with a fix for each. No account, about a minute.

Free, no signup needed

Questions

Are NEXT_PUBLIC_ variables secret?
No. Next.js copies every NEXT_PUBLIC_ variable into the JavaScript sent to the browser. Use them only for values meant to be public.
Are Next.js server actions protected?
Not by themselves. Anyone can call a server action with any arguments, like an API route, so check the user and their rights inside each one.
Do Cursor rules make my code secure?
They help Cursor choose the safe version more often, but they don’t guarantee it. Review changes to sign-in, payments and database access, and scan the code on each pull request.
Can I scan my Cursor app for free?
Yes, our free scan checks the live site for leaked keys, exposed files and missing security headers in about a minute. Scanning the code on each pull request is free too, for public repositories and 2 private ones a month.

Related guides

Is my Cursor app secure? · Afterprompt