On this page
I run a store that sells AI skills. In October 2026 I found that anyone with a URL could download all 765 of them for free. Here's how it happened, the four other problems the fix turned up, and the checks I now run on everything I ship.
How I found it
I'm not a developer. I've run an SEO agency since 2006, and since February this year I've built two products almost entirely with Claude Code. One of them, authority.md, sells Claude skills: thinking frameworks drawn from people like Warren Buffett and Chris Voss, plus task tools priced up to $199.
One afternoon in October I was adding a free download to the site, a joke skill called Just Be British that makes your AI write overly British emails.
To see how the existing paid downloads worked, I pasted a product URL into the browser. The file downloaded. No login. No payment. A $149 tool, free to anyone who could guess the address.
It wasn't one file. Every framework, tool and playbook on the site, 765 products and nearly 900 files, was sitting in a public folder. The addresses followed the product names, so guessing them took no skill at all.
I've no way of knowing how many people found it before I did.
Why the AI did it, and why I didn't notice
The AI did exactly what it was asked. The site needed the files somewhere it could read them, and the quickest place in Next.js is the public folder. Anything in that folder is served to the whole internet. That's what it's for.
The actual delivery was built properly. Files are emailed only after Stripe confirms payment. Every test purchase worked. Every demo worked. The leak never showed up, because a leak doesn't throw an error.
That's the pattern. AI coding tools are brilliant at making things work. They're bad at asking who else it works for. You ask for a download link and you get one. Whether a stranger can use it too isn't a question anyone asked.
It was worse than a folder
When I went to fix it, I found the site wasn't just leaving the files lying around. It was handing out directions.
authority.md publishes a products.json feed for AI shopping agents, and it runs an MCP server (listed on HOL Plugins) so any agent can search the catalogue and buy. Both were returning a direct file URL for every product. The feed alone listed 1,530 download links. Ask the MCP server about a framework, and it gives you the price, a preview and a link to the full file.
If you build MCP servers, this is the bit to take away. Whatever your tools return is published. An agent will pass it on to a user, a log, a context window or another agent. A field that seemed harmless in a JSON response, a URL or an internal ID, is now public. I'd built a sales channel for agents and given it a free sample of everything.
The fix took an afternoon. The files moved to a folder the server can read but the web can't. The feed and the MCP server stopped returning file URLs. Old links now redirect to the product page instead of a 404. Then I checked that the delivered files came out byte-for-byte identical to before, and that the live MCP server could still read them. That matters, because the payment code reads the same files.
Four more things the same week turned up
Once you start looking, you find more. None of these were dramatic. All of them were invisible from the front end.
The demo that never cut anything
My home page has a "see the difference" demo that shows half of a paid framework. The browser downloaded the whole file, then trimmed it for display. Except the trim had an off-by-one check that never fired, so it showed the lot. Even if it had worked, the full file was sitting in the network tab. Anything you send to the browser is public. Trimming it there is decoration.
The key I left in a chat
Four weeks earlier I'd pasted a full-access email API key into an AI chat to fix a broadcast. It did the job. It was also still live a month later, with permission to send email from my domain and read my contact list. If you paste a key into a chat to get unstuck, delete it the same day. Better, make a key that can only do one job.
The opt-in box nobody could see
I gave away a free skill in exchange for an email address, with an optional box to tick for marketing. One line of site-wide CSS had stripped the native styling from every form field, checkboxes included. The box was there. It was just invisible, so nobody knew to tick it, and the same rule had hidden a checkbox on another form too.
The fix that broke something I'd forgotten
Moving the files out of the public folder tripped a GitHub Action that generates new frameworks. It went looking in the old folder, found nothing, and decided all 641 frameworks were missing. It failed safely, because the generator itself checked the new folder. When AI helps you move something, it rarely remembers the automation that depended on it.
The mistakes AI-built apps keep making
My leak was one example of a pattern. These are the mistakes I now check for in everything I ship. None of them break a demo.
| Mistake | What it looks like | Quick check |
|---|---|---|
| Paid or private files in a public folder | Downloads work with no login | Paste a file URL into a private browser window |
| Tool or API responses that publish too much | File URLs, internal IDs or emails in JSON | Read one raw response from each endpoint and MCP tool |
| Trusting the browser | Prices, roles or user IDs sent from the page | Change the price in your browser's dev tools and check out |
| Payment webhooks nobody verifies | Orders fulfilled from any request that looks right | Find the line in your webhook that checks Stripe's signature |
| Delivering on the success page | Visiting /success gives you the goods | Open your success URL without paying |
| Open AI endpoints | A route that calls a paid model with no login or limit | Call it from a private window, then check your usage bill |
| Secrets in the browser bundle | Secret keys with a public prefix like NEXT_PUBLIC_ | Search your site's JavaScript for sk_live_ and sk-ant- |
| Hidden, not protected | Admin pages that are unlinked but open | Visit them logged out |
| Keys and logins that outlive the job | Keys pasted into chats, old collaborators | List every key and user, revoke what you can't explain |
The common thread is that each one works. AI tools are graded on whether the thing works, and so are we.
What I do now before anything ships
This takes an afternoon, and you don't need to read code to do most of it.
- Write down where the money moves. Checkout, discount codes, fulfilment, downloads. Each one is a place where trusting the wrong thing costs you.
- Use your own site as a stranger. A private browser window, logged out, no payment. Try every download link, success page and admin URL you know.
- Read what your app says to machines. Your feeds, your API responses, your MCP tool outputs. If a stranger shouldn't have it, the response shouldn't contain it.
- Search what you ship to browsers. View the page source, open the JavaScript files, search for key prefixes. Publishable keys are fine. Secret keys are not.
- Count your keys and logins. Every API key, every user on every connected account. Anything you can't explain gets revoked. Anything you've ever pasted into a chat gets rotated.
- After every fix, check what else it touched. Scheduled jobs, GitHub Actions, old preview deployments still serving yesterday's files. Then try the original problem again and make sure it's gone.
A free skill that does this for you
I turned the whole process into a Claude skill and gave it away. It's called Security Audit, and it's built for people like me who ship with AI and don't have a security engineer.
It maps your app first: every route, who can call it and where the money moves. Then it works through the mistakes above and more, and proves each finding or labels it as unproven. With your permission, it also checks your live site using read-only requests, because the code alone can't tell you what's exposed. My leak didn't look like a bug in the code. It looked like a URL that worked.
Everything is ranked by what it could cost you: money, customer data, accounts or a surprise bill. It ends with a fix brief you paste straight into Claude Code, then re-checks once you've fixed things.
It has rules it won't break. It only audits apps you own. It never writes exploit code. And it will never tell you your app is "secure", because nobody honest can. It isn't a penetration test, and if a customer or insurer needs one, get one.
It isn't the only option either. Cloudflare's open-source security-audit-skill runs a multi-phase audit with separate agents verifying each finding, and it's built for security researchers working through larger codebases. krinalme's ai-security-audit includes a scanner script you can run on its own, and I borrowed four of its ideas for our version 1.1. Use whichever fits. Just use something before your customers find the problem for you.
You can get it free at authority.md/free/security-audit. You'll need an email address, because that's how it's delivered.

