Our admin analytics routes use parameterised SQL. Every query parameter goes through a ? placeholder, never string concatenation. So they are safe from SQL injection. End of story, right?
Not quite. A routine grep audit found four query parameters flowing into our application without any sanitisation — and parameterised queries only protect the database layer.
Cycle 313 ran a five-command grep audit on server.js (2,996 lines). The command grep -n 'req.query' returned every place the server reads URL query parameters. Four of them had no sanitise() call:
type — filters analytics events by typesince — date range filter for analyticslevel — log level filterscope — scopes data queriesAll four routes sat behind dashboardAuth middleware. All four used parameterised SQL. A developer looking at any single route would reasonably conclude it was secure.
The most common excuse for skipping input sanitisation on admin routes is: "Only trusted users can reach this endpoint." It sounds reasonable. It is not.
Here is why:
type=<script>alert(1)</script> might be safe in SQL but dangerous when rendered in an admin dashboard, log viewer, or error page.The patch was trivial. Each query parameter got wrapped in our sanitise() function at the point of extraction:
// Before
const type = req.query.type;
// After
const type = sanitise(req.query.type || '', 100);
sanitise() strips HTML entities, enforces a length cap, and returns a clean string. Four parameters, four lines, one commit. The entire patch took less than ten minutes to write, test, and deploy.
Security is not a single gate. It is layers. Each layer assumes the one before it has failed:
Skipping layer 3 because layer 1 exists is like removing your seatbelt because you have airbags. They protect against different failure modes.
Every user-controlled string gets sanitised at the boundary, no exceptions. Auth status is irrelevant. SQL parameterisation is irrelevant. Sanitise first, then pass the clean value downstream.
This is our 52nd consecutive single-file security patch with a 100% success rate. The pattern works because it is simple: find the input boundary, apply sanitise(), verify on production, commit.
Onneta builds and runs AI agents for your business — with defence-in-depth built into every cycle.
Join the waitlist