Software Securityhome
Fix a findingSearch
Learning path · Practitioner · Application security engineers

AppSec in practice

Review a system, build a test plan, and carry a finding through to verified remediation.

WHAT YOU WILL BUILD
  • Translate a trust-boundary concern into a falsifiable test.
  • Carry a finding from triage through candidate and release evidence.
  • Distinguish observed effects, proposed controls and unsupported assurance claims.
BEFORE YOU START
  • Read HTTP behavior and a simple data-flow diagram.
  • Use the supplied systems and test identities; no access to a live application is necessary.

Estimated time: about 8.5–11 hours across 18 steps, including 10 browser labs.

By the end: A scoped review dossier containing a threat case, permission test plan, business invariants, outbound fetch policy, dependency decision and release-aware remediation record.

Keep the artifacts from each step together. Mark a step complete after checking its stated outcome. A lab or guide you complete here also shows as complete in other paths that include it.

0 of 18 steps complete

Trace the supplied tenant operation through every service, worker and store. Select one attacker-controlled value that crosses a trust boundary.

Keep: An abuse case with preconditions, asset, expected forbidden effect and test oracle.

Complete when: The case names a specific enforcing component and separates established architecture from assumptions.

Open guide : Review tenant boundaries before implementation

Translate the guide’s object relationships into allowed and forbidden requests, including a legitimate control.

Keep: A role/tenant/ownership test matrix linked to the abuse case pattern.

Complete when: Each result follows from current authoritative relationships; hidden UI controls are not accepted as enforcement.

Open guide : Enforce authorization for every object action

Repair the supplied lab and retain the results for legitimate, same-tenant and cross-tenant cases.

Keep: A reproducible regression note identifying the tested code.

Complete when: The repair preserves intended access and fails forbidden cases for the correct reason; candidate evidence is not relabeled as production evidence.

Open lab : IDOR lab: Repair an invoice authorization bug

Apply the guide's server-side invariants to a checkout: list each price, quantity, coupon and workflow rule that a request must not set.

Keep: An invariant list naming, for each rule, the request field it distrusts and the server data it uses instead.

Complete when: Every amount an order charges traces to server data or a written rule, never to a value the client sent.

Open guide : Prevent business-logic flaws with server-side invariants

Run the business logic lab: price each line from the catalog, reject impossible quantities and repeated lines, and apply at most one coupon under its own limits.

Keep: A repaired pricing function with the lab's passing cases.

Complete when: Client prices, negative or excessive quantities and stacked or reused coupons change nothing, and ordinary orders total as before.

Open lab : Business logic lab: Price an order without trusting the client

Use the guide's table of special-purpose address ranges to write the destination policy for one outbound fetch, redirects included.

Keep: A destination policy naming the allowed schemes, ports and address ranges, and how each redirect is checked.

Complete when: The policy judges every resolved address rather than the host name, and treats each redirect as a new request.

Open guide : Constrain server-side fetches to their intended destinations

Run the JavaScript SSRF lab: classify every address each hop resolves to, re-check each redirect target and stop after five redirects.

Keep: A repaired fetch guard with the lab's passing cases.

Complete when: Metadata, loopback, private and documentation addresses are blocked directly, through encodings and through redirects, while public partner hosts are still fetched.

Open lab : SSRF lab: Guard an outbound fetch against SSRF

Run the Python SSRF lab: parse with urlsplit, resolve the host, and allow only addresses that the ipaddress module reports as global.

Keep: A repaired validator with the lab's passing cases.

Complete when: Encoded loopback, IPv4-mapped loopback, carrier-grade NAT and a name with one private answer are blocked, and a public name or literal is allowed.

Open lab : SSRF validation lab: Validate a fetch URL with ipaddress

Evaluate the supplied advisory against installed identity, deployed version and vulnerable behavior. Keep this fixture separate from the tenant example.

Keep: A scoped advisory/exposure decision and a proposed closure condition.

Complete when: Explain why a package match is an investigation input and why candidate patch evidence alone cannot close deployed exposure.

Open guide : Respond to an exploited or malicious dependency

Apply the guide’s policy to the competing build records and compare the approved digest with the proposed deployment selector.

Keep: A release decision listing source, builder, artifact and policy predicates.

Complete when: Reject every near miss with a specific reason and explain how a mutable tag can break the verification-to-deployment link.

Open guide : Protect the path from pull request to release

Use the webhook trace to distinguish rejected authenticity, duplicate delivery and duplicate payment.

Keep: A cause-and-effect timeline with a durable closure oracle.

Complete when: The report identifies the business ledger observation needed to prove one effect, rather than relying on an HTTP response.

Open guide : Verify webhook deliveries before acting

Repair the webhook lab, then produce a short report with initial cause, change, observed tests, scope and residual limits.

Keep: A complete practice finding report backed by the browser lab observations.

Complete when: Another engineer can explain why the fix works and what deployment evidence would still be required for real closure.

Open lab : Webhook signature and replay lab: Repair a webhook handler that pays twice

Run the command injection lab: replace the shell command line with execFile and an argument list, put "--" before the path, refuse refs that start with a dash and allow only the log, blame and diff views.

Keep: A repaired git runner with the lab's passing cases.

Complete when: Separators, pipes, substitutions, newlines and redirections in any value start nothing else, option-like refs and other subcommands are refused, and file names with spaces, quotes, non-ASCII characters or a leading dash still work.

Open lab : OS command injection lab: Run git without a shell

Run the memory-safety model lab: before echoing a heartbeat, check the header, the claimed payload length and the 16 bytes of padding against the record's real length, and discard anything else.

Keep: A repaired heartbeat handler with the lab's passing cases, and a note on where a parser in your own code compares a length field with the input it received.

Complete when: An oversized claim no longer returns the neighbouring buffer or reads past the heap, malformed messages are discarded, and ordinary heartbeats are echoed exactly.

Open lab : Buffer over-read lab: Stop a heartbeat over-read

Run the SSTI lab: render the user's display name through a fixed Jinja template with autoescaping on, rather than building the template from the name.

Keep: A repaired render function with the lab's passing cases.

Complete when: A name containing {{ }}, {% %} or HTML is shown as text and cannot read the render context.

Open lab : SSTI lab: Render a welcome banner without template injection

Run the XXE lab: configure the lxml parser with resolve_entities=False, no_network=True and load_dtd=False so a crafted invoice cannot read a local file.

Keep: A repaired XML parser with the lab's passing cases.

Complete when: An external entity pointing at a file or URL is left unresolved, while built-in entities and ordinary invoices still parse.

Open lab : XXE lab: Parse an uploaded invoice without resolving external entities

Run the deserialization lab: parse the token as JSON and validate it against a fixed schema, with no fallback that unpickles untrusted bytes.

Keep: A repaired token loader with the lab's passing cases.

Complete when: A base64 pickle, even one carrying a valid HMAC, is rejected rather than run, and a JSON token that breaks the schema is refused.

Open lab : Pickle deserialization lab: Load a session token without pickle

Rank the nine probe findings in the guide’s exercise, then run the probe script against a local server you set up with a .git folder and a source map.

Keep: A ranked findings table with the exposure, the fix and the ASVS 5.0 V13.4 requirement for each.

Complete when: Name each framework’s production switch for debug output and show the probe reports nothing on a hardened server.

Open guide : Ship without debug modes, verbose errors or exposed files
FINAL APPLICATION · FRESH SCENARIO

Decide whether a bulk export fix is ready

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 team fixed its ordinary invoice endpoint, but is asking you to approve an export release. Build a finding, regression plan and release decision from the supplied evidence.

Supplied material

Plain text
Release candidate: source r42, artifact sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.
Policy: authenticated users may export only invoices they own in their active tenant. A finance-admin role may export all invoices in that tenant only.
Alice = north ordinary user; Bob = north finance-admin; Eve = south ordinary user.
Rows: n1 belongs Alice/north; n2 belongs another north user; s1 belongs Eve/south.
GET /invoices/n1 performs a server-side ownership check.
POST /exports takes tenantId and columns from JSON. Worker query filters by tenantId from that JSON. Cache key is 'export:'+requested format. Worker runs as a privileged service.
Observed authorized test: Alice requests north/pdf and gets n1,n2; Eve then requests south/pdf and receives the cached north document.
CI record T19 passed on r41/artifact bbbb…; r42 adds the export cache. The r42 build also contains library parsekit 2.4.0. The supplied synthetic advisory affects parsekit below 2.4.1 when XML external entity parsing is enabled. This export consumes JSON and produces PDF; there is no complete call-path inventory yet.
Webhook event export-paid-9 is retried after the export worker times out.

Work through the case

  1. Trace API→queue→worker→cache→download and identify where authority must be carried and checked.

  2. Write a reproducible two-tenant finding and a minimum positive/negative test matrix including cache order reversal.

  3. Assess the dependency record without claiming the JSON path proves the vulnerable XML code is unreachable everywhere.

  4. Specify a release gate and durable handling of the retried event; decide whether the supplied evidence justifies shipping.

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.

Which invoices may each person export under the policy?

Bob, north finance-admin
Alice, north ordinary user
Eve, south ordinary user

Decide the release questions.

Can candidate r42 ship on the supplied evidence?
Does CI record T19 count as evidence for r42?
Can parsekit 2.4.0 be labeled not affected because this export uses JSON?

Evidence to retain

  • Finding with impact, exact synthetic requests, affected candidate and causal chain.
  • Actor/role/object/cache test plan plus worker authorization checks.
  • Dependency applicability record separating known, unknown and next evidence.
  • Release decision naming blocking evidence and responsible roles.

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

1. Finding connects API, worker and cache causes to observed disclosure.
2. Tests include legitimate finance-admin access and both tenant/ownership denials.
3. Dependency status and test provenance preserve uncertainty and candidate identity.
4. Release gate and retry handling name decisive evidence and accountable owners.
Critical failures: tick any your answer still contains

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

Block the candidate on tenant disclosure and same-tenant ownership bypass. The worker needs trusted actor/tenant/action context and authorization at the actual read/download boundary; a broad service credential does not inherit the user’s permissions. Cache separation must reflect the authorized result audience and invalidation policy, or private exports should bypass shared cache. Bob may export n1,n2 for north, Alice n1 only, and Eve s1 only. T19 is for another artifact and cannot close r42. The dependency remains under investigation until deployed paths/configuration are accounted for; do not label it not affected merely because the reviewed endpoint uses JSON. Retried events must not create duplicate billing effects.

Download case workbook

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: Build and verify a signed software release

Build the release, then repair the consuming gate in exercise.mjs until all 20 tests pass, so evidence about one artifact can no longer approve another: the T19-on-r41 mistake in this case.

Keep: Failing and passing test output, and the test that rejects evidence bound to a different artifact digest, mapped to your release decision.

Node.js 22+ · no dependencies · 60–90 min · 20 tests

Other connected projects

Next path · Specialist

Build an evidence-driven security program

Set ownership and requirements, produce release-specific evidence, manage exceptions and scope external obligations.

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.

Find your next answer

Type to search. Search runs in your browser.

Searches run in your browser, but the host can infer them from the index files they load. The address shows your search so you can bookmark or share it.