Back to skill

Security audit

Next Best Practices

Security checks for vulnerabilities and agentic risk

Overview

This is a Next.js guidance skill with no executable payload, but several examples could lead an agent to generate insecure application code if copied as best practices.

Install only if you are prepared to treat the examples as rough Next.js notes, not secure templates. Before letting an agent apply this skill, require auth checks, runtime validation, field allowlists, secret redaction, consent/privacy review for analytics, and pinned reviewed versions for npx tools.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (5)

T09 · Insecure Skill Coding Practices

Warning
Location
route-handlers.md:99
Finding
Authentication Cookie Disclosed in JSON Response Example<![CDATA[ ## Vulnerability Details **File Location**: `route-handlers.md:99-105` **Vulnerability Type**: Authentication credential disclosure **Risk Level**: Medium ### Vulnerable Code ```tsx // Headers const authHeader = request.headers.get('authorization') // Cookies (Next.js helper) const cookieStore = await cookies() const token = cookieStore.get('token') return Response.json({ query, token }) ``` ### Technical Analysis The route-handler example reads a value named `token` from the request's cookie store and returns it in a client-visible JSON response. Authentication and session credentials should remain server-side and should only be used to authenticate or authorize the request. Returning the token can expose it to client-side JavaScript, browser extensions, HTTP response logging, observability platforms, reverse proxies, and any caller able to invoke the endpoint. The example also reads the `Authorization` header but does not validate it or use it to enforce access control. This is documentation rather than an active deployed endpoint. However, the Skill is intended to guide code generation and review, so an Agent could reproduce the insecure pattern in an application. ### Attack Path 1. A developer or Agent copies the request-helper example into a route handler. 2. An authenticated user requests the endpoint and includes the session cookie. 3. The handler retrieves the authentication token from the cookie store. 4. The handler serializes the token into its JSON response. 5. Malicious client-side code, a compromised browser extension, an unauthorized caller, or response-logging infrastructure captures the token. 6. If the token is reusable, the attacker presents it to protected endpoints and impersonates the affected user. ### Impact Assessment Successful exploitation could disclose reusable session credentials and permit account impersonation within the authorization scope of the exposed token. The resulting privileges depend on the victim ...[truncated 289 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Never serialize access tokens, refresh tokens, session identifiers, or authentication-cookie values into an API response. - Use the credential only for server-side authentication and authorization. - Return non-sensitive information such as an authenticated boolean or a minimal public user profile. - Add an explicit warning explaining that request headers and cookies may contain secrets. - Replace the example with a pattern similar to: ```tsx export async function GET(request: Request) { const session = await authenticateRequest(request) if (!session) { return Response.json({ error: 'Unauthorized' }, { status: 401 }) } return Response.json({ authenticated: true, userId: session.user.id, }) } ``` - Mark authentication cookies `HttpOnly`, `Secure`, and with an appropriate `SameSite` policy. - Configure application and infrastructure logging to redact authorization headers, cookies, and token-like response fields. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
data-patterns.md:62
Finding
Server Action Examples Perform Mutations Without Authentication or Authorization<![CDATA[ ## Vulnerability Details **File Location**: `data-patterns.md:62-71` **Vulnerability Type**: Missing authorization and input validation in state-changing Server Actions **Risk Level**: Medium ### Vulnerable Code ```tsx export async function createPost(formData: FormData) { const title = formData.get('title') as string; await db.post.create({ data: { title } }); revalidatePath('/posts'); } export async function deletePost(id: string) { await db.post.delete({ where: { id } }); revalidateTag('posts'); } ``` ### Technical Analysis Server Actions remain remotely callable server entry points and must be treated as untrusted request handlers. The examples create and delete database records without showing: - Authentication of the caller. - Authorization for the requested operation. - Object-level authorization for the selected post. - Runtime validation of `formData` or `id`. - Length, format, or content limits. - Rate limiting or abuse controls. The TypeScript assertion `as string` does not perform runtime validation. An attacker can submit a missing, malformed, oversized, or otherwise unexpected value. The deletion action accepts an arbitrary identifier without verifying that the caller owns the record or has an administrative role. These examples are not active application code, but they can propagate insecure implementation patterns when the Skill is used to generate application mutations. ### Attack Path 1. A developer or Agent implements the documented Server Actions without adding access-control checks. 2. An attacker discovers or invokes the action endpoint from a crafted client request. 3. The attacker submits arbitrary post content or supplies the identifier of another user's post. 4. The server trusts the caller-controlled values and executes the ORM mutation. 5. The attacker creates unauthorized content or deletes records outside their permitted scope. ### Impact Assessment The pattern can lead to unauthorized record cre ...[truncated 438 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Authenticate the caller inside every state-changing Server Action. - Perform role-based and object-level authorization before each mutation. - Validate all inputs at runtime using a schema validator such as Zod, Valibot, or an equivalent library. - Do not rely on TypeScript assertions as security validation. - Normalize and constrain identifiers, strings, and payload sizes. - Scope database queries to the authenticated principal instead of querying only by caller-supplied IDs. - Apply rate limiting and audit logging to sensitive operations. - Return generic errors that do not disclose internal database details. - Update the example to demonstrate a secure pattern: ```tsx 'use server' export async function deletePost(idInput: unknown) { const session = await requireSession() const id = PostIdSchema.parse(idInput) const post = await db.post.findUnique({ where: { id }, select: { ownerId: true }, }) if (!post || post.ownerId !== session.user.id) { throw new Error('Not authorized') } await db.post.delete({ where: { id } }) revalidateTag('posts') } ``` ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
data-patterns.md:113
Finding
Route Handler Passes Unvalidated Request Body Directly to ORM<![CDATA[ ## Vulnerability Details **File Location**: `data-patterns.md:113-120` **Vulnerability Type**: Mass assignment and missing API authorization **Risk Level**: Medium ### Vulnerable Code ```tsx // POST for mutations export async function POST(request: NextRequest) { const body = await request.json(); const post = await db.post.create({ data: body }); return NextResponse.json(post, { status: 201 }); } ``` ### Technical Analysis The entire attacker-controlled JSON body is passed directly to the ORM's `create` operation. This is a mass-assignment pattern: any field accepted by the model may become caller-controlled, including fields that the public API did not intend to expose. The example also omits authentication, authorization, request-size limits, content-type validation, schema validation, and explicit field allowlisting. Depending on the database schema, an attacker may attempt to set ownership, publication status, moderation state, tenant identifiers, pricing, or other protected properties. Returning the complete ORM result can also expose internal fields if the model contains data that should not be public. ### Attack Path 1. A developer or Agent copies the example into a public Route Handler. 2. An attacker sends a crafted JSON object containing both expected and protected model properties. 3. `request.json()` parses the object without schema enforcement. 4. The complete object is supplied as the ORM `data` argument. 5. The ORM persists accepted fields under attacker-selected values. 6. The response may reveal the resulting internal database representation. ### Impact Assessment Potential consequences include unauthorized record creation, protected-field modification during creation, tenant-boundary violations, workflow bypass, and exposure of internal fields. Exact privileges depend on the ORM schema, database constraints, and database service account. The issue does not itself provide system-level access, but it can compromise a ...[truncated 59 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Authenticate and authorize the caller before processing the mutation. - Parse requests with a strict runtime schema. - Reject unknown fields. - Construct the ORM payload from an explicit allowlist rather than spreading or forwarding the request body. - Derive protected fields such as `ownerId` or `tenantId` from the authenticated session. - Return an explicit response DTO rather than the complete database record. - Enforce request-body size and content-type limits. - Use a pattern such as: ```tsx const CreatePostSchema = z.object({ title: z.string().trim().min(1).max(200), content: z.string().max(50_000), }).strict() export async function POST(request: NextRequest) { const session = await requireSession() const input = CreatePostSchema.parse(await request.json()) const post = await db.post.create({ data: { title: input.title, content: input.content, ownerId: session.user.id, }, select: { id: true, title: true, content: true, }, }) return NextResponse.json(post, { status: 201 }) } ``` ]]>

T08 · Insecure Dependencies

Warning
Location
async-patterns.md:86
Finding
Mutable Codemod Package Is Downloaded and Executed Through npx<![CDATA[ ## Vulnerability Details **File Locations**: - `async-patterns.md:86` - `file-conventions.md:136` **Vulnerability Type**: Unpinned third-party package execution **Risk Level**: Medium ### Vulnerable Code ```bash npx @next/codemod@latest next-async-request-api . ``` ```bash npx @next/codemod@latest upgrade ``` ### Technical Analysis The commands instruct users to resolve the mutable `@latest` tag and immediately execute the downloaded package. The package is relevant to Next.js migration and is not shown to be malicious, but mutable resolution prevents reproducible review and allows the effective executable code to change after the Skill has been audited. An `npx`-executed package runs with the invoking user's privileges and may read or modify the current project. Codemods are intentionally capable of rewriting source files, increasing the effect of a compromised package, compromised maintainer account, registry incident, or malicious transitive dependency. This is a supply-chain weakness rather than evidence that the referenced package currently contains malicious code. ### Attack Path 1. A developer follows the migration instruction. 2. `npx` resolves `@next/codemod@latest` from the package registry. 3. The resolved package or one of its dependencies has been compromised or unexpectedly changed. 4. Package or tool code executes with the developer's local account permissions. 5. The code can modify project files and may access resources available to that user. 6. Malicious changes may subsequently enter source control, build artifacts, or deployed releases. ### Impact Assessment A compromised package could alter source code, steal locally accessible development credentials, tamper with build configuration, or introduce a backdoor into generated application code. The maximum scope is bounded by the operating-system permissions, filesystem access, and credentials available to the user running `npx`. No present compromise, privilege escalati ...[truncated 71 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Replace `@latest` with an exact, reviewed package version. - Record the package and transitive dependencies in a lockfile. - Install the tool separately before execution so the resolved artifact can be reviewed. - Use registry provenance, signatures, integrity metadata, or checksums where supported. - Run codemods only after committing or backing up the working tree. - Execute migration tools in an isolated container or restricted environment without production credentials. - Review the resulting diff before committing or building. - Prefer a command pattern such as: ```bash npm install --save-dev --save-exact @next/codemod@<reviewed-version> npx --no-install @next/codemod next-async-request-api . ``` - Document the reviewed version explicitly and update it through a controlled dependency-review process. ]]>

T08 · Insecure Dependencies

Warning
Location
self-hosting.md:314
Finding
OpenNext and SST Tooling Is Executed Without Exact Version Pinning<![CDATA[ ## Vulnerability Details **File Location**: `self-hosting.md:314-316` **Vulnerability Type**: Unpinned deployment-tool execution **Risk Level**: Medium ### Vulnerable Code ```bash npx create-sst@latest # or npx @opennextjs/aws build ``` ### Technical Analysis Both commands may download and execute third-party package code. The first explicitly resolves a mutable `@latest` version, while the second does not specify a version and therefore does not guarantee that users execute an artifact previously reviewed with the Skill. Deployment and build tools commonly receive broad access to application source code, environment variables, cloud configuration, and generated artifacts. Consequently, compromise of the package, its maintainer account, the registry, or a transitive dependency can have a larger impact than a normal runtime dependency. The tooling is relevant to the declared self-hosting functionality, and no evidence shows that the named packages are currently malicious. The finding concerns insufficient supply-chain controls. ### Attack Path 1. A user follows the self-hosting instructions in a development or deployment environment. 2. `npx` downloads the currently resolved package and dependencies. 3. A compromised or unexpectedly changed package executes during project creation or build. 4. The executed code reads or alters resources accessible to the invoking process. 5. If cloud credentials or deployment configuration are present, the malicious code may misuse those credentials or tamper with build output. 6. Compromised output may be deployed to production. ### Impact Assessment Potential impact includes source-code modification, build compromise, exposure of environment variables, theft of accessible cloud credentials, and deployment of tampered artifacts. The scope depends on where the command is run and the permissions assigned to the local or CI identity. The audited project does not itself include a remote payload, malicious pack ...[truncated 71 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Pin `create-sst` and `@opennextjs/aws` to exact reviewed versions. - Commit and enforce a lockfile in development and CI. - Use `npm ci` or an equivalent frozen-lockfile installation mode. - Separate dependency installation from execution and use `npx --no-install` after installation. - Verify package provenance and integrity metadata where available. - Run build tools with a least-privileged CI identity. - Do not expose unrelated production secrets to dependency installation or build steps. - Restrict cloud credentials to the exact resources and actions required for deployment. - Review generated files and build artifacts before deployment. - Execute untrusted or newly upgraded build tooling in a sandboxed environment. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Output HandlingUnvalidated Output Injection, Cross-Context Output, Unbounded Output
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (17)

Unvalidated Output Injection

High
Category
Output Handling
Content
```tsx
// Bad: Missing id
<Script dangerouslySetInnerHTML={{ __html: 'console.log("hi")' }} />

// Good: Has id
<Script id="my-script" dangerouslySetInnerHTML={{ __html: 'console.log("hi")' }} />
Confidence
65% confidence
Finding
Model output is used without validation or sanitization. Unvalidated output injected into downstream contexts (SQL, shell, HTML) enables injection attacks and arbitrary code execution.

Unvalidated Output Injection

High
Category
Output Handling
Content
```tsx
// Bad: Missing id
<Script dangerouslySetInnerHTML={{ __html: 'console.log("hi")' }} />

// Good: Has id
<Script id="my-script" dangerouslySetInnerHTML={{ __html: 'console.log("hi")' }} />
Confidence
65% confidence
Finding
Model output is used without validation or sanitization. Unvalidated output injected into downstream contexts (SQL, shell, HTML) enables injection attacks and arbitrary code execution.

Rp1

Medium
Category
MCP Rug Pull
Confidence
95% confidence
Finding
The documentation instructs users to run `npx @next/codemod@latest ...`, which fetches and executes remote package code without pinning a specific version. If the upstream package is compromised or a malicious release is published, users following this guidance could execute attacker-controlled code on their machine or in CI.

External Transmission

Medium
Category
Data Exfiltration
Content
const users = await db.user.findMany();

  // Or fetch from external API
  const posts = await fetch('https://api.example.com/posts').then(r => r.json());

  return (
    <ul>
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
79% confidence
Finding
This markdown file instructs users to call a local MCP endpoint and highlights tools such as `get_project_metadata` and `get_logs`, which return the project path, dev server URL, and log file location. The description does not warn that these responses may reveal sensitive local system or project information during debugging.

External Transmission

Medium
Category
Data Exfiltration
Content
The endpoint uses JSON-RPC 2.0 over HTTP POST:

```bash
curl -X POST http://localhost:<port>/_next/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Rp1

Medium
Category
MCP Rug Pull
Confidence
97% confidence
Finding
The documentation instructs users to run `npx @next/codemod@latest upgrade`, which fetches and executes the latest published package version at runtime rather than a reviewed, pinned version. If the package or one of its resolution paths were compromised, users following this guidance could execute unintended code on their local machine or CI environment.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The example reads a token from cookies and includes it in a JSON response, which teaches a pattern that exposes sensitive authentication material back to the client. In documentation for Next.js route handlers, readers may copy this directly into production code, increasing the chance of token leakage through browser-visible responses, logs, caches, or client-side script access.

Ssd 3

Medium
Confidence
98% confidence
Finding
Echoing a cookie-derived token back to the client defeats the usual purpose of keeping such credentials confined to secure cookie storage, and can make the token accessible in application responses and downstream telemetry. As a best-practices skill, this is more dangerous because it presents the behavior as a normal request-helper pattern rather than explicitly warning against exposing secrets.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
This markdown file instructs users to add Google Analytics and Google Tag Manager integrations, which send visitor and system interaction data to third parties. The section provides implementation guidance but does not mention any privacy, consent, or data-sharing implications, which is a missing warning for behavior that can affect user data and privacy.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The example recommends exposing runtime configuration through a public API endpoint and returns environment-derived values without any warning, allowlist guidance, or authentication. This pattern can easily lead developers to leak sensitive operational details or mistakenly expose secrets if they copy the example and include unsafe variables.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Missing User Warnings

Low
Confidence
84% confidence
Finding
The markdown includes a deletion mutation example that removes a post directly with `db.post.delete`, but the surrounding documentation does not mention confirmation, reversibility, or user impact. For markdown files, this qualifies as an omitted warning about behavior that could affect user data.

Natural-Language Policy Violations

Low
Confidence
83% confidence
Finding
The example sets `<html lang="en">`, which forces an English locale in the provided pattern. Because the document does not mention this as an example-only placeholder, offer alternatives, or justify an English-only constraint, it can conflict with language/locale policy expectations.

Natural-Language Policy Violations

Low
Confidence
83% confidence
Finding
This sample again uses `<html lang="en">` as a fixed locale. Repeating the pattern without caveat can encourage implementers to ship English-only markup even when their application serves other languages.

Natural-Language Policy Violations

Low
Confidence
82% confidence
Finding
The third layout example uses `lang="en"` with no surrounding guidance about localization. Across multiple examples, this presents an implicit English-only default rather than offering a user or app-specific locale choice.

Static analysis

No suspicious patterns detected.