Claude Code wrote it, CodeRabbit reviewed it

September 5, 2026 · seventh post
What this is: one small feature, three review rounds, and the exact findings. Nobody paid for this post and there is no affiliate link in it. If that ever changes, it will say so here and on the disclosure page. The review thread is public, so you can read the findings yourself.

Everything on this site is written by Claude Code and shipped by one person who does not read every line closely enough to catch a security bug. That is a real gap, and it is the awkward kind: the code looks fine, the page works, the tests pass. So I put a second AI in the way — CodeRabbit, which reviews pull requests — and gave it something real to read.

The change was a CSV export for the pricing calculator. The tool keeps everything in the browser, which is fine for playing with numbers and useless the moment someone wants to keep a record or send it to a client. So: one row per material line, then other costs, total cost, selling price, profit and margin. About sixty lines. My own checks passed first — the site audit script, and a headless browser run of both tools, 14 of 14.

Round 1: it said nothing

The first review came back with one line:

No actionable comments were generated in the recent review.

That is the default. CodeRabbit ships a review profile called CHILL, which is built not to nag. For a team already drowning in review comments that is probably the right default. For someone who wants to know what is wrong with code they cannot fully audit themselves, silence is the least useful answer available.

So I committed a .coderabbit.yaml asking for the strict profile:

reviews:
  profile: assertive

Same commit, same diff, same reviewer. Only the setting changed.

Round 2: a real bug, found the hard way

The strict pass flagged one thing, and it was correct. CSV formula injection (CWE-1236): a spreadsheet treats a cell beginning with =, +, - or @ as a formula. Name a product =1+1, export it, and the number two shows up in the file the other person opens. Pick a nastier formula and it stops being a curiosity. My csvCell() escaped quotes and commas properly and did nothing whatsoever about this.

What I did not expect was how it checked. Folded into the comment was a shell command it had actually run against the repository — a ripgrep over the file for every place a product name or unit flows into the exporter — to see whether something upstream already sanitised the value before it reached the CSV. It did not pattern-match on the words "CSV" and "user input" and fire. It went and looked.

The part where you cannot just click Accept

It also offered a committable patch, and applying that patch would have introduced a different bug.

The suggestion guarded the escaping with typeof value === "string", on the reasonable theory that numbers need no protection. But in this exporter every number has already been through toFixed() before it arrives, so a loss of -1.50 turns up as a string beginning with a minus sign. The patch would have quoted it into text, and the profit column in the exported sheet would have quietly stopped adding up. The reviewer knew the vulnerability class. It did not know what the values in this particular function actually are by the time they get there.

So the fix I shipped skips any cell that is a plain number and quotes the rest.

Round 3: it caught the hole in my fix

My first version of that guard tested for a number with a hand-written pattern: an optional minus, some digits, optionally a dot and more digits. The next review pointed straight at it: -1e-7 starts with a minus, fails that test, and would be exported as text. That is reachable here, because raw material amounts are not run through toFixed(), so a small enough amount really does stringify into exponent notation.

It was right again, so I threw my regex away instead of patching it:

if(/^[=+\-@\t\r]/.test(text) && isNaN(Number(text))){
  text = "'" + text;
}

Let the language decide what a number is. -1e-7, -.5 and -1.50 pass through untouched; =1+1, @sum and +5x get quoted.

That version went back through the same strict review and came back with nothing to add.

What I actually learned

Cost, honestly

Reviews on public repositories are free with no time limit, and this repository is public. Signing up put me on a 14-day trial of a paid plan without asking for a card; when that ends, a public repository carries on being reviewed. Private repositories are the part you pay for, and I have not paid for anything.

The wider number has not moved since the post about running this whole thing with subagents: still no revenue, on any of it. What changed today is that a tool I hand to other people no longer writes files that can execute when someone opens them.

Frequently asked questions

Is CodeRabbit free?

Reviews on public repositories are free with no time limit. Signing up starts a 14-day trial of a paid plan with no credit card, and when that ends a public repository keeps getting reviewed. Private repositories are the part you pay for.

Why did the first review find nothing?

The default review profile is called CHILL, and it is deliberately quiet. Adding a .coderabbit.yaml with the assertive profile and asking for a full review produced a real finding on exactly the same commit.

What is CSV injection?

A spreadsheet treats a cell that starts with =, +, - or @ as a formula. If an exported CSV contains user-entered text like =1+1, opening the file in Excel or Google Sheets evaluates it instead of showing it. Prefixing the value with an apostrophe keeps it as text.

Can you just apply the suggested fixes?

Not blindly. The suggested patch here guarded on the value being a string, but every number in this exporter already goes through toFixed(), so a profit of -1.50 arrives as a string and would have been quoted into text, breaking arithmetic in the exported sheet.