Find and fix a broken access check
Reproduce an authorization flaw, fix it, and keep the regression tests.
Need to set up your terminal or tools? · Look up a term
YOUR TASK · LOCAL MACHINE
Reproduce the access-control bug, add the missing ownership check, and get all five tests to pass.
The extracted folder from lab 01, a text editor, and Node.js 24. No npm install is needed.
Your edited app.mjs passes all five unchanged tests.
Do the exercise on your computer. This page provides instructions and saves completion marks; it does not run commands or check your files.
Follow these steps
Terminal · extracted starter folder
Open the project terminal and check Node
Use the same folder you extracted in lab 01. If you do not have it yet, complete the download step in lab 01.
How do I open a terminal in the right folder?
Windows: open the extracted folder in File Explorer. Click the address bar, type
powershell, and press Enter.VS Code: open the extracted folder with File → Open Folder, then choose Terminal → New Terminal.
macOS or Linux: open Terminal and type
cdfollowed by the extracted folder’s path. If the path contains spaces, put it in quotes.The terminal must be in the folder containing
app.mjsandsecurity.test.mjs. Commands below are entered in that terminal, not in the browser address bar.TERMINALnode --version
If Node is missing, install the version 24 release from the official Node.js download page, reopen the terminal, and retry.
Check before continuingA version starting with v24. If you see “node is not recognized” or “command not found”, stop here and install Node first.
Terminal · same folder
Run the tests before editing anything
TERMINALnode verify-lab.mjs baseline
The starter deliberately lets Bob read Alice’s note. The two failing tests show the bug. Do not change the tests to make them pass.
Check before continuing3 tests pass and 2 fail. This is the expected starting result, not a setup failure. If all 5 already pass, this copy may already be fixed.
Text editor · app.mjs
Insert the ownership check and save the file
Open
app.mjs. Find the comment// LAB 02: enforce record ownership here, before returning any record data.InsidereadRecord(), insert the following block immediately below that comment and above the line that returns status 200.INSERT INTO APP.MJSif (record.owner !== user) { return { status: 403, body: { error: 'Forbidden' } }; }Keep the existing missing-user check, missing-record check, and successful return. Save the file. Paste this code into the editor, not the terminal.
Check before continuingThe new check sits between the missing-record check and the successful 200 response.
Terminal · same folder
Run the same tests again
TERMINALnode verify-lab.mjs fixed
All five should now pass. If they do not, check that you saved
app.mjsin this exact folder and placed the block before the successful return. Compare with the full solution below if needed.Check before continuing5 tests pass and 0 fail. Keep this fixed app.mjs for the remaining lessons.
Practice challenge: give Bob his own record
The guard should compare ownership for every record, not hard-code Alice. Try this extension after the main fix passes.
- Download ownership-challenge.test.mjs and save it beside app.mjs. Keep security.test.mjs unchanged.
- Run the command below. Expect 1 pass and 2 failures: record 2 does not exist yet.
- In app.mjs, add a second entry to the records Map: key
'2', withid: '2',owner: 'bob', andtitle: 'Bob note'. Keep record 1 and the ownership guard. - Save and rerun the challenge. Expect all 3 tests to pass, then verify the original 5 security tests still pass.
node --test ownership-challenge.test.mjs
Success means Bob can read his own note while both users are denied access to the other person’s note. This challenge is optional; it does not change the main lab completion requirements.
Why it works & help if you get stuck
THE CHANGE, LINE BY LINE
One guard, in the right place.
Keep the authentication and missing-record checks. Add the ownership guard immediately after the record exists check, before the successful response.
const record = records.get(id);
if (!record) return { status: 404, body: { error: 'Not found' } };
+ if (record.owner !== user) {
+ return { status: 403, body: { error: 'Forbidden' } };
+ }
return { status: 200, body: record };The + signs mark additions in this comparison. Do not paste the + signs into JavaScript.
- Use the server’s record owner. The comparison uses ownership in the service’s data, not an owner value supplied by the caller.
- Check after finding the record. There is no owner to compare if the record does not exist.
- Return only the error. A 403 status with the record still in the body would leak the same data.
To use that file, save it as app.mjs in a separate copy of the kit. It uses the same synthetic demo identities.
| Request | Expected status | Why |
|---|---|---|
| No or unrecognized identity | 401 | The caller has not authenticated. |
| Alice asks for record 1 | 200 | Alice owns the record. |
| Bob asks for record 1 | 403 | Bob is authenticated but does not own it. |
| Alice asks for record 999 | 404 | The record does not exist. |
✔ anonymous callers receive 401 ✔ the owner can read their record ✔ a different user cannot read the record ✔ a missing record returns 404 ✔ HTTP authorization boundary and health route ℹ tests 5 ℹ pass 5 ℹ fail 0
Timings are omitted. The HTTP test also checks the owner response, unknown tokens, missing records, and unsupported methods.
IF YOUR RESULT IS DIFFERENT
Common wrong turns
| What happened | Why it matters | What to do instead |
|---|---|---|
| Add only “if (user)”. | Bob has a valid identity too. The original bug remains. | Compare user with record.owner. |
| Return 403 for everyone. | The owner test fails. This wrong fix was checked during course validation. | Keep Alice’s successful case. |
| Change the status but return the record. | The title is still exposed. | Return an error object without record data. |
| The file no longer parses. | Diff + markers or curly quotes may have been pasted. | Use plain JavaScript and rerun the unchanged tests. |
Need to set up your terminal or tools? · Look up a term
YOUR TASK · DOCKER
Reproduce the access-control bug, add the missing ownership check, and get all five tests to pass.
The extracted starter folder, a text editor, and Docker running with Linux containers. Node.js runs inside a container.
Your edited app.mjs passes all five unchanged tests.
Do the exercise on your computer. This page provides instructions and saves completion marks; it does not run commands or check your files.
Follow these steps
Terminal · extracted starter folder
Open the project terminal and check Docker
Open the folder from lab 01. Edit files on your own computer; Docker will read them to run the tests.
How do I open a terminal in the right folder?
Windows: open the extracted folder in File Explorer. Click the address bar, type
powershell, and press Enter.VS Code: open the extracted folder with File → Open Folder, then choose Terminal → New Terminal.
macOS or Linux: open Terminal and type
cdfollowed by the extracted folder’s path. If the path contains spaces, put it in quotes.The terminal must be in the folder containing
app.mjsandsecurity.test.mjs. Commands below are entered in that terminal, not in the browser address bar.TERMINALdocker info
If Docker is missing, install it using Docker’s installation guide, start its engine, and choose Linux containers. The commands below use Docker Engine 23 or newer and mount the current folder read-only.
Check before continuingdocker info includes server information. If it reports a connection error, start Docker before continuing.
Terminal · same folder
Run the tests before editing anything
TERMINALdocker run --rm --mount "type=bind,source=.,target=/app,readonly" -w /app node:24-alpine node verify-lab.mjs baseline
The starter deliberately lets Bob read Alice’s note. The two failing tests show the bug. Do not change the tests to make them pass.
The first run downloads the Node.js image. Each test run uses a new temporary container and reads your saved files from this folder. You do not need to build the supplied Dockerfile yet: its test gate would reject the deliberately broken starter.
Check before continuing3 tests pass and 2 fail. This is the expected starting result, not a setup failure. If all 5 already pass, this copy may already be fixed.
Text editor · app.mjs
Insert the ownership check and save the file
Open
app.mjs. Find the comment// LAB 02: enforce record ownership here, before returning any record data.InsidereadRecord(), insert the following block immediately below that comment and above the line that returns status 200.INSERT INTO APP.MJSif (record.owner !== user) { return { status: 403, body: { error: 'Forbidden' } }; }Keep the existing missing-user check, missing-record check, and successful return. Save the file. Paste this code into the editor, not the terminal.
Check before continuingThe new check sits between the missing-record check and the successful 200 response.
Terminal · same folder
Run the same tests again
TERMINALdocker run --rm --mount "type=bind,source=.,target=/app,readonly" -w /app node:24-alpine node verify-lab.mjs fixed
All five should now pass. If they do not, check that you saved
app.mjsin this exact folder and placed the block before the successful return. Compare with the full solution below if needed.Check before continuing5 tests pass and 0 fail. Keep this fixed app.mjs for the remaining lessons.
Practice challenge: give Bob his own record
The guard should compare ownership for every record, not hard-code Alice. Try this extension after the main fix passes.
- Download ownership-challenge.test.mjs and save it beside app.mjs. Keep security.test.mjs unchanged.
- Run the command below. Expect 1 pass and 2 failures: record 2 does not exist yet.
- In app.mjs, add a second entry to the records Map: key
'2', withid: '2',owner: 'bob', andtitle: 'Bob note'. Keep record 1 and the ownership guard. - Save and rerun the challenge. Expect all 3 tests to pass, then verify the original 5 security tests still pass.
docker run --rm --mount "type=bind,source=.,target=/app,readonly" -w /app node:24-alpine node --test ownership-challenge.test.mjs
Success means Bob can read his own note while both users are denied access to the other person’s note. This challenge is optional; it does not change the main lab completion requirements.
TRY WITHOUT COPYING · OPTIONAL
Explain the condition
Without copying the fix, explain why the ownership check must happen before returning the record. What would happen if you checked afterward?
Compare your reasoning
Once the server has returned private data, a later check cannot take it back. Reject the request before sending that data.
Explain your answer aloud or write a sentence before opening the explanation. This does not change your completion record.
A QUICK CHECK
Which check fixes the reported flaw?
Compare with the solution +
if (record.owner !== user) {
return { status: 403, body: { error: 'Forbidden' } };
}Node.js test runner ↗Done with this one?
Marking complete is your own record.
Confirm both results before marking this lab complete.