列出 7 项安全检查点:密钥泄露、服务端模块混入客户端、环境变量隔离、Git 历史清理等。
AI coding assistants can dramatically reduce the time between an idea and a working application. Unfortunately, they do not reduce the application's attack surface.
Generated code often looks reasonable, compiles successfully, and passes the happy-path test. The dangerous mistakes tend to live in the assumptions around that code: who is allowed to call an endpoint, which credentials are exposed, and whether one user can access another user's data.
Before shipping an AI-assisted Next.js and Supabase application, I check these seven areas.
Start by checking the repository and client bundle for credentials.
In Next.js, any variable prefixed with NEXT_PUBLIC_ can be included in browser-delivered code. A Supabase publishable or anonymous key is intended for client use, but a service-role key is not.
Service-role keys in frontend code
API keys committed to the repository
Secrets printed in build or application logs
Environment files accidentally tracked by Git
Server-only modules imported by client components
Rotating a secret after exposure is safer than simply removing it from the latest commit. Git history, build artifacts, logs, and deployment previews may still contain the original value.
Hiding a button is not authorization.
Every sensitive route, server action, and API handler should verify the caller's identity and permissions on the server. Never trust a user ID, organization ID, role, or ownership field merely because the frontend supplied it.
A dangerous pattern looks like this:
const { userId } = await request.json();
const { data } = await supabase
.from("documents")
.select("*")
.eq("user_id", userId);
The caller controls userId. Instead, derive identity from a verified session or token and use that verified identity in the query.
Enabling Row Level Security is only the beginning.
For every user-owned or tenant-owned table, test at least four cases:
An authenticated user can access their own row.
That user cannot access another user's row.
An unauthenticated request is rejected.
Inserts and updates cannot assign ownership to someone else.
Remember that SELECT, INSERT, UPDATE, and DELETE may require separate policies. For writes, verify both which existing rows can be targeted and which new row values are allowed.
The most valuable RLS test is often not "Can Alice read Alice's data?" It is "Can Alice read or modify Bob's data?"
TypeScript types disappear at runtime.
Validate request bodies, query parameters, webhook payloads, uploaded-file metadata, and structured AI output before using them. A schema validator such as Zod can help, but the important part is treating every external value as untrusted.
Validation should cover more than shape. Also enforce:
Maximum lengths and collection sizes
Numeric and date ranges
File type and size restrictions
Unknown-field handling
Business rules such as ownership and valid state transitions
An authenticated endpoint can still be abused.
Apply rate limits to expensive or sensitive operations such as AI generation, authentication attempts, email delivery, file processing, and public forms. Set request-size limits and timeouts as well.
For browser-facing APIs, configure CORS intentionally. Avoid combining a wildcard origin with credentials, and do not treat CORS as an authorization mechanism—it only controls participating browsers.
A clean application scan does not guarantee a safe delivery pipeline.
Lockfiles are committed and used in CI
Third-party CI actions are pinned
Workflow permissions follow least privilege
Untrusted pull requests cannot access deployment secrets
Security jobs cannot silently fail
Dependency installation is isolated from publishing and production credentials
Container images run as a non-root user where practical
Build jobs execute code from your dependencies. Treat them as part of the application's security boundary.
Agent configuration is executable policy written in natural language.
Review repository instructions, tool permissions, hooks, MCP servers, and automated commands with the same care as source code. An agent should not receive production credentials or destructive permissions simply because those permissions make development more convenient.
Least-privilege tool access
Explicit approval for destructive or external actions
Separate development and production credentials
Logs tied to the exact commit and command
Human review before deployment
Before shipping, confirm that:
No privileged secret reaches the client or repository
Every sensitive server operation verifies identity and authorization
Cross-user and cross-tenant access tests fail safely
External input is validated at runtime
Expensive endpoints have abuse controls
CI jobs use minimal permissions and isolated secrets
AI agents cannot silently exceed their intended authority
AI can accelerate implementation. It cannot decide which assumptions are safe for your users and your production environment.
What security check has caught the most surprising issue in one of your projects?