nextjs-security — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited nextjs-security (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
This skill is the Next.js-specific layer on top of secure-coding and api-security. Next.js has gone through several architectural shifts in recent years (Pages Router, App Router, Server Actions, middleware evolution), and the security implications shift with them. It is also used by teams that do not have a sharp line between front-end and back-end — and that is exactly where bugs live.
Triggers on:
next.config.js/.mjs/.ts, middleware.ts in the project root, an app/ directory with page.tsx and layout.tsx, pages/ (older router), "use server" or "use client" directives, NextAuth/auth.js config.security-review or api-security when Next.js is in the stack.secure-coding.api-security. Here for the Next.js implementation.p/javascript) → sast-orchestrator.package.json / package-lock.json → cve-triage.iac-security where possible, otherwise platform-specific docs..env.local or Vercel env vars → secrets-scanner.Seven phases. Phase 1 (middleware and auth-bypass) is the heart — that is where the CVE-2025-29927-class bug sat and where architectural misconceptions cause problems most often.
Next.js middleware runs before every matching request and is often used for authentication gates and redirects. That is precisely why it is an attack surface.
[verify against https://nextjs.org/blog] and https://github.com/vercel/next.js/security/advisories): An internal header that Next.js used for infinite-loop detection could be set externally, causing middleware to be skipped. Effect: middleware-based auth could be bypassed. Patched in 14.2.25, 15.2.3. Reviewer impact questions:return NextResponse.redirect(new URL(request.nextUrl.searchParams.get('next') ?? '/', request.url)) — if next is not validated, this is an open redirect. Allowlist destinations.export const config = { matcher: '/admin/:path*' } — a too-narrow matcher does not protect what you thought it protected. Verify all sensitive paths are matched.Server Actions ("use server") are RPC endpoints called from a Client Component but executing as server code. They share security concerns with API routes, plus a few of their own.
'use server';
export async function deletePost(id: string) {
const session = await auth(); // do not skip
if (!session) throw new Error('unauth');
const post = await db.post.findUnique({ where: { id } });
if (post.authorId !== session.user.id) throw new Error('forbidden'); // IDOR fix
await db.post.delete({ where: { id } });
}FormData or directly-called args. Validate with zod/valibot/yup. Trust nothing that comes from the client.Origin and Host headers. Custom proxy setups can break this; verify Next.js' CSRF guard is still active behind your proxy.The App Router introduced Server Components and Client Components. The boundary is meaningful at runtime: what is in a Server Component does not end up in the client bundle, what is in a Client Component does.
password_hash, no other user's data that happened to come along in your query, no feature flags that competitors should not see.NEXT_PUBLIC_* vars are available. Anything else reads as undefined. That is the right default; but an accidentally renamed NEXT_PUBLIC_API_KEY leaks into the production bundle. Grep for NEXT_PUBLIC_ and confirm everything explicitly belongs in public.api-security). Auth in the handler, not only in the UI.auth.js (rebranded from NextAuth v5) is the de facto auth library for Next.js.
database or jwt. JWT strategy stuffs all session data into an encrypted JWT cookie — fast reads, no server-side invalidation. Database strategy has invalidation but requires a DB hit per request. The choice is a trade-off; the JWT default is reasonable for small apps, database for anything where logout really must invalidate.openssl rand -base64 32), in vault or env var, different per environment. See secrets-scanner.authorize function yourself, all credential validation is your responsibility. Verify password hashing (argon2id via the argon2 package or bcrypt), and timing-safe comparison.pages.signIn etc. explicit. No dynamic redirect URL from a callbackUrl query param without an allowlist check.HttpOnly, Secure (in prod), SameSite=Lax by default. Do not override unless necessary.remotePatterns in next.config.js is the allowlist for where Next.js may fetch images. A wildcard hostname is effectively an image-proxy SSRF: someone loads /_next/image?url=<internal-URL> and uses your server to reach internal endpoints. Scope remotePatterns to your exact CDN/bucket domain.Canonical example of this class: [verify against CVE database and Next.js advisories — multiple image-proxy related CVEs have appeared].
FormData with File — validate server-side on content type, size, magic bytes. Client File.type is spoofable. Store outside public/ to prevent path traversal.api-security phase 5). Block private IP ranges, DNS-rebinding protection.Headers are configured in next.config.js headers() or in middleware.
// next.config.mjs
const securityHeaders = [
{ key: 'Strict-Transport-Security', value: 'max-age=63072000; includeSubDomains; preload' },
{ key: 'X-Content-Type-Options', value: 'nosniff' },
{ key: 'X-Frame-Options', value: 'DENY' },
{ key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
{ key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=()' },
{ key: 'Content-Security-Policy', value: "default-src 'self'; script-src 'self' 'nonce-<nonce>'; ..." },
];
export default {
async headers() {
return [{ source: '/:path*', headers: securityHeaders }];
},
};'unsafe-inline' and 'unsafe-eval' where possible — they largely null out CSP's XSS protection.preload only if you are on the HSTS preload list or want to be. max-age at least 1 year for preload eligibility.|safe. Only on sanitized content, never directly on user input. Use DOMPurify or sanitize-html for user-generated HTML.robots.txt, to keep staging out of Google by accident.Verification-loop: Layer 1 (next.config, middleware, layout/page auth checks, Server Actions all walked through?), assumptions ("auth.js works by default" only if you have seen the actual config). Layer 2 (CVE-2025-29927 with fix versions checked against nextjs.org advisories, auth.js v5 API names correct, no invented Next.js version ranges in fix claims).
Next.js security review — <app>
Next.js: <x.y.z> | Router: <App | Pages | mixed>
auth.js / NextAuth: <version, provider list>
Middleware:
Present: <yes/no>
Auth via middleware: <only layer | part of defense-in-depth>
Matchers: <scope>
CVE-2025-29927 patched:<yes/no>
Open redirects: <list>
Server Actions (App Router):
Number of actions: N
Auth check per action: <yes for all | gaps on ...>
Input validation: <zod/valibot/... | none>
Rate-limited: <yes/no>
Server/Client boundary:
NEXT_PUBLIC_* exposure: <list, review>
Secrets in Client props:<grep for password/token/key>
Hydration leaks: <visible in dev tools?>
auth.js:
AUTH_SECRET source: <vault/env>
Session strategy: <jwt | database>
Providers: <OAuth + PKCE | credentials + hashing scheme>
Callback-URL allowlist:<yes/no>
next/image:
remotePatterns scope: <specific | wildcard — FINDING>
Security headers:
HSTS: <on + preload>
CSP: <nonce/hash | unsafe-inline — FINDING>
Frame-Options: <DENY/SAMEORIGIN>
Version check:
Latest security release: <date>
cve-triage handoff: <N deps with vulns>
Findings (severity-sorted, follow security-review format)
Verification-loop: ...dangerouslySetInnerHTML.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.