Browser and API boundaries
Protect state changes, browser policies, rendering, object access and concurrent business operations, repairing seven concrete defects along the way.
- Separate request origin, script execution and resource authorization boundaries.
- Preserve a business invariant across concurrent requests.
- Transfer source/sink reasoning to file extraction and framework integration.
- Understand HTTP requests, cookies and basic JavaScript objects.
- The path uses supplied fixtures and browser labs; no deployed browser target is needed.
Estimated time: about 8.5–11 hours across 18 steps, including 9 browser labs and 2 local projects.
By the end: A browser/API boundary matrix and seven lab repairs, each with one useful success and one forbidden effect.
0 of 18 steps complete
Apply the guide’s origin/session assumptions to its request cases and name the server-side acceptance rule.
Keep: An origin-and-credential decision table.
Complete when: Explain why being logged in does not itself authorize a cross-site request and preserve a legitimate state change.
Open guide : Protect cookie-authenticated actions against CSRFRun the CSRF lab with the intended origin/credential contract and record ordinary and forged outcomes.
Keep: A repaired request policy with actual lab results.
Complete when: Legitimate changes succeed and the supplied forged requests fail before account mutation.
Open lab : CSRF lab: Protect an account change from a forged requestEvaluate the CORS and CSP examples against their stated browser capabilities and protected assets.
Keep: A two-column map of what each policy permits, rejects and cannot establish.
Complete when: Do not claim a CORS response rule is CSRF protection or that CSP grants business authorization.
Open guide : Configure CORS and CSP around the browser capabilities you needRun the CORS policy lab: compare whole origins with the allowlist, answer the preflight, and send Vary: Origin on every response.
Keep: A repaired response-header function with the lab's passing cases.
Complete when: Reflected, look-alike, http:// and null origins get no grant, the listed origins keep credentialed access, and every response varies on Origin.
Open lab : CORS policy lab: Grant CORS to the right origins onlyRun the CSP builder lab: start from default-src 'none', allow scripts by nonce and 'strict-dynamic' only, and open img-src and connect-src to the declared origins.
Keep: A repaired policy builder with the lab's passing cases.
Complete when: Neither an injected inline script nor an injected external one runs under the built policy, and the page's declared images and API calls still load.
Open lab : CSP builder lab: Build a strict Content-Security-PolicyTrace the same untrusted content through plain-text and rich-content rendering and inspect the supplied outcomes.
Keep: A renderer contract identifying allowed elements, URLs and network effects.
Complete when: The accepted representation preserves useful content without relying on removal of one suspicious word or event handler.
Open guide : Render rich text without granting it application privilegesRun the HTML sanitization lab: allow seven formatting tags, href as the only attribute and https: as the only link scheme.
Keep: A repaired sanitizer configuration with the lab's passing cases.
Complete when: Event handlers, script-capable tags and javascript: or data: links are removed, while paragraphs, emphasis, lists and https links keep their text.
Open lab : HTML sanitization lab: Configure a sanitizer for user commentsUse the supplied roles and objects to decide privileged actions and property updates separately.
Keep: A function/property permission matrix.
Complete when: An allowed object read is not treated as permission to invoke an administrative action or overwrite a protected field.
Open guide : Authorize functions and fields as well as objectsRepair the invoice lab, then compare its authorization checks with the earlier function/property matrix.
Keep: A final object-access result table.
Complete when: Separate tenant, ownership, function and property permissions in the explanation; a single passing owner test does not cover all four.
Open lab : IDOR lab: Repair an invoice authorization bugInterleave the supplied requests and locate the point where separate checks both observe stale state.
Keep: A state-transition trace plus the proposed atomic invariant.
Complete when: Explain which database/transaction condition prevents overspending while allowing one legitimate operation.
Open guide : Preserve business invariants under concurrent requestsRun competing purchases in the checkout lab and compare the final ledger/balance with the invariant.
Keep: An atomic repair and observed concurrent outcome.
Complete when: No accepted sequence spends more than available funds, and one affordable purchase still succeeds.
Open lab : Race condition lab: Repair a balance check that racesMap route registration, middleware, parsing, authorization and response rendering in the guide fixture.
Keep: A lifecycle coverage checklist tied to actual routes and effects.
Complete when: A custom or legacy route cannot inherit a security claim merely because the default framework path is protected.
Open guide : Preserve framework security controls across the request lifecycleTreat an archive entry name as untrusted input interpreted by a filesystem consumer. Use the lab’s supplied extraction contract.
Keep: A repaired path check and explanation of traversal/alias assumptions.
Complete when: The lab’s safe entries remain usable and forbidden entries cannot escape the designated root; do not generalize lexical checks to unmodeled symlink behavior.
Open lab : Path traversal lab: Keep archive entries inside their extraction directoryDownload the proxy and cache project, run node observe.mjs to watch Bob receive Alice’s invoice, then repair the cache key and storage rules in exercise.mjs.
Keep: Your cache contract for each route and the observe.mjs output before and after the repair.
Complete when: All 12 tests pass, Alice and Bob each receive their own invoice, and the public catalog is still served from the cache.
Open project : Repair a real proxy and shared cacheDownload the prototype pollution project, run node --test exercise.test.mjs, then replace the generic merge with the profile schema and repair the admin check.
Keep: A before-and-after diagram of the property lookup chain and the test output.
Complete when: All 18 tests pass and you can explain why schema selection, null-prototype records and own-property checks protect different boundaries.
Open project : Stop prototype pollution at merge and authorization boundariesRun the open redirect lab: accept next only as a path with one leading slash and no backslash or control character, confirm with the URL parser that it stays on the site's origin, and redirect to the parsed href.
Keep: A repaired redirect function with the lab's passing cases.
Complete when: //, ///, backslash, tab, user-name, host-suffix and javascript: values all fall back to /account, while ordinary paths keep their query, fragment and encoding.
Open lab : Open redirect lab: Return users to this site onlyRun the mass assignment lab: accept displayName, bio and the two preferences through an allowlist that also applies inside the nested object, validate each value, and refuse or ignore everything else.
Keep: A repaired profile update with the lab's passing cases.
Complete when: role, Role, emailVerified, id, a nested organization owner and extra preference keys never change the stored record, and ordinary and partial updates still apply.
Open lab : Mass assignment lab: Update only the fields users may editFind the signaling, TURN and media-server problems in the guide’s telehealth excerpts and map each to an ASVS 5.0 V17 requirement or authorization rule.
Keep: A findings table with the requirement, the risk and the fix, in fix order.
Complete when: Explain why signaling authorization protects the media keys, and which coturn settings stop relaying to internal addresses.
Open guide : Secure WebRTC signaling, TURN relays and media serversProtect a limited-seat checkout behind a browser
Use the complete synthetic fixture below in a notebook or text file. No live target, paid service or secret is needed. Make your own decision before opening the worked assessment. Then take the implementation option below, a local project in which you repair running code until its tests pass, and keep its test output with your written answers.
A browser-only ticket checkout combines cookies, a rich-text receipt and concurrent stock updates. Produce a control and test plan that preserves ordinary purchases.
Supplied material
Origin: https://tickets.example.test. Session cookie is Secure, HttpOnly, SameSite=Lax. POST /buy requires an Origin exactly equal to that origin and a CSRF token bound to the active session; missing/opaque Origin is denied for this browser-only endpoint.
Stock: one seat; Alice and Bob each have enough credit. Both requests read stock=1 before either writes. A purchase must decrement stock and charge credit in one transaction.
Rich-text receipt accepts links and basic emphasis only. Current renderer assigns user text to innerHTML.
PATCH /profile copies every supplied JSON property into the user record, including role and tenantId.
ZIP attachment entries: receipt.txt, ../server/config, receipts/ok.txt. Ordinary nested entries are allowed; traversal is not.
Test requests: A valid Alice token+origin; B valid Alice cookie with attacker.test origin; C Alice cookie with Bob’s CSRF token; D missing Origin; E Alice request with role=admin; F two simultaneous valid buys.Work through the case
Make an A–F expected-outcome table. State what SameSite, Origin, CSRF tokens and authorization each establish.
Describe the rich-text pipeline and separate safe plain-text rendering from sanitized rich-text parsing.
Write the checkout invariant and a concurrent test with observable final stock, charge and receipt counts.
Specify profile field allowlisting and archive containment checks, then choose at least one ordinary-use case for every denial rule.
Decide the fixed cases
These parts of the case have one right answer, taken from the worked assessment. Check them here; the rubric below assesses your written reasoning. The capstone meets the bar when every decision is right and the self-check reaches it.
What is the expected outcome of each test request?
After request F
Keep or reject each ZIP entry?
Evidence to retain
- Six-case browser/API decision table.
- Rendering and property-update design.
- Concurrent checkout assertion set.
- Archive allowed/rejected entry table.
Assess your work
Score each criterion with the anchor whose evidence your answer shows: 0, 1 or 2 points. Every anchor describes something a reviewer can point to in your work. Aim for at least 7 of 8, with no unresolved critical failure.
Score your answer against the anchors
Choose the anchor that matches your answer for each criterion.
Recorded checks stay in this browser only, so you can compare attempts. This is a self-assessment, not a certification.
Compare with the worked assessment
A may purchase; B, C and D are denied before business effects by this endpoint contract. E must not modify role or tenantId; reject unknown protected fields or ignore them under an explicit documented API contract and verify they remain unchanged. F must yield at most one committed purchase, one charge and stock 0; a transaction/conditional write must preserve that invariant under concurrency. A plain-text sink avoids HTML interpretation; rich-text use requires a maintained sanitizer with a narrow policy and safe links. The two normal archive paths may be retained; ../server/config is rejected. These are independent controls: CORS or CSP does not replace CSRF, field authorization or atomic checkout.
Save a draft on this device to resume later. Automatic saving begins after you choose Save. Drafts are never sent by this application; use synthetic data.
No draft saved on this device yet.
Implementation option: Make a checkout transaction atomic
Reproduce the race with reproduce_race.py, then repair exercise.py until all 3 tests pass: two purchases of 80 must give one paid, one declined and a balance of 20.
Keep: The reproduction output (final balance -60), failing and passing test output, and a note of the statement that now enforces the invariant.
Python 3.11+ · standard library · 30–45 min · 3 tests
Other connected projects
- Keep extracted files inside their boundary — Python 3.11+ · standard library
AppSec in practice
Review a system, build a test plan, and carry a finding through to verified remediation.
Your progress, your browser
Progress is stored on this device. Export it to carry it to another browser, or clear it whenever you want. Importing a file adds its completed items to the progress already here; items this site no longer offers are skipped and counted, and nothing else is removed.