<?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: Haiyang Ren</title>
    <description>The latest articles on DEV Community by Haiyang Ren (@haiyang_ren_6a7f72da75abb).</description>
    <link>https://dev.to/haiyang_ren_6a7f72da75abb</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%2F4019244%2F62f1a87c-e991-4c87-b991-fb03618393e7.png</url>
      <title>DEV Community: Haiyang Ren</title>
      <link>https://dev.to/haiyang_ren_6a7f72da75abb</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/haiyang_ren_6a7f72da75abb"/>
    <language>en</language>
    <item>
      <title>I audited my own AI-built SaaS expecting a disaster. The payment logic was fine — but two things almost drained my API bill and leaked my keys.</title>
      <dc:creator>Haiyang Ren</dc:creator>
      <pubDate>Tue, 07 Jul 2026 10:17:25 +0000</pubDate>
      <link>https://dev.to/haiyang_ren_6a7f72da75abb/i-audited-my-own-ai-built-saas-expecting-a-disaster-the-payment-logic-was-fine-but-two-things-266c</link>
      <guid>https://dev.to/haiyang_ren_6a7f72da75abb/i-audited-my-own-ai-built-saas-expecting-a-disaster-the-payment-logic-was-fine-but-two-things-266c</guid>
      <description>&lt;p&gt;I'm not a security person.&lt;/p&gt;

&lt;p&gt;I built a small paid SaaS mostly by prompting AI — Cursor and Claude Code doing most of the typing, me steering. It's a tool people pay for, with the payment processor checkout wired in. And right before I opened it up to real users, I got nervous.&lt;/p&gt;

&lt;p&gt;Not because anything looked broken. Everything worked. The demo was clean, the checkout went through, the paid features unlocked. That was exactly the problem: it &lt;em&gt;worked&lt;/em&gt;, and I couldn't fully read my own codebase to tell whether it was actually safe. "Works" and "safe to put real money and real user data behind" are not the same sentence, and I only half-understood the code standing between them.&lt;/p&gt;

&lt;p&gt;So before launch, I sat down and audited it myself — line by line through the parts that could actually hurt me. Here's what I found, including the part that surprised me.&lt;/p&gt;

&lt;h2&gt;
  
  
  The stuff I was scared of
&lt;/h2&gt;

&lt;p&gt;If you've read any "AI writes insecure code" thread, you know the usual suspects, and they were exactly what I was afraid of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Could someone change the price in their browser and pay $1 instead of $29?&lt;/li&gt;
&lt;li&gt;Was my paywall real, or could anyone just skip payment and grab the paid output?&lt;/li&gt;
&lt;li&gt;Did the AI quietly paste my secret keys somewhere the browser could see them?&lt;/li&gt;
&lt;li&gt;Was there some "skip payment" dev shortcut still live in production?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are the ones that kill you quietly. Not a dramatic hack — just a user who pokes around, changes one value, and gets your paid product for free while you're asleep.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that surprised me
&lt;/h2&gt;

&lt;p&gt;The payment path was solid.&lt;/p&gt;

&lt;p&gt;The price was enforced on the server — the client only sends which plan you picked, never the amount, so there's nothing to tamper with. After the payment processor captured the payment, the code actually verified it: right status, right amount, right currency, before handing anything over. The token that unlocks the paid report was cryptographically signed, so you can't forge one. And that "skip payment" shortcut I was worried about? It was locked to the development environment — in production it's simply off.&lt;/p&gt;

&lt;p&gt;I half-expected a mess and didn't find one.&lt;/p&gt;

&lt;p&gt;I'm saying that on purpose, because most "AI code is insecure" takes skip the part where sometimes it's fine. If I told you everything was broken, I'd be doing the thing security people do to sell you something — making you scared. It wasn't broken. Whoever wrote it (me, plus a lot of AI) got the money path right. Credit where it's due.&lt;/p&gt;

&lt;p&gt;Which is exactly what made the two real problems interesting. They weren't where I was looking.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two things that actually would've gotten me
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;One: my free feature could drain my own bill.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The app has a free step that calls an AI model — that's the hook that gets people in the door. It's supposed to be rate-limited so nobody can hammer it. But when I read the limiter carefully, there was a hole: if one specific piece of infrastructure wasn't configured just right, the rate limit didn't tighten — it switched &lt;em&gt;off&lt;/em&gt; entirely. No limit at all. And the thing it was keying off of could be swapped by the user sending the request.&lt;/p&gt;

&lt;p&gt;Translation: someone with a simple script could call my free AI feature over and over and run up the bill &lt;em&gt;I&lt;/em&gt; pay to the AI provider. No hack, no login, no trace. Just my own credit card, quietly emptying.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Two: a backup file full of live keys was one careless zip away from leaking.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I had a "backup" copy of my environment file sitting in the project — the one with the real, live keys in it. It was ignored by version control, so it never hit my repo. But it was still sitting right there in the project folder, which means the moment I zipped the project to share it, or moved it somewhere, those keys were riding along. That's not a hack either. That's a habit. And habits like that are how keys end up somewhere they shouldn't be.&lt;/p&gt;

&lt;p&gt;Here's the part that stuck with me: &lt;strong&gt;a scanner would've flagged neither of these.&lt;/strong&gt; I ran the automated tools too. One problem was runtime logic — a limiter that fails the wrong way under one condition. The other was a habit — a file in the wrong place. Tools are great at the mechanical stuff, the hardcoded-key-on-line-42 stuff. These two needed someone actually reading the money-and-abuse path and asking "okay, but what happens when this piece &lt;em&gt;isn't&lt;/em&gt; there?"&lt;/p&gt;

&lt;h2&gt;
  
  
  What I didn't check
&lt;/h2&gt;

&lt;p&gt;To be straight with you: this wasn't a full penetration test. I didn't attack the live site, I didn't read every line of the longest file, and I didn't audit the payment processor account config. This was me reading the parts most likely to cost me money or leak data before launch — not a certificate that says the whole thing is bulletproof.&lt;/p&gt;

&lt;p&gt;Fixing the two issues took an afternoon. Rotating the keys that had been sitting in that backup took ten minutes. Now the thing can take money without me lying awake about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you built something like this
&lt;/h2&gt;

&lt;p&gt;If you've shipped — or are about to ship — something you built with AI that takes money or calls an API, these two classes are worth checking yourself, because they're the ones the tools stay quiet about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does your free, AI-calling feature have a rate limit that &lt;strong&gt;fails closed&lt;/strong&gt; — off means &lt;em&gt;blocked&lt;/em&gt;, not &lt;em&gt;unlimited&lt;/em&gt;? And is it keyed off something a user can't just swap?&lt;/li&gt;
&lt;li&gt;Is there any file with real keys in it sitting inside your project folder, one zip away from going somewhere public?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wrote up the full checklist I used, in plain English, here:&lt;br&gt;
&lt;a href="https://gist.github.com/haiyangr-cmyk/cd98502fa007b07d1807af1516f3e13a" rel="noopener noreferrer"&gt;https://gist.github.com/haiyangr-cmyk/cd98502fa007b07d1807af1516f3e13a&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And run a free scanner while you're at it — SafeToShip, Semgrep, whatever fits.&lt;br&gt;
They'll catch the mechanical stuff (exposed keys, headers) in two minutes, and&lt;br&gt;
you shouldn't pay anyone before you've done that.&lt;/p&gt;

&lt;p&gt;What they won't catch is the two classes above: the runtime logic (a limiter&lt;br&gt;
that fails the wrong way) and the permission/payment logic (who can do what,&lt;br&gt;
can the price be tampered). Those need a person reading the money-and-abuse&lt;br&gt;
path. That's the part I do now — after the scanner, not instead of it. If your&lt;br&gt;
app takes money or holds user data, drop a comment or reach out.&lt;/p&gt;

&lt;p&gt;Either way: check the free-endpoint one. It's the quiet one, and it's the one&lt;br&gt;
that bites AI-built apps the most.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>webdev</category>
      <category>indiehackers</category>
    </item>
  </channel>
</rss>
