All skills
sugarforever avatar

/nextjs-security-scan

@d957e45

Comprehensive security vulnerability scanner for Next.js and TypeScript/JavaScript projects. Detects OWASP Top 10 vulnerabilities, XSS, injection flaws, authentication issues, hardcoded secrets, and Next.js-specific security problems. Audits dependencies for known CVEs and generates actionable security reports.

Use this Skill: https://skilld.dev/gh/sugarforever/01coder-agent-skills/nextjs-security-scan

This session only. Nothing lands on disk.

referencesnextjs-specific.md

≈2.2k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Next.js Specific Security Vulnerabilities

Overview

Next.js has specific security considerations due to its hybrid rendering model, Server Components, Server Actions, and middleware architecture. This document covers Next.js-specific vulnerabilities and recent CVEs.


Recent Critical CVEs

CVE-2025-29927 - Middleware Authorization Bypass

Severity: CRITICAL (CVSS 9.1) Affected Versions: All versions before patches Status: Patched

Description: Attackers can bypass middleware-based security checks by adding an x-middleware-subrequest header to requests.

Detection:

// ❌ VULNERABLE - Relying solely on middleware for auth
// middleware.ts
export function middleware(request: NextRequest) {
  const token = request.cookies.get('session');
  if (!token) {
    return NextResponse.redirect('/login');
  }
}

// No additional auth check in the protected route

Remediation:

  1. Upgrade Next.js to patched version
  2. Never rely solely on middleware for authentication
  3. Implement defense in depth
// ✅ SAFE - Multi-layer authentication
// middleware.ts (first layer)
export function middleware(request: NextRequest) {
  const token = request.cookies.get('session');
  if (!token) {
    return NextResponse.redirect('/login');
  }
}

// page.tsx or API route (second layer)
export default async function ProtectedPage() {
  const session = await auth();
  if (!session) {
    redirect('/login');
  }
  // ...
}

CVE-2025-55182 / CVE-2025-66478 - React Server Components Vulnerability

Severity: CRITICAL Affected Versions: Next.js ≥14.3.0-canary.77, ≥15, ≥16 Status: Patched

Description: Remote code execution vulnerability in React Server Components (react-server-dom-*).

Remediation:

  1. Upgrade to patched Next.js version immediately
  2. Rotate all application secrets after patching
  3. Review server logs for suspicious activity

Server Components Data Exposure

Patterns to Detect

Severity: HIGH

// ❌ VULNERABLE - Passing sensitive data to Client Components
// Server Component
async function UserProfile() {
  const user = await db.user.findUnique({
    include: { passwordHash: true }  // Includes sensitive data!
  });

  return <ClientProfileCard user={user} />;  // Leaked to client!
}

// ❌ VULNERABLE - Exposing API keys
async function DataFetcher() {
  const data = await fetch(url, {
    headers: { 'Authorization': process.env.API_KEY }
  });

  // If this becomes a Client Component, API_KEY might leak
  return <div>{JSON.stringify(data)}</div>;
}

Safe Alternative:

// ✅ SAFE - Only pass necessary data
async function UserProfile() {
  const user = await db.user.findUnique({
    select: {
      id: true,
      name: true,
      email: true
      // No sensitive fields
    }
  });

  return <ClientProfileCard user={user} />;
}

Server Actions Security Issues

Missing Server Action Validation

Severity: HIGH

// ❌ VULNERABLE - No input validation
'use server'

export async function createPost(formData: FormData) {
  const title = formData.get('title');
  const content = formData.get('content');

  await db.post.create({
    data: { title, content }  // Unvalidated input!
  });
}

Safe Alternative:

// ✅ SAFE - With validation
'use server'

import { z } from 'zod';

const postSchema = z.object({
  title: z.string().min(1).max(200),
  content: z.string().min(1).max(10000)
});

export async function createPost(formData: FormData) {
  const result = postSchema.safeParse({
    title: formData.get('title'),
    content: formData.get('content')
  });

  if (!result.success) {
    return { error: result.error.flatten() };
  }

  await db.post.create({ data: result.data });
}

Server Action Size Limits

// next.config.js - Set appropriate limits
module.exports = {
  experimental: {
    serverActions: {
      bodySizeLimit: '2mb',  // Prevent DoS via large payloads
      allowedOrigins: ['example.com']  // CSRF protection
    }
  }
}

Environment Variable Exposure

Patterns to Detect

Severity: CRITICAL

// ❌ VULNERABLE - Server env in client code
// Client Component
'use client'

export function ApiCaller() {
  // This won't work AND indicates a security misunderstanding
  const apiKey = process.env.API_KEY;
}

// ❌ VULNERABLE - Exposing server vars via NEXT_PUBLIC_
// .env
NEXT_PUBLIC_DATABASE_URL=postgresql://...  // WRONG!
NEXT_PUBLIC_API_SECRET=sk-...  // WRONG!

Detection Regex:

NEXT_PUBLIC_.*SECRET
NEXT_PUBLIC_.*KEY
NEXT_PUBLIC_.*PASSWORD
NEXT_PUBLIC_.*TOKEN
NEXT_PUBLIC_.*DATABASE

Safe Pattern:

# .env - Server-only (no NEXT_PUBLIC_ prefix)
DATABASE_URL=postgresql://...
API_SECRET=sk-...

# .env - Client-safe only
NEXT_PUBLIC_APP_URL=https://example.com
NEXT_PUBLIC_ANALYTICS_ID=GA-123

Security Headers Configuration

Missing Headers

Severity: MEDIUM

// ❌ MISSING - No security headers configured
// next.config.js
module.exports = {
  // No headers configuration
}

Safe Alternative:

// ✅ SAFE - Comprehensive security headers
// next.config.js
const securityHeaders = [
  {
    key: 'X-DNS-Prefetch-Control',
    value: 'on'
  },
  {
    key: 'Strict-Transport-Security',
    value: 'max-age=63072000; includeSubDomains; preload'
  },
  {
    key: 'X-Frame-Options',
    value: 'SAMEORIGIN'
  },
  {
    key: 'X-Content-Type-Options',
    value: 'nosniff'
  },
  {
    key: 'Referrer-Policy',
    value: 'origin-when-cross-origin'
  },
  {
    key: 'Content-Security-Policy',
    value: "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';"
  }
];

module.exports = {
  async headers() {
    return [
      {
        source: '/:path*',
        headers: securityHeaders
      }
    ];
  }
};

Image Component Vulnerabilities

Patterns to Detect

Severity: MEDIUM

// ❌ VULNERABLE - Unrestricted remote images
// next.config.js
module.exports = {
  images: {
    remotePatterns: [
      { protocol: 'https', hostname: '**' }  // Too permissive!
    ]
  }
}

// ❌ VULNERABLE - User-controlled image src
<Image src={userProvidedUrl} alt="User image" />

Safe Alternative:

// ✅ SAFE - Restricted domains
module.exports = {
  images: {
    remotePatterns: [
      { protocol: 'https', hostname: 'cdn.example.com' },
      { protocol: 'https', hostname: 'images.example.com' }
    ]
  }
}

// ✅ SAFE - Validate image URLs
const isAllowedImageHost = (url: string) => {
  const allowed = ['cdn.example.com', 'images.example.com'];
  try {
    const { hostname } = new URL(url);
    return allowed.includes(hostname);
  } catch {
    return false;
  }
};

Redirect and Rewrite Vulnerabilities

Open Redirect

Severity: MEDIUM

// ❌ VULNERABLE - Open redirect
// app/api/redirect/route.ts
export async function GET(request: Request) {
  const url = request.nextUrl.searchParams.get('url');
  return NextResponse.redirect(url!);  // Open redirect!
}

// ❌ VULNERABLE - redirect() with user input
import { redirect } from 'next/navigation';

export default function Page({ searchParams }) {
  if (someCondition) {
    redirect(searchParams.next);  // Open redirect!
  }
}

Safe Alternative:

// ✅ SAFE - Validate redirect URL
const ALLOWED_HOSTS = ['example.com', 'app.example.com'];

function safeRedirect(url: string): string {
  try {
    const parsed = new URL(url, 'https://example.com');
    if (ALLOWED_HOSTS.includes(parsed.hostname)) {
      return parsed.toString();
    }
  } catch {}
  return '/';  // Default to home
}

export async function GET(request: Request) {
  const url = request.nextUrl.searchParams.get('url') || '/';
  return NextResponse.redirect(safeRedirect(url));
}

Scan Checklist

  • Next.js version is up to date (check for CVEs)
  • Middleware is not sole authentication layer
  • Server Components don't leak sensitive data to client
  • Server Actions have input validation
  • Server Actions have authentication checks
  • No sensitive env vars use NEXT_PUBLIC_ prefix
  • Security headers are configured
  • Image domains are restricted
  • No open redirects
  • serverActions.bodySizeLimit is configured
  • serverActions.allowedOrigins is configured

Version Check Commands

# Check Next.js version
npm list next

# Check for outdated packages
npm outdated

# Check for known vulnerabilities
npm audit

Source: SKILL.md on GitHub

2 alerts5mo5 checks · Risk HIGH
  • Gen Agent Trust Hub7mo

    This skill performs security audits on Next.js projects using shell scripts and regex patterns. Its primary security risk is Indirect Prompt Injection, as it processes untrusted source code and configuration files without sanitization or boundary markers. It also executes commands on user-controlled directories.

  • Socket7mo

    1 alert: obfuscatedFile

  • Snyk7mo

    Risk: LOW · No issues

  • Runlayer7mo

    10/10 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at d957e45. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 days ago.

Steadyupdated 9 months ago

README badge

README badge for sugarforever/01coder-agent-skills/nextjs-security-scan