1 min lesson
Write the fix as a frictionless PR comment
Walk through the worked example in "Write the fix as a frictionless PR comment", then explain what it demonstrates.
Step 1 of 2
Write the fix as a frictionless PR commentRemediation quality is half the score
app.get("/api/invoices/:id", requireAuth, async (req, res) => { const id = req.params.id; // Parameterized query - driver escapes; injection closed. // Ownership in the WHERE clause - authz can't be forgotten downstream. const invoice = await db.query( "SELECT * FROM invoices WHERE id = $1 AND owner_id = $2", [id, req.user.id], ); if (!invoice) return res.status(404).end(); // 404, not 403: don't confirm existence return res.json(invoice); // no secrets logged, ever });
Note the design choices a reviewer rewards. Ownership lives in the WHERE clause, so the authorization can't be skipped by a later refactor. The miss returns 404 rather than 403, because a 403 confirms the record exists and leaks information. That's the difference between patching a line and removing the bug class.
“Three issues, two blocking. Line 3 concatenates id into SQL - that's injection, fix is a parameterized query. There's also no ownership check, so it's an IDOR; I'd put owner_id = req.user.id in the WHERE clause so authz can't be forgotten. Separately, line 5 logs the Stripe secret - that key is now burned, so this needs a rotation, not just a deletion.”