Dev.to Security πŸ” Cybersecurity πŸ‘ 0 πŸ“– 2 min read

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, e

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 });
});

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);
}

Patch B: shut down the endpoint

return res.sendStatus(403);

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 });

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.

πŸ“° Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes β€” full credit and traffic to the original publisher.