DEV Community

Amartya Jha for CodeAnt AI

Posted on Originally published at codeant.ai

CVE-2026-71509 · Approve your own expenses — and your whole team's

CVSS 6.5 · Approval / access-control bypass (CWE-862) · Fixed in Dolibarr 24.0.0

The big “Approve” button correctly needs a manager. But the plain “edit this report” form lets you type approved and name anyone as the approver, straight into the fields, with no manager required.

The attack in six steps

  1. Expense reports need a second person to approve: basic separation of duties.
  2. The dedicated Approve button checks for it: the front door is genuinely locked.
  3. But the generic "update report" route writes the same fields: status and approver, set directly. (this is the break)
  4. Nothing checks the approver differs from the author: or that you hold the approval right at all.
  5. Mark your own claim approved and name anyone as approver: from an account that can only file claims.
  6. Your claim, and a subordinate's, enter the payable queue: the second signature never happened. → self-approved payouts.

Expense approval is a textbook example of separation of duties: the person who files a claim must not be the person who approves it. That one rule is what stands between a company and an employee quietly signing off their own reimbursements. Dolibarr knows the rule, and the obvious button that approves a report enforces it properly.

This is the story of a second, plainer path that writes the very same fields the Approve button does, without asking whether the person approving is the person who filed.

The front door is locked. The side door was left open, and it leads to the same room.

The question with an obvious answer, and a real one

With any approval workflow, the first question to ask is the classic one: can the same person file a claim and approve it? We asked it of the dedicated Approve action, and the answer was a reassuring no. That action checks for the right permission, exactly as it should. A shallow security test stops here and calls it safe.

The real answer lived one path over. Alongside the Approve button, Dolibarr has a generic “update this expense report” path, the kind of catch-all that lets you edit a report's details. And that path lets you write the report's status and its approver directly, as if they were ordinary fields like a date or a note.

This is where API security testing and business logic testing matter. An endpoint can correctly protect its dedicated privileged action while another route exposes the same state change without enforcing the same authorization rules.

Signing your own name on the approval line

So an account that can only file claims opens the generic update path, sets the status to approved, and writes itself, or anyone it likes, into the approver field. Nothing checks that the approver differs from the author. Nothing checks that the account holds the approval right at all. The claim moves into the payable queue, approved, with a name on the approval line that the filer chose.

We did it to our own claim, and then to a subordinate's, from an account that was never supposed to approve anything.

Why a correct check isn't enough

The uncomfortable lesson here is that the Approve button being correct bought almost nothing. A control is only as strong as the weakest path that can reach the same state. If one carefully guarded action sets a field, and a second, generic action sets the same field with no guard, then the second action is the real security boundary, and it is wide open.

The damage is money leaving on approvals that never happened. In a company relying on Dolibarr to enforce the two-person rule on reimbursements, this quietly dissolves it: a single employee can file and approve, or approve for others, and push claims into the queue that pays them.

For Dolibarr security testing, this is an important distinction. Testing only the intended approval workflow can miss a business logic vulnerability exposed through a generic update endpoint. Effective penetration testing has to trace sensitive state changes across every route that can create them.

What closes it

The fix in 24.0.0 brings the generic update path under the same rule the Approve action already enforced. The status and approver fields can no longer be written freely, and the separation between filing and approving holds on every path, not just the obvious one.

The takeaway generalises to any workflow with a privileged step: find every path that can write the state, not just the one built for it. A locked front door means nothing while a back-office form writes the same fields with no check at all.

This is also the kind of authorization gap that a web application penetration test should look for: not just whether a user can access an endpoint, but whether they can use an alternative endpoint to reach a privileged application state they were never meant to control.


The fix. Upgrade to Dolibarr 24.0.0. That closes this finding. Want to know where your own app disagrees with itself? → Start with a free CodeAnt pentest

← Back to all nine findings

Top comments (0)