Investigate a denied request
Read a useful security event and walk through an incident response decision.
Need to set up your terminal or tools? · Look up a term
YOUR TASK · LOCAL MACHINE
Produce an access-denial log and use it to complete a short incident worksheet.
The fixed API, Node.js 24, and incident-playbook.txt from the starter folder.
You have observed a 403 event, completed the worksheet prompts, and stopped the server.
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 1 · extracted starter folder
Start the server and keep its output visible
TERMINAL 1node app.mjs
Lab 05 ended by stopping the server. Start it again now, and leave this terminal open so you can see the log output.
Check before continuingLearning fixture listening on port 3000.
Terminal 2
Generate one allowed and one denied request
Open a second terminal in the project folder. Run Alice’s request, then Bob’s request, one at a time.
TERMINAL 2 · ALICEnode -e "fetch('http://127.0.0.1:3000/records/1', {headers:{Authorization:'Bearer demo-alice'}}).then(async r=>console.log(r.status, await r.json()))"TERMINAL 2 · BOBnode -e "fetch('http://127.0.0.1:3000/records/1', {headers:{Authorization:'Bearer demo-bob'}}).then(async r=>console.log(r.status, await r.json()))"Check before continuingTerminal 2 shows 200 for Alice and 403 for Bob.
Terminal 1
Find the denial in the server log
Look at Terminal 1, where the server is running. It should show these decision events:
{"event":"record_read","status":200} {"event":"record_read","status":403}The second line records the denial you just generated. These log lines should not contain bearer tokens or Alice’s note title. One denial by itself is not proof of an attack.
Check before continuingYou can point to the 403 event and explain which request produced it.
Text editor · incident-playbook.txt
Complete the incident worksheet
Open
incident-playbook.txt. Read the scenario: a client update is causing repeated requests for stale record IDs. Fill in the three role blanks; one practice name may cover all roles. Then answer all four prompts:- What evidence would distinguish a client bug from deliberate enumeration?
- Who can approve a containment action?
- How would you measure recovery?
- What improvement will you make, who owns it, and by when?
For example, compare the denial rate before and after the client update, identify affected client versions, and check whether record IDs are being requested sequentially. Save your decisions; you do not need to install a monitoring service.
Check before continuingThe worksheet has named roles and concrete answers to all four exercise prompts.
Terminal 1 → this page
Stop the server and finish the course
Press Ctrl+C in Terminal 1. Answer the quick check below. Mark the lesson complete once the worksheet is saved.
Check before continuingThe server has stopped and your incident worksheet is saved beside the threat model.
Why it works & help if you get stuck
READ THE DECISION
A denial is evidence to interpret.
Learning fixture listening on port 3000
{"event":"record_read","status":401}
{"event":"record_read","status":200}
{"event":"record_read","status":403}The 403 tells you the access decision denied the request. It doesn’t tell you whether the caller made a mistake, the client has a bug, or someone is deliberately probing. In this exercise, you know it came from the synthetic Bob request.
Run a ten-minute tabletop
- Minute 0: record the denial and ask how it compares with normal traffic. Who is looking at it?
- Minute 3: a client update has just shipped. What evidence would distinguish stale links from repeated enumeration?
- Minute 6: choose a proportionate action and name who can approve it. Write down the evidence you would retain.
- Minute 10: define recovery and assign one improvement, such as a new regression case or useful correlation context.
Use the incident worksheet to record the decisions. The validation checked that the example logs contain a denial event and omit the synthetic bearer values and private note title. It did not validate a production alert, SIEM integration, or an incident response team.
IF YOUR RESULT IS DIFFERENT
Common wrong turns
| What happened | Why it matters | What to do instead |
|---|---|---|
| Treat every 403 as a confirmed breach. | Denials can be expected behavior or client mistakes. | Validate context, baseline, and impact. |
| Log the bearer value or full record for debugging. | The log becomes a new source of sensitive data. | Record the decision and suitable correlation context instead. |
| Finish the worksheet without an owner. | The improvement may never reach the next change. | Assign a specific follow-up, owner, and verification method. |
Need to set up your terminal or tools? · Look up a term
YOUR TASK · DOCKER
Produce an access-denial log and use it to complete a short incident worksheet.
Docker, the fixed exercise files, and incident-playbook.txt. Lab 05 ended by stopping the container.
You have observed a 403 event, completed the worksheet prompts, and stopped the server.
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 1 · extracted starter folder
Start the container and keep its logs visible
Rebuild if you changed any source files; then start the exercise. Leave this terminal open.
TERMINAL 1docker build -t opsrung-lab:local . docker run --rm --name opsrung-lab -p 127.0.0.1:3000:3000 opsrung-lab:local
Check before continuingThe image builds and the container prints Learning fixture listening on port 3000.
Terminal 2
Generate one allowed and one denied request
Open a second terminal on your computer and run these one at a time. The requests execute inside the running exercise container.
TERMINAL 2docker exec opsrung-lab node -e "fetch('http://127.0.0.1:3000/records/1', {headers:{Authorization:'Bearer demo-alice'}}).then(async r=>console.log(r.status, await r.json()))"TERMINAL 2docker exec opsrung-lab node -e "fetch('http://127.0.0.1:3000/records/1', {headers:{Authorization:'Bearer demo-bob'}}).then(async r=>console.log(r.status, await r.json()))"Check before continuingTerminal 2 shows 200 for Alice and 403 for Bob.
Terminal 1
Find the denial in the server log
Look at Terminal 1, where the server is running. It should show these decision events:
{"event":"record_read","status":200} {"event":"record_read","status":403}The second line records the denial you just generated. These log lines should not contain bearer tokens or Alice’s note title. One denial by itself is not proof of an attack.
Check before continuingYou can point to the 403 event and explain which request produced it.
Text editor · incident-playbook.txt
Complete the incident worksheet
Open
incident-playbook.txt. Read the scenario: a client update is causing repeated requests for stale record IDs. Fill in the three role blanks; one practice name may cover all roles. Then answer all four prompts:- What evidence would distinguish a client bug from deliberate enumeration?
- Who can approve a containment action?
- How would you measure recovery?
- What improvement will you make, who owns it, and by when?
For example, compare the denial rate before and after the client update, identify affected client versions, and check whether record IDs are being requested sequentially. Save your decisions; you do not need to install a monitoring service.
Check before continuingThe worksheet has named roles and concrete answers to all four exercise prompts.
Terminal 2 → this page
Stop the container and finish the course
TERMINAL 2docker stop opsrung-lab
Answer the quick check below, then mark the lesson complete once your incident worksheet is saved.
Check before continuingThe exercise container stops and is removed. Your incident worksheet is saved on your computer.
AFTER THE SIX LESSONS
Try one change of your own.
Make a separate copy of your fixed starter project. Add a synthetic record with ID 2, owner bob, and a different title to the existing records Map. Keep the existing identities unchanged.
- Predict the results when Bob and Alice request record 2.
- Add tests in
security.test.mjsusing the existing readRecord examples: Bob gets 200 and the new title; Alice gets 403 with no title. - Run the tests. The original five must still pass, together with your new tests.
- Explain why your ownership guard should need no changes.
Use Node directly or your chosen Docker test route. Keep this separate from the original five-test lesson copy, and do not use real data.
Next, practice resolving a Git conflict, or revisit where these checks fit in a pipeline.
TRY WITHOUT COPYING · OPTIONAL
Explain the evidence
Does one 403 log event prove an attack? Name one useful follow-up question and one piece of information that should not go in the log.
Compare your reasoning
No. It could be a mistake or a deliberate request. Ask whether denials repeat in an unusual pattern and what context is available. Do not log bearer credentials or private note contents.
Explain your answer aloud or write a sentence before opening the explanation. This does not change your completion record.
A QUICK CHECK
What should you do with one access-denied event?
Compare with the solution +
Keep logs structured and focused on the decision. Validate scope, preserve evidence with access controls, and choose a proportionate response. In a real system, define retention and add suitable correlation context without recording secrets.OWASP logging guidance ↗
Done with this one?
Marking complete is your own record.
Confirm both results before marking this lab complete.