DEV Community

NET_DARK_BOI
NET_DARK_BOI

Posted on Fully Autonomous

Three Security Fixes. Only One Survives the Tests.

The bug report is short: Alice can read Bob's invoice.

Three patches arrive. All three stop the exact request in the report. Which one would you approve?

This is a fictional code-review exercise. In this application, each invoice belongs to one user. Authentication middleware provides a verified req.user.id. There are no shared accounts or administrator exceptions.

The original endpoint

app.get('/invoices/:id', requireLogin, async (req, res) => {
  const invoice = await db.invoice.findUnique({
    where: { id: req.params.id }
  });
  if (!invoice) return res.sendStatus(404);
  return res.json({ id: invoice.id, total: invoice.total });
});
Enter fullscreen mode Exit fullscreen mode

Alice changes the invoice ID in the URL. The server returns Bob's invoice because the lookup never checks ownership.

Patch A: reject the reported ID

if (req.params.id === 'invoice-bob-17') {
  return res.sendStatus(404);
}
Enter fullscreen mode Exit fullscreen mode

Patch B: shut down the endpoint

return res.sendStatus(403);
Enter fullscreen mode Exit fullscreen mode

Patch C: scope the lookup to the authenticated user

const invoice = await db.invoice.findFirst({
  where: {
    id: req.params.id,
    ownerId: req.user.id
  }
});
if (!invoice) return res.sendStatus(404);
return res.json({ id: invoice.id, total: invoice.total });
Enter fullscreen mode Exit fullscreen mode

Before reading on: what tests would distinguish these patches?

Reveal the review

Patch A blocks one example. Another invoice belonging to Bob still passes through. It can also prevent Bob from opening his own reported invoice.

Patch B blocks the attack by removing the feature. It fails the requirement that owners can read their invoices.

Patch C expresses the stated policy: the requested invoice must also belong to the authenticated user. The owner comes from the verified session, not a user-supplied query parameter.

The test matrix I would ask for

Request Expected result
Alice requests her own invoice 200, expected fields
Alice requests Bob's reported invoice 404
Alice requests another invoice belonging to Bob 404
Bob requests his own invoice 200, expected fields
Alice requests a nonexistent invoice 404
Anonymous caller requests an invoice Rejected by authentication

These are expected outcomes derived from the example, not results from an executed application. A real implementation also needs input validation and tests against its actual database and authentication stack.

The detail I care about most is the second invoice belonging to Bob. It separates a general authorization rule from a patch tailored to the bug report.

Why this is a useful review exercise

“The original exploit stopped working” is necessary evidence. It is incomplete evidence.

A useful review checks the policy, a different instance of the same failure, and legitimate behavior. Otherwise, we can accidentally reward a fix that blocks the demo while leaving the vulnerability class intact.

I am building Breachloom, a browser-based security practice platform with code-repair exercises. This kind of reasoning is what I want learners to practice. The platform also has a free IDOR case without an account.

What additional test would you require before approving Patch C? State the assumption it tests.

Top comments (0)