AI Pentesting

CVE-2026-71509: Employees Can Approve Their Own Expenses

Amartya | CodeAnt AI Code Review Platform
Sonali Sood

Founding GTM, CodeAnt AI

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

The big Approve button correctly needs a manager's sign-off. The plain edit-this-report form doesn't ask for one at all, and happily lets you type approved into the status field and your own name into the approver field.

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 case of separation of duties: whoever files a claim must not be the one who signs off on it. That single rule is what stands between a company and an employee quietly approving their own reimbursement. Dolibarr's dedicated Approve action knows the rule and enforces it correctly.

This is the story of a second, plainer path that writes the exact same fields the Approve button does, and never stops to ask whether the person approving is the person who filed.

The front door is locked. The side door was left open, and it leads into the same room, which is the same shape of gap behind the wrong-permission bug that let low-privilege accounts delete recorded payments elsewhere in this series: a privileged action guarded correctly in one place, and reachable through a completely different route that never asks the same question.

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 actually closes this

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 now holds on every path, not just the obvious one.

The takeaway generalizes to any workflow with a privileged step: find every path that can write that 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, and this is exactly the broken function level authorization pattern that keeps showing up across this Dolibarr assessment under different names.

This is also the kind of gap that real web application penetration testing is supposed to catch, not just whether a user can reach an endpoint, but whether an entirely different endpoint lets them reach a privileged application state they were never meant to control. Expense fraud built this way never trips an alarm, because every request along the way is valid, authenticated, and technically permitted.

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 in the Dolibarr research series

FAQs

Start Your 14-Day Free Trial

AI code reviews, security and quality trusted by modern engineering teams.

Table of Content
No headings found on page
Ship clean & secure code faster

Get Pentest Report

NO CC REQUIRED