Security audit workflow - vulnerability scan β verification
npx skills add soilmass/vibe-coding-plugin --skill "security"
Install specific skill from multi-skill repository
# Description
>
# SKILL.md
name: security
description: >
Next.js 15 application security β CSP headers, server-only imports, env validation with Zod, CSRF protection, input sanitization, rate limiting
allowed-tools: Read, Grep, Glob
Security
Purpose
Application security hardening for Next.js 15. Covers CSP headers, server-only boundaries,
environment validation, and input sanitization. The ONE skill for defense-in-depth patterns.
When to Use
- Adding Content Security Policy headers
- Protecting server-only code from client bundles
- Validating environment variables at startup
- Implementing rate limiting on API routes
When NOT to Use
- Authentication/authorization setup β
auth - Form input validation β
react-forms - Server Action input validation β
react-server-actions
Pattern
server-only guard
// lib/db.ts
import "server-only"; // Throws build error if imported in client component
import { PrismaClient } from "@prisma/client";
export const db = new PrismaClient();
Environment validation with Zod
// lib/env.ts
import { z } from "zod";
const envSchema = z.object({
DATABASE_URL: z.string().url(),
AUTH_SECRET: z.string().min(32),
NEXT_PUBLIC_APP_URL: z.string().url(),
});
export const env = envSchema.parse(process.env);
CSP headers via next.config.ts
// next.config.ts
const config = {
async headers() {
return [
{
source: "/(.*)",
headers: [
{
key: "Content-Security-Policy",
// DEVELOPMENT ONLY β 'unsafe-inline' and 'unsafe-eval' disable CSP protection
// For production, use nonce-based CSP via middleware (see below)
value: [
"default-src 'self'",
"script-src 'self' 'unsafe-inline' 'unsafe-eval'",
"style-src 'self' 'unsafe-inline'",
"img-src 'self' data: https:",
].join("; "),
},
{ key: "X-Frame-Options", value: "DENY" },
{ key: "X-Content-Type-Options", value: "nosniff" },
{ key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
{ key: "Permissions-Policy", value: "camera=(), microphone=(), geolocation=()" },
],
},
];
},
};
export default config;
Server Action authorization check
"use server";
import { auth } from "@/lib/auth";
export async function deletePost(id: string) {
const session = await auth();
if (!session?.user) throw new Error("Unauthorized");
const post = await db.post.findUnique({ where: { id } });
if (post?.authorId !== session.user.id) throw new Error("Forbidden");
await db.post.delete({ where: { id } });
}
CORS configuration for API routes
// src/lib/cors.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
const ALLOWED_ORIGINS = [
process.env.NEXT_PUBLIC_APP_URL,
"https://your-other-domain.com",
].filter(Boolean);
export function corsHeaders(request: NextRequest) {
const origin = request.headers.get("origin") ?? "";
const isAllowed = ALLOWED_ORIGINS.includes(origin);
return {
"Access-Control-Allow-Origin": isAllowed ? origin : "",
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type, Authorization",
"Access-Control-Max-Age": "86400",
};
}
CSP middleware pattern (dynamic nonces)
// src/middleware.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
export function middleware(request: NextRequest) {
const nonce = Buffer.from(crypto.randomUUID()).toString("base64");
const csp = [
`default-src 'self'`,
`script-src 'self' 'nonce-${nonce}' 'strict-dynamic'`,
`style-src 'self' 'nonce-${nonce}'`,
`img-src 'self' data: https:`,
`font-src 'self'`,
`frame-ancestors 'none'`,
`base-uri 'self'`,
`form-action 'self'`,
].join("; ");
const response = NextResponse.next();
response.headers.set("Content-Security-Policy", csp);
response.headers.set("x-nonce", nonce);
return response;
}
server-only import guard (import in any sensitive module)
// Any server-only module β fails at build time if bundled for client
import "server-only";
Dependency vulnerability scanning checklist
# Run periodically and in CI
npm audit # Check for known vulnerabilities
npm audit --fix # Auto-fix compatible updates
npx npm-check-updates -u # Check for major updates
Anti-pattern
// WRONG: trusting client-side validation alone
"use client";
function CreatePost() {
const handleSubmit = (data: FormData) => {
if (data.get("title")?.toString().length > 100) return; // Client-only check
createPost(data); // Server Action with NO server-side validation!
};
}
// WRONG: wildcard CORS (allows any origin to call your API)
headers: { "Access-Control-Allow-Origin": "*" }
// Use explicit allowed origins instead
// WRONG: CSRF vulnerability β no origin check on Server Actions
// Server Actions in Next.js 15 have built-in CSRF protection via
// the __next_action header, but custom API routes do NOT.
// Always verify Origin header on custom POST endpoints.
Client-side validation is UX. Server-side validation is security.
Always validate on the server β client checks can be bypassed.
Common Mistakes
- Missing
import "server-only"on sensitive modules (db, auth helpers) - No env validation β crashes at runtime instead of startup
- Forgetting CSP headers β allows XSS vectors
- Trusting client-side validation without server-side checks
- Exposing error stack traces in production error responses
- Wildcard CORS (
*) on authenticated endpoints β use explicit origins - No CSRF protection on custom API POST routes β verify Origin header
- Not running
npm auditin CI β known vulnerabilities go undetected
Checklist
- [ ]
import "server-only"on all sensitive server modules - [ ] Environment variables validated with Zod schema at startup
- [ ] CSP and security headers configured in
next.config.tsor middleware - [ ] All Server Actions check authentication and authorization
- [ ] Error responses never leak stack traces or internal details
- [ ] CORS configured with explicit allowed origins (no wildcard on auth routes)
- [ ] CSP does NOT use unsafe-inline/unsafe-eval in production
- [ ] Permissions-Policy header configured
- [ ]
npm auditruns in CI pipeline - [ ] Dependencies reviewed for known vulnerabilities periodically
Composes With
authβ authentication provides identity, security enforces authorizationerror-handlingβ error boundaries must not leak sensitive infoapi-routesβ route handlers need auth checks and rate limitingrate-limitingβ rate limiting is DDoS defense at the application layerloggingβ audit trails and security event logging
# Supported AI Coding Agents
This skill is compatible with the SKILL.md standard and works with all major AI coding agents:
Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.