ai · tutorial · agents

Code That Passes Lint and Ships a Vuln

Green lint plus green tests can still ship injection, auth bypass, or leaked secrets. The quiet-fail classes plus the review that catches them. Inside.

A crowd hauling a huge wooden horse into Troy, painting by Giovanni Domenico Tiepolo, about 1760
A crowd hauling a huge wooden horse into Troy, painting by Giovanni Domenico Tiepolo, about 1760 · Photo: Giovanni Domenico Tiepolo, public domain, via Wikimedia Commons

The green build with a hole in it

The agent's PR passes lint. The tests are green. The preview renders beautifully.

Wednesday, a user exports another user's invoices by changing an ID in the URL.

Thursday, a secret shows up in an error trace. Friday, a crafted search string dumps a table.

Every check passed because every check inspects form.

Syntax is valid.

Types align.

Tests assert what the agent wrote.

Security lives in meaning. Who may read this row? Where does this string run?

The holes that pass every gate

Hole What it looks like What the gate says
Missing ownership check A clean route with no user check Green
Injection A tidy query builder Green
Secret in a log Helpful debug output Green
Paywall early-return A refactored handler Green
Mass assignment A spread operator save Green

Agent code is especially prone, for three reasons.

Tests assert the agent's assumptions. The agent writes tests proving its code does what it intended, including the vulnerability. A test can happily assert that someone else's row returns 200.

Refactors drop guards silently. Move code between files and the paid-access check, the ownership check and the redaction can vanish. The diff shows a clean move.

Helpfulness leaks secrets. Agents log generously. Full payloads. Error stacks with context. Each line is a possible broadcast.

What to do

Enforce ownership where the data is read.

A route that asks for "this user's order" cannot forget the check. A route that asks for "this order" can.

Put the check in the lowest layer, so it survives refactors and new endpoints.

Keep secrets out of output. Decide which values must never appear in a log, an error or an alert. Review every new output path against that list.

Then review the guard set on every diff. Four questions, asked of each agent PR that touches routes or data.

None of this is answerable by lint.

The adversary test carries the load. Each PR gains one test asserting that the forbidden thing fails.

Green then means safe, not just well shaped.

A Monday habit

Pick your riskiest route. Ask the four questions of it.

Write the adversary test it does not have. Run it. Watch it fail, then make it pass.

One route a week. In a month the dangerous ones are covered.

What it costs

Ten minutes of review per PR. A habit of keeping ownership checks low.

That slows an agent a little. One cross-user leak costs more trust than weeks of features earn.

Know this first

BoringPush does not redact secrets from logs for you. Review habits still matter. So does the adversary test.

Green lint is not proof of safe.

Why BoringPush is the way to do this

Green is only the start.

BoringPush ships guards for open redirects, for one user reading another's record, and for runaway requests.

Its rules put the access check first in every route.

Safe by default. Proven by your own tests.

FAQ

Is a quiet vulnerability just prompt engineering gone wrong?

No. It is a category gap, not a wording bug. No prompt makes a linter see meaning or makes a test assert a case nobody asked for.

The fix is structural (ownership in the data layer) plus procedural (guard review and adversary tests). Better prompts help a little; fences help completely.

Should I run an automated scanner instead of reviews?

Alongside, not instead. Scanners catch known patterns such as outdated dependencies and classic injection shapes. Reviews catch meaning such as ownership and business-logic abuse.

Scanner-green plus review-skipped is exactly the failure this post describes. Layer them: a scanner on every push, a review on every PR.

How do I write adversary tests agents won't neutralize?

Order them explicitly in the task: user B requests user A's order and expects a refusal. Then read the test's assertion before you read the implementation.

A test that asserts the leak passes is worse than no test. Treat the test file as the place where safety gets specified.

Do small solo apps really get attacked?

Probed constantly, targeted rarely. Bots try ID swapping, injection strings and secret harvesting against every app they find.

The classes here defend against the probes, which are certain, and cost little against targeted attackers, who are rare.

What about dependencies?

Same quiet-fail problem, one step removed. Supply chain is the sixth class: trusted code you did not write, running with your keys loaded. Keep dependencies few, audit them on a schedule, and review additions as carefully as your own code.

When does security review become a dedicated role?

Past about five engineers, or with regulated data such as health or finance. Checklist discipline stops scaling when reviewers rotate. Until then, four questions and one adversary test per PR beat most startups' actual practice.

Keep reading

+ all the boring stuff (analytics, auth, SQLite ↔ Supabase, full marketplace, webhooks)

The Agent-First Shipping Kit

Command from your phone.

Built so autonomous AI agents build what you want without breaking what's already paying you.

$199

Get BoringPush

One-time payment · Lifetime updates · Unlimited projects.

Keep reading