Repair an invoice authorization bug

Bob can retrieve Alice’s invoice by changing the URL. You own the endpoint’s permission check. Reproduce the leak, patch the function below, and prove that Alice can still read her invoice.

Requires Node.js 22 or newer. No package installation or server needed. To work in a container instead, open the folder with VS Code Dev Containers or GitHub Codespaces: .devcontainer/devcontainer.json uses the Dev Containers Node.js 24 image, pinned by digest.
1. Unzip all files into one folder.
2. In that folder, run: node --test exercise.test.mjs
3. Edit policy.mjs, then run the same command again.

The starter deliberately fails some tests. A successful repair passes every case, including cases that catch common wrong fixes. The browser runs this same cases.mjs with the same fixtures; locally Node runs your code directly. If you completed the optional first stage in the browser, Download my work adds your input as stage1-input.json and the tests include it. Only run code you wrote or reviewed.

1. Reproduce the leak
Run the starter code. Open “Bob requests Alice’s invoice” in the report. The actual response is 200 and contains North’s invoice even though Bob belongs to South.

2. Repair the decision
Edit canReadInvoice. Return false for a missing caller or invoice. Require both the caller’s tenant and ID to match the stored invoice’s tenant and owner. Use && to require both relationships.

3. Test the boundary
Run your code again. A tenant-only fix still fails Carol’s request; an owner-only fix still fails the South user also named alice. Read each failed request and repair the missing comparison.

4. Preserve useful behavior
Finish with every check passing, including Alice’s own request and a mixed-owner export. Rejecting everything is not a fix.

SUPPLIED CONTRACT
The handler supplies a trusted caller from the authenticated session and an invoice loaded from storage. Both have a tenant. User IDs are unique only within that tenant. Product rule: only the invoice owner in the same tenant can read it.

REFERENCE
reference.mjs contains an explained solution: For Alice/North reading invoice1, both comparisons are true and the adapter returns 200 with amount 120. Carol/North fails ownership; Alice/South fails tenant scope even though the id matches. Missing caller or invoice returns false. The export invokes the same predicate for each row, so its partial-result contract returns only invoice1. This differs from an atomic export contract that would reject a mixed-owner request entirely; choose that behavior explicitly in a real feature.

SCOPE
This workshop models the permission predicate. Production enforcement belongs on a trusted server and must cover each operation, role, and route. The 403 response in the test adapter is illustrative; some systems deliberately conceal existence with 404.

NEXT TASK
Find one alternate route to the same data—an export, background job, or update endpoint. Where will it call this policy, and which denied case will you add to its tests?

SOURCE
https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.html
