<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Peter</title>
    <description>The latest articles on DEV Community by Peter (@peterbuildssecure).</description>
    <link>https://dev.to/peterbuildssecure</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4069416%2F46ddcb97-f3a3-4dff-ae16-7022fd4d0be5.png</url>
      <title>DEV Community: Peter</title>
      <link>https://dev.to/peterbuildssecure</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/peterbuildssecure"/>
    <language>en</language>
    <item>
      <title>7 Security Checks Before Shipping an AI-Built Next.js + Supabase App</title>
      <dc:creator>Peter</dc:creator>
      <pubDate>Sun, 09 Aug 2026 02:49:42 +0000</pubDate>
      <link>https://dev.to/peterbuildssecure/7-security-checks-before-shipping-an-ai-built-nextjs-supabase-app-1bnk</link>
      <guid>https://dev.to/peterbuildssecure/7-security-checks-before-shipping-an-ai-built-nextjs-supabase-app-1bnk</guid>
      <description>&lt;h1&gt;
  
  
  7 Security Checks Before Shipping an AI-Built Next.js + Supabase App
&lt;/h1&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Before shipping an AI-assisted Next.js and Supabase application, I check these seven areas.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Search for exposed secrets
&lt;/h2&gt;

&lt;p&gt;Start by checking the repository and client bundle for credentials.&lt;/p&gt;

&lt;p&gt;In Next.js, any variable prefixed with &lt;code&gt;NEXT_PUBLIC_&lt;/code&gt; 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.&lt;/p&gt;

&lt;p&gt;Look for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Service-role keys in frontend code&lt;/li&gt;
&lt;li&gt;API keys committed to the repository&lt;/li&gt;
&lt;li&gt;Secrets printed in build or application logs&lt;/li&gt;
&lt;li&gt;Environment files accidentally tracked by Git&lt;/li&gt;
&lt;li&gt;Server-only modules imported by client components&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Verify authorization on the server
&lt;/h2&gt;

&lt;p&gt;Hiding a button is not authorization.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;A dangerous pattern looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;supabase&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;documents&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user_id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The caller controls &lt;code&gt;userId&lt;/code&gt;. Instead, derive identity from a verified session or token and use that verified identity in the query.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Test Row Level Security adversarially
&lt;/h2&gt;

&lt;p&gt;Enabling Row Level Security is only the beginning.&lt;/p&gt;

&lt;p&gt;For every user-owned or tenant-owned table, test at least four cases:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;An authenticated user can access their own row.&lt;/li&gt;
&lt;li&gt;That user cannot access another user’s row.&lt;/li&gt;
&lt;li&gt;An unauthenticated request is rejected.&lt;/li&gt;
&lt;li&gt;Inserts and updates cannot assign ownership to someone else.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Remember that &lt;code&gt;SELECT&lt;/code&gt;, &lt;code&gt;INSERT&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt;, and &lt;code&gt;DELETE&lt;/code&gt; may require separate policies. For writes, verify both which existing rows can be targeted and which new row values are allowed.&lt;/p&gt;

&lt;p&gt;The most valuable RLS test is often not “Can Alice read Alice’s data?” It is “Can Alice read or modify Bob’s data?”&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Validate input at every boundary
&lt;/h2&gt;

&lt;p&gt;TypeScript types disappear at runtime.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Validation should cover more than shape. Also enforce:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Maximum lengths and collection sizes&lt;/li&gt;
&lt;li&gt;Allowed enum values&lt;/li&gt;
&lt;li&gt;Numeric and date ranges&lt;/li&gt;
&lt;li&gt;File type and size restrictions&lt;/li&gt;
&lt;li&gt;Unknown-field handling&lt;/li&gt;
&lt;li&gt;Business rules such as ownership and valid state transitions&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. Add abuse controls
&lt;/h2&gt;

&lt;p&gt;An authenticated endpoint can still be abused.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Secure dependencies and CI/CD
&lt;/h2&gt;

&lt;p&gt;A clean application scan does not guarantee a safe delivery pipeline.&lt;/p&gt;

&lt;p&gt;Check that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lockfiles are committed and used in CI&lt;/li&gt;
&lt;li&gt;Third-party CI actions are pinned&lt;/li&gt;
&lt;li&gt;Workflow permissions follow least privilege&lt;/li&gt;
&lt;li&gt;Untrusted pull requests cannot access deployment secrets&lt;/li&gt;
&lt;li&gt;Security jobs cannot silently fail&lt;/li&gt;
&lt;li&gt;Dependency installation is isolated from publishing and production credentials&lt;/li&gt;
&lt;li&gt;Container images run as a non-root user where practical&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Build jobs execute code from your dependencies. Treat them as part of the application’s security boundary.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Review AI-agent permissions and instructions
&lt;/h2&gt;

&lt;p&gt;Agent configuration is executable policy written in natural language.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Prefer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Least-privilege tool access&lt;/li&gt;
&lt;li&gt;Explicit approval for destructive or external actions&lt;/li&gt;
&lt;li&gt;Sandboxed execution&lt;/li&gt;
&lt;li&gt;Separate development and production credentials&lt;/li&gt;
&lt;li&gt;Logs tied to the exact commit and command&lt;/li&gt;
&lt;li&gt;Human review before deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A compact pre-launch checklist
&lt;/h2&gt;

&lt;p&gt;Before shipping, confirm that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No privileged secret reaches the client or repository&lt;/li&gt;
&lt;li&gt;Every sensitive server operation verifies identity and authorization&lt;/li&gt;
&lt;li&gt;Cross-user and cross-tenant access tests fail safely&lt;/li&gt;
&lt;li&gt;External input is validated at runtime&lt;/li&gt;
&lt;li&gt;Expensive endpoints have abuse controls&lt;/li&gt;
&lt;li&gt;CI jobs use minimal permissions and isolated secrets&lt;/li&gt;
&lt;li&gt;AI agents cannot silently exceed their intended authority&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI can accelerate implementation. It cannot decide which assumptions are safe for your users and your production environment.&lt;/p&gt;

&lt;p&gt;What security check has caught the most surprising issue in one of your projects?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>nextjs</category>
      <category>supabase</category>
    </item>
  </channel>
</rss>
