Keep Secrets and Server-Only Code Out of Next.js Client Bundles
Catches secrets behind NEXT_PUBLIC_, server modules imported into Client Components, and full database records or tokens passed to the browser.
What it flags
import { requireSession } from '@/lib/auth';
import { db } from '@/lib/db';
import { ProfileForm } from './profile-form';
export default async function SettingsPage() {
const session = await requireSession();
const user = await db.user.findUniqueOrThrow({
where: { id: session.userId },
});
// ProfileForm is a Client Component: passwordHash and apiToken ship too
return <ProfileForm user={user} />;
}import { requireSession } from '@/lib/auth';
import { db } from '@/lib/db';
import { ProfileForm } from './profile-form';
export default async function SettingsPage() {
const session = await requireSession();
const user = await db.user.findUniqueOrThrow({
where: { id: session.userId },
select: { name: true, email: true, avatarUrl: true },
});
return <ProfileForm user={user} />;
}Why it matters
Anything a Client Component receives ends up in the browser. NEXT_PUBLIC_ variables and entries in the next.config env key are inlined into the JavaScript bundle at build time, and props passed from a Server Component are serialized into the page payload, where anyone can read them in the page source or network panel. Passing a full user record to render a name also ships the email, password hash, and internal flags. Nothing on screen reveals the leak, so it passes manual testing, and a key compiled into a public bundle stays exposed until it is rotated.
Agent instructions
cubic checks every pull request against these instructions. Paste them into AGENTS.md or Cursor rules and your AI coding tool follows them while it writes code.
AGENTS.md
Add this section to AGENTS.md at the repository root. Codex, Cursor, and other AI coding tools read it.
## Keep Secrets and Server-Only Code Out of Next.js Client Bundles Applies to `**/*.ts`, `**/*.tsx`, `next.config.*`, `**/.env*`. Review Next.js code for secrets, server-only modules, and private data that reach the browser. Flag changes that: - Put a secret behind a NEXT_PUBLIC_ variable (secret API keys, service-role or admin keys, signing secrets, database URLs), or rename a server variable to NEXT_PUBLIC_ so a Client Component can read it. - Add secrets to the env key in next.config, which inlines values into client bundles regardless of prefix. - Import a module that reads private env vars, creates a database or ORM client, or calls internal services into a "use client" file or anything it imports. - Add a data access, auth, or secrets module that Client Components could import, without import "server-only" at the top. - Pass full database records, ORM models, session objects, or access and refresh tokens as props from a Server Component to a Client Component that renders a few fields. - Return raw database records or internal objects from a Server Action instead of only the fields the UI needs. - Type Client Component props as whole server models (user: User from the ORM), which invites callers to pass private fields. Do not flag: - Values designed to be public behind NEXT_PUBLIC_, such as publishable or anon keys meant for browsers, analytics IDs, and public URLs. - Private env vars read in Server Components, Route Handlers, "use server" files, or server-only modules. - Props built from an explicit field selection or DTO mapping, even when the object is large.
Checks files matching **/*.ts, **/*.tsx, next.config.*, **/.env*.
More Next.js agents
See all Next.js code review standards
Run this agent on every pull request
cubic reviews every pull request against the agents you turn on and comments on the lines that break them.