Sample assessment · not a customer’s data

A complete Audn.AI whitebox assessment of OWASP Juice Shop.

Juice Shop is the intentionally vulnerable application the industry keeps around for testing exactly this kind of tool, which is why our published sample is an assessment of it rather than of somebody’s private codebase. Everything below is the deliverable itself — 24 fused findings, the paths derived from them and every artifact a run produces — rendered by the same code that renders a paying reader’s report. Nothing is redacted and nothing is dressed up.

$299 per scan, currently $99 — temporary launch discounttemporary launch discountScan your repo — $99

One payment, one run, one permanent link. Your report is this document with your repository as the subject — however much or little the run finds.

24
Fused findings
11
Reproduced live
7
Attack paths
13
Live findings
40
Static leads

Every figure is recounted from the stored assessment printed below, so this strip cannot overstate the report it sits on top of. 40 static leads and 13 live findings went into the fusion; the tiers below say which of the two is behind each result.

Full assessment · sample

Adversarial assessment ofjuice-shop/juice-shop

24 fused findings, of which 11 were reproduced against the running target, across 7 derived attack paths. Static analysis and a live red-team run were fused into one ranked list; everything below is that evidence in the form the pipeline emits it.

Live target
audn-scan-juice-shop-1424063d.onrender.com
Surface
web-app
Drive mode
default
Run duration
49m 40s
Scenarios
3 planned · 3 produced findings
Run id
a7f3c1e0-9b42-4d18-8c6a-1424063d5e77
Snapshot taken
2026-08-05 02:59 UTC
01 Executive summary

What this run establishes.

11 findings were reproduced against the deployed target — those are facts about the running system, not code smells. 4 of the 11 were flagged in source as well and are corroborated by both halves; the others rest on the live observation alone. 4 static lead(s) were down-ranked because the run exercised that surface without reproducing them. The remainder are leads: real patterns in the source that this run did not reach.

24
Fused findings
11
Reproduced live
7
Attack paths
40
Static leads
13
Live findings

Severity distribution

critical8
high6
medium4
low2
info4

Confidence tiers

Confirmed4Flagged in source AND reproduced against the running target.
Observed live7Reproduced live with no static lead behind it.
Static lead9Flagged in source; the run never reached it.
Likely false positive4Source lead the run exercised without reproducing.

Collection notes

Where a section below is thin, this is why. “We could not read it” and “there was nothing there” are different answers and we do not conflate them.

  • .redteam/sast/findings.md was truncated at 200,000 characters (47,913 omitted).
  • The B2B order RCE, the profile-username eval and the XML-upload XXE are present in source and enabled on the deployed target, but their surfaces were outside the three driven scenarios, so none was revalidated live and all three are reported as static-unverified leads.
  • Artifact collection is the last thing that happens before teardown: the sandbox is destroyed at the end of a run, so anything not read by then cannot be read afterwards.
02 Attack paths

How someone gets in, and what happens then.

Each chain is built only from evidence this run carries. Where a link is documented tradecraft rather than something the run demonstrated, the step says so — an assessment you can trust has to be willing to say “not proven here”.

criticalObserved liveweb-appsc-auth-bypassInsecure direct object reference in routes/basket.ts

The live run exercised this with no static lead behind it: believed because it was observed, and a gap in the source scan worth fixing separately.

  1. 01 · entryReach the web-app surface

    The run drove this through the web-app / api-rest / api-graphql surface under scenario sc-auth-bypass (Authentication & Authorization Bypass), so the entry point is the deployed target's own request path — no insider access assumed.

    sess-01/basket-idor
  2. 02 · weaknessInsecure direct object reference — IDOR exposes another user's shopping basket

    CWE-639 at routes/basket.ts:19. The basket endpoint looks a basket up by the :id path parameter with no check that it belongs to the caller. Incrementing the id returned other customers' baskets and their line items. Reproduced live.

    sess-01/basket-idor
  3. 03 · pivotEscalate into Server-Side Request Forgery (SSRF)

    This class chains into sc-ssrf (Server-Side Request Forgery (SSRF)), and the run also reported "SSRF via the profile-image URL upload" there — the escalation is a path this assessment saw, not a textbook possibility.

    sess-03/ssrf-imageurl
  4. 04 · impactInsecure direct object reference (critical)

    Records belonging to other users or tenants become readable — and frequently writable — by changing an identifier.

criticalObserved liveweb-appsc-auth-bypassMissing integrity verification in lib/insecurity.ts

The live run exercised this with no static lead behind it: believed because it was observed, and a gap in the source scan worth fixing separately.

  1. 01 · entryReach the web-app surface

    The run drove this through the web-app / api-rest / api-graphql surface under scenario sc-auth-bypass (Authentication & Authorization Bypass), so the entry point is the deployed target's own request path — no insider access assumed.

    sess-01/jwt-forge
  2. 02 · weaknessMissing integrity verification — Authentication tokens can be forged with the hardcoded signing key

    CWE-347 at lib/insecurity.ts:21. The RSA private key that signs session JWTs is committed in the source and reachable in the deployed bundle. Using it, a token asserting role admin was minted and accepted by the authorised endpoints. Reproduced live.

    sess-01/jwt-forge
  3. 03 · pivotEscalate into Server-Side Request Forgery (SSRF)

    This class chains into sc-ssrf (Server-Side Request Forgery (SSRF)), and the run also reported "SSRF via the profile-image URL upload" there — the escalation is a path this assessment saw, not a textbook possibility.

    sess-03/ssrf-imageurl
  4. 04 · impactMissing integrity verification (critical)

    Data or a signature is trusted without being verified, so anything an attacker can modify in transit is accepted as authentic.

criticalObserved liveweb-appsc-auth-bypassSQL injection in routes/login.ts

The live run exercised this with no static lead behind it: believed because it was observed, and a gap in the source scan worth fixing separately.

  1. 01 · entryReach the web-app surface

    The run drove this through the web-app / api-rest / api-graphql surface under scenario sc-auth-bypass (Authentication & Authorization Bypass), so the entry point is the deployed target's own request path — no insider access assumed.

    sess-01/login-sqli
  2. 02 · weaknessSQL injection — SQL injection in the login query bypasses authentication

    CWE-89 at routes/login.ts:34. The login handler concatenates req.body.email straight into its user-lookup SQL. Sending ' OR 1=1-- as the email returned the first user and issued a valid session. Reproduced live.

    sess-01/login-sqli
  3. 03 · pivotEscalate into Server-Side Request Forgery (SSRF)

    This class chains into sc-ssrf (Server-Side Request Forgery (SSRF)), and the run also reported "SSRF via the profile-image URL upload" there — the escalation is a path this assessment saw, not a textbook possibility.

    sess-03/ssrf-imageurl
  4. 04 · impactSQL injection (critical)

    Database contents reachable through the injectable parameter can be read, and on a writable connection modified — this is direct data disclosure, not a hardening nit.

criticalObserved liveweb-appsc-software-surfacePath traversal in routes/fileServer.ts

The live run exercised this with no static lead behind it: believed because it was observed, and a gap in the source scan worth fixing separately.

  1. 01 · entryReach the web-app surface

    The run drove this through the web-app / api-rest / api-graphql / mcp-server surface under scenario sc-software-surface (Software Attack-Surface Enumeration), so the entry point is the deployed target's own request path — no insider access assumed.

    sess-02/ftp-traversal
  2. 02 · weaknessPath traversal — Poison null byte defeats the ftp download type check

    CWE-22 at routes/fileServer.ts:33. The public file route admits any slash-free name ending .md or .pdf, then strips everything from a %00 onwards before res.sendFile resolves it under ftp/. Appending %2500.md to a forbidden name served the developer backup ftp/package.json.bak in full. Reproduced live.

    sess-02/ftp-traversal
  3. 03 · pivotEscalate into Server-Side Request Forgery (SSRF)

    This class chains into sc-ssrf (Server-Side Request Forgery (SSRF)), and the run also reported "SSRF via the profile-image URL upload" there — the escalation is a path this assessment saw, not a textbook possibility.

    sess-03/ssrf-imageurl
  4. 04 · impactPath traversal (critical)

    Files outside the intended directory become readable, which in practice means configuration and credential material.

criticalObserved liveweb-appsc-software-surfaceSQL injection in routes/showProductReviews.ts

The live run exercised this with no static lead behind it: believed because it was observed, and a gap in the source scan worth fixing separately.

  1. 01 · entryReach the web-app surface

    The run drove this through the web-app / api-rest / api-graphql / mcp-server surface under scenario sc-software-surface (Software Attack-Surface Enumeration), so the entry point is the deployed target's own request path — no insider access assumed.

    sess-02/nosql-review
  2. 02 · weaknessSQL injection — NoSQL injection in the product-review lookup

    CWE-943 at routes/showProductReviews.ts:36. The review endpoint interpolates the product id into a MongoDB $where JavaScript expression. A crafted id ran attacker-chosen JS in the query engine and returned documents the caller should not see. Reproduced live.

    sess-02/nosql-review
  3. 03 · pivotEscalate into Server-Side Request Forgery (SSRF)

    This class chains into sc-ssrf (Server-Side Request Forgery (SSRF)), and the run also reported "SSRF via the profile-image URL upload" there — the escalation is a path this assessment saw, not a textbook possibility.

    sess-03/ssrf-imageurl
  4. 04 · impactSQL injection (critical)

    Database contents reachable through the injectable parameter can be read, and on a writable connection modified — this is direct data disclosure, not a hardening nit.

criticalObserved liveweb-appsc-ssrfServer-side request forgery in routes/profileImageUrlUpload.ts

The live run exercised this with no static lead behind it: believed because it was observed, and a gap in the source scan worth fixing separately.

  1. 01 · entryReach the web-app surface

    The run drove this through the web-app / api-rest / api-graphql surface under scenario sc-ssrf (Server-Side Request Forgery (SSRF)), so the entry point is the deployed target's own request path — no insider access assumed.

    sess-03/ssrf-imageurl
  2. 02 · weaknessServer-side request forgery — SSRF via the profile-image URL upload

    CWE-918 at routes/profileImageUrlUpload.ts:24. The profile-image feature fetches an attacker-supplied imageUrl server-side with no host validation. Pointing it at internal addresses made the server issue those requests; the cloud metadata endpoint responded. Reproduced live.

    sess-03/ssrf-imageurl
  3. 03 · pivotEscalate into Authentication & Authorization Bypass

    This class chains into sc-auth-bypass (Authentication & Authorization Bypass), and the run also reported "Authentication tokens can be forged with the hardcoded signing key" there — the escalation is a path this assessment saw, not a textbook possibility.

    sess-01/jwt-forge
  4. 04 · impactServer-side request forgery (critical)

    The server's network position is borrowed to reach internal-only services and cloud metadata that the public internet cannot.

highObserved liveweb-appsc-software-surfaceCross-site scripting in frontend/src/app/search-result/search-result.component.html

The live run exercised this with no static lead behind it: believed because it was observed, and a gap in the source scan worth fixing separately.

  1. 01 · entryReach the web-app surface

    The run drove this through the web-app / api-rest / api-graphql / mcp-server surface under scenario sc-software-surface (Software Attack-Surface Enumeration), so the entry point is the deployed target's own request path — no insider access assumed.

    sess-02/xss-search
  2. 02 · weaknessCross-site scripting — Reflected XSS in the search result header

    CWE-79 at frontend/src/app/search-result/search-result.component.html:11. The search term is bound with [innerHTML] and rendered without sanitisation. A query carrying an <iframe src="javascript:…"> payload executed in the tested session. Reproduced live.

    sess-02/xss-search
  3. 03 · pivotEscalate into Server-Side Request Forgery (SSRF)

    This class chains into sc-ssrf (Server-Side Request Forgery (SSRF)), and the run also reported "SSRF via the profile-image URL upload" there — the escalation is a path this assessment saw, not a textbook possibility.

    sess-03/ssrf-imageurl
  4. 04 · impactCross-site scripting (high)

    Attacker script runs inside another user's session, which means session theft and actions performed as that user.

03 Findings

All 24 findings, ranked.

Ranked by confidence first and severity second: a confirmed medium is more actionable than an unverified critical.

  1. #1criticalConfirmedstatic + liveWeak password recovery — a known security answer resets any account

    Confirmed: static analysis flagged the reset handler gating only on an HMAC of the security answer, and the live run reset a seeded account using its publicly known answer. Static + dynamic agreement — highest confidence.

    Tainted flow: req.body.answer → security.hmac(answer) → equality against the stored SecurityAnswer, behind nothing but the bypassable reset limiter below. Security answers in the seed data are guessable public facts, so the password is overwritten on a matched guess.

    cwe CWE-640at routes/resetPassword.ts:41scenario sc-auth-bypassmatched on cwe+fileevidence sess-01/reset-secans
  2. #2highConfirmedstatic + liveImproper access control on the user data export

    Confirmed: the export selects orders on a lossy transform of the caller's own email rather than on an account id, and the run registered an address that masks to a seeded user's and got that user's orders back inside its own export. SAST flagged the caller-controlled query key. Static + dynamic agreement.

    The handler builds email.replace(/[aeiou]/gi, '') and queries ordersCollection.find({ email: updatedEmail }). Vowel-masking collapses distinct addresses onto the same key, and the key is one the caller chooses at registration, so an account whose address masks to the victim's exports the victim's orders.

    cwe CWE-639at routes/dataExport.ts:33scenario sc-auth-bypassmatched on cwe+fileevidence sess-01/dataexport-idor
  3. #3highConfirmedstatic + livePassword-reset rate limit is bypassable via a spoofed X-Forwarded-For

    Confirmed: the limiter keys on the client-supplied X-Forwarded-For header, and the run defeated it by rotating that header while brute-forcing the reset answer. SAST flagged the header-derived key. Static + dynamic agreement.

    rateLimit keyGenerator returns headers['X-Forwarded-For'] ?? ip; a fresh forwarded-for per request puts every attempt in a different bucket, so the 100-per-5-minutes cap never applies.

    cwe CWE-307at server.ts:346scenario sc-auth-bypassmatched on cwe+fileevidence sess-01/reset-ratelimit
  4. #4mediumConfirmedstatic + liveProduct reviews can be posted under a forged author

    Confirmed: the review insert takes the author from the request body rather than the session, and the run posted a review attributed to another user. SAST flagged the unauthenticated author field. Static + dynamic agreement.

    req.body.author is written straight into the reviews collection; the handler never binds the author to the authenticated principal, so authorship can be set to anyone.

    cwe CWE-345at routes/createProductReviews.ts:26scenario sc-software-surfacematched on cwe+fileevidence sess-02/forged-review
  5. #5criticalObserved livelive runSQL injection in the login query bypasses authentication

    The login handler concatenates req.body.email straight into its user-lookup SQL. Sending ' OR 1=1-- as the email returned the first user and issued a valid session, so authentication is bypassed without a password. Reproduced live against the deployed target.

    cwe CWE-89at routes/login.ts:34scenario sc-auth-bypassevidence sess-01/login-sqli
  6. #6criticalObserved livelive runAuthentication tokens can be forged with the hardcoded signing key

    The RSA private key that signs session JWTs is committed in the source and reachable in the deployed bundle. Using it, a token asserting role admin was minted and accepted by the authorised endpoints, so any account — administrator included — can be impersonated. Reproduced live.

    cwe CWE-347at lib/insecurity.ts:21scenario sc-auth-bypassevidence sess-01/jwt-forge
  7. #7criticalObserved livelive runNoSQL injection in the product-review lookup

    The review endpoint interpolates the product id into a MongoDB $where JavaScript expression whenever the NoSQL challenge is enabled, which it was on this deployment. A crafted id ran attacker-chosen JS in the query engine and returned review documents the caller should not see, while measurably blocking the event loop. Reproduced live.

    cwe CWE-943at routes/showProductReviews.ts:36scenario sc-software-surfaceevidence sess-02/nosql-review
  8. #8criticalObserved livelive runPoison null byte defeats the ftp download type check

    The public file route admits any slash-free name ending .md or .pdf, then strips everything from a %00 onwards before res.sendFile resolves it under ftp/. Appending %2500.md to a forbidden name defeated the type check and the developer backup ftp/package.json.bak was served in full. The slash guard holds, so this reaches restricted files inside ftp/ rather than outside it. Reproduced live.

    cwe CWE-22at routes/fileServer.ts:33scenario sc-software-surfaceevidence sess-02/ftp-traversal
  9. #9criticalObserved livelive runIDOR exposes another user's shopping basket

    The basket endpoint looks a basket up by the :id path parameter with no check that it belongs to the caller. Incrementing the id returned other customers' baskets and their line items. Reproduced live.

    cwe CWE-639at routes/basket.ts:19scenario sc-auth-bypassevidence sess-01/basket-idor
  10. #10criticalObserved livelive runSSRF via the profile-image URL upload

    The profile-image feature fetches an attacker-supplied imageUrl server-side with no host validation. Pointing it at internal addresses made the server issue those requests; the cloud metadata endpoint responded. Reproduced live.

    cwe CWE-918at routes/profileImageUrlUpload.ts:24scenario sc-ssrfevidence sess-03/ssrf-imageurl
  11. #11highObserved livelive runReflected XSS in the search result header

    The search term is bound with [innerHTML] and rendered without sanitisation. A query carrying an <iframe src="javascript:…"> payload executed in the tested session. Reproduced live.

    cwe CWE-79at frontend/src/app/search-result/search-result.component.html:11scenario sc-software-surfaceevidence sess-02/xss-search
  12. #12criticalStatic leadsource scanServer-side code execution in the B2B order sandbox

    Static-analysis lead (CWE-94) in routes/b2bOrder.ts the red-team run did not reach — the deprecated /b2b/v2 order interface was not in the surface the driven scenarios enumerated, and standing the lead up means escaping notevil's safeEval rather than merely posting to the endpoint. Unverified — reported as a lead for manual review, not a confirmed vulnerability.

    orderLinesData from the request body is passed to vm.runInContext('safeEval(orderLinesData)') behind only a 2s timeout; a payload that escapes the eval sandbox runs with the process's privileges.

    cwe CWE-94at routes/b2bOrder.ts:23
  13. #13highStatic leadsource scanUnsafe eval of the profile username

    Static-analysis lead (CWE-95) in routes/userProfile.ts the run did not reach — the eval fires on the username already stored against the account, so reaching it means first persisting a #{…} username through the profile form, a write the driven scenarios never performed. Unverified lead.

    The stored username is read back, matched against #{(.)} and its inner expression handed to eval(); the result is then substituted into a server-side pug template.

    cwe CWE-95at routes/userProfile.ts:61
  14. #14highStatic leadsource scanXML external entity injection in the B2B complaint upload

    Static-analysis lead (CWE-611) in routes/fileUpload.ts the run did not reach — the driven scenarios exercised the JSON REST surface, not the multipart /file-upload endpoint the deprecated XML interface hangs off. Nothing gates the parse itself, which makes this the static lead most worth an analyst's hour. Unverified lead.

    Uploaded XML is parsed with entity substitution and external-entity loading both switched on, and the parsed document is echoed back inside the rejection message; a crafted DOCTYPE reads local files or reaches internal URLs through the parser and gets the result returned.

    cwe CWE-611at routes/fileUpload.ts:76
  15. #15highStatic leadsource scanHardcoded HMAC secret behind the security answers

    Static-analysis lead (CWE-321) in lib/insecurity.ts the run did not reach — precomputing security-answer digests, or deriving a deluxe-membership token, was outside the driven scenarios. Unverified lead.

    A fixed HMAC key literal is compiled into the source and is what every stored security answer is keyed under; further down the same file deluxeToken() HMACs with the committed RSA private key. Anyone holding the source can compute both offline.

    cwe CWE-321at lib/insecurity.ts:42
  16. #16mediumStatic leadsource scanWeak password hashing (unsalted MD5)

    Static-analysis lead (CWE-916) in lib/insecurity.ts the run did not reach — confirming it needs a recovered password store to crack against, which the driven scenarios did not export. Unverified lead.

    Passwords are stored as an unsalted MD5 digest, so a leaked table is recovered offline at commodity speed rather than resisting an attacker.

    cwe CWE-916at lib/insecurity.ts:41
  17. #17mediumStatic leadsource scanOpen redirect through a permissive allow-list match

    Static-analysis lead (CWE-601) in lib/insecurity.ts the run did not reach — the redirect endpoint was not in scope for the driven scenarios. Unverified lead.

    isRedirectAllowed accepts a target if any allow-listed URL is a substring of it, so a URL that merely contains an allowed value anywhere passes and the user is forwarded off-site.

    cwe CWE-601at lib/insecurity.ts:136
  18. #18mediumStatic leadsource scanUncontrolled resource consumption in the review lookup

    Static-analysis lead (CWE-400) in routes/showProductReviews.ts distinct from the injection above — the $where expression can be made to spin, which the run did not push to a sustained denial of service. Unverified lead.

    A $where JavaScript expression under caller control can be crafted to burn CPU on the single query thread; there is no per-caller budget on the endpoint.

    cwe CWE-400at routes/showProductReviews.ts:36
  19. #19lowStatic leadsource scanInsecure randomness used for a token secret

    Static-analysis lead (CWE-330) in lib/insecurity.ts the run did not reach — the deny-all path that consumes it is not on a scenario surface. Unverified lead.

    denyAll() builds its secret from Math.random(); a non-cryptographic PRNG is predictable enough that a value derived from it should never be treated as a secret.

    cwe CWE-330at lib/insecurity.ts:53
  20. #20lowStatic leadsource scanOutdated authentication dependency (jsonwebtoken 0.4.0)

    Static-analysis lead (CWE-1104) in package.json the run did not reach — a version-pinned dependency is a supply-chain lead, not a live behaviour the run could exercise. Unverified lead.

    jsonwebtoken is pinned at 0.4.0 (and express-jwt at 0.1.3), releases predating years of algorithm-confusion and verification fixes; the pin is the finding, independent of any single call site.

    cwe CWE-1104at package.json:132
  21. #21infoLikely false positivestatic + livePath traversal in the log-file download route

    The run tested this static lead (CWE-22) in routes/logfileServer.ts and could not reproduce it: every ../ and %2f payload was rejected by the handler's guard that refuses any filename containing a forward slash, so nothing outside logs/ was reachable. A likely false positive. SAST rated it high; down-ranked.

    Reported flow: params.file → path.resolve('logs/', file) → res.sendFile. The scan missed the preceding if (!file.includes("/")) guard that blocks traversal.

    cwe CWE-22at routes/logfileServer.ts:14scenario sc-software-surfacematched on cwe+filereported as highevidence sess-02/logfile-probe
  22. #22infoLikely false positivestatic + liveSSRF in the webhook notification handler

    The run tested this static lead (CWE-918) in lib/webhook.ts and found nothing to point anywhere: the URL that reaches fetch() is notify()'s last parameter, which defaults to process.env.SOLUTIONS_WEBHOOK and is never passed by its one caller, so no request value reaches the sink — and with that variable unset the function returns before the fetch. A likely false positive beside the confirmed SSRF in the image-URL upload. SAST rated it high; down-ranked.

    The scanner treated a function parameter as a taint source and followed it to an outbound fetch. Its only value is operator configuration read from the environment; there is no caller-controlled input on the path, which is what an SSRF needs.

    cwe CWE-918at lib/webhook.ts:18scenario sc-ssrfmatched on cwe+filereported as highevidence sess-03/webhook-probe
  23. #23infoLikely false positivestatic + liveOS command injection in the metrics endpoint

    The run tested this static lead (CWE-78) against /metrics, which is a Prometheus scrape endpoint: the only request value serveMetrics() reads is the User-Agent header, and it is matched against a configured list of scrapers to decide whether a challenge counts as solved. The module imports no child_process, so there is no shell on the path and shell metacharacters in the header changed nothing. Could not reproduce; a likely false positive. SAST rated it medium; down-ranked.

    The scan followed the User-Agent header into a comparison and typed the sink as command-like. The value is only tested with String.includes against config.challenges.metricsIgnoredUserAgents; everything else on the endpoint is a counter label.

    cwe CWE-78at routes/metrics.ts:87scenario sc-software-surfacematched on cwe+filereported as mediumevidence sess-02/metrics-probe
  24. #24infoLikely false positivestatic + liveMissing authorization on the order-history endpoints

    The run tested this static lead (CWE-862) in routes/orderHistory.ts: allOrders() really does read every order with no check of its own, but both it and toggleDeliveryStatus() are mounted behind security.isAccounting() in server.ts, and every request without the accounting role came back 403 {"error":"Malicious activity detected"}. Could not reproduce; a likely false positive. SAST rated it high; down-ranked.

    SAST saw two handlers read and mutate order records with no inline authorization check — ordersCollection.find() with no filter, and an update keyed on params.id. The check is applied at the route mount, which a file-local scan of routes/orderHistory.ts cannot see.

    cwe CWE-862at routes/orderHistory.ts:27scenario sc-auth-bypassmatched on cwe+filereported as highevidence sess-01/order-authz-probe
04 Static analysis detail

The source scan, unfiltered.

High recall, low precision by design — this is the raw lead list the fusion above reasoned over, including the leads it down-ranked. Where the scanner computed a CVSS base score it is printed as the scanner gave it, unadjusted; hover the score for the vector it was derived from.

40 merged leads
  • highsemgrepjs.express.sql-injection.tainted-sql-string CVSS 9.8 User input from req.body.email is concatenated into a raw SQL string · routes/login.ts
  • highsemgrepjs.express.sql-injection.tainted-sql-string CVSS 9.1 Search criterion is interpolated into a raw SELECT with a LIKE clause · routes/search.ts
  • highcodeqljs.mongodb.nosql-where-injection CVSS 9.0 Request id flows into a MongoDB $where JavaScript expression · routes/showProductReviews.ts
  • highsemgrepjs.node.path-traversal.sendfile Path parameter reaches res.sendFile after a slash and extension check only · routes/fileServer.ts
  • mediumsemgrepjs.node.path-traversal.sendfile Path parameter reaches res.sendFile under logs/ · routes/logfileServer.ts
  • highcodeqljs.node.ssrf.unvalidated-fetch CVSS 8.6 Server-side fetch on an unvalidated user-supplied URL · routes/profileImageUrlUpload.ts
  • mediumcodeqljs.node.ssrf.unvalidated-fetch Outbound fetch on a URL taken from a function parameter · lib/webhook.ts
  • highsemgrepjs.angular.xss.innerhtml-binding Value bound to [innerHTML] without sanitisation · frontend/src/app/search-result/search-result.component.html
  • mediumsemgrepjs.angular.xss.bypass-security-trust bypassSecurityTrustHtml applied to the q query parameter · frontend/src/app/search-result/search-result.component.ts
  • highcodeqljs.node.code-injection.vm-runincontext CVSS 9.3 Request body evaluated in a vm sandbox guarded only by a timeout · routes/b2bOrder.ts
  • highsemgrepjs.node.eval.dynamic-code User-controlled username expression passed to eval() · routes/userProfile.ts
  • highcodeqljs.node.xxe.external-entities-enabled CVSS 7.7 XML parsed with external entity resolution enabled · routes/fileUpload.ts
  • highsemgrepjs.crypto.hardcoded-key CVSS 7.5 Hardcoded RSA private key used to sign JWTs · lib/insecurity.ts
  • mediumsemgrepjs.crypto.hardcoded-hmac-secret Hardcoded HMAC secret literal · lib/insecurity.ts
  • mediumsemgrepjs.crypto.weak-hash.md5 MD5 used to hash passwords · lib/insecurity.ts
  • mediumcodeqljs.node.open-redirect.substring-allowlist Redirect target validated with a substring match · lib/insecurity.ts
  • lowsemgrepjs.crypto.insecure-randomness Math.random() used to derive a secret · lib/insecurity.ts
  • mediumcodeqljs.express.missing-authorization Handler reads every order record with no inline authorization check · routes/orderHistory.ts
  • highcodeqljs.express.broken-object-level-authz CVSS 8.1 Basket looked up by path id with no ownership check · routes/basket.ts
  • mediumcodeqljs.express.broken-object-level-authz Export selects orders on a vowel-masked email rather than an account id · routes/dataExport.ts
  • highsemgrepjs.auth.weak-recovery.security-question Password reset gated only on a guessable security answer · routes/resetPassword.ts
  • mediumsemgrepjs.express.rate-limit.client-controlled-key Rate limiter keys on the client X-Forwarded-For header · server.ts
  • mediumsemgrepjs.express.missing-integrity-check.author Review author taken from the request body · routes/createProductReviews.ts
  • mediumsemgrepjs.os-command.child-process Request header value reaches a sink the rule types as command-like · routes/metrics.ts
  • lowvulnhunterjs.dependency.outdated jsonwebtoken pinned at 0.4.0 · package.json
  • lowvulnhunterjs.dependency.outdated express-jwt pinned at 0.1.3 · package.json
  • lowvulnhunterjs.dependency.outdated sanitize-html pinned at 1.4.2 · package.json
  • lowvulnhunterjs.dependency.outdated marsdb (unmaintained) present · package.json
  • mediumcodeqljs.node.dos.uncontrolled-consumption Caller-controlled $where expression can be made to spin · routes/showProductReviews.ts
  • lowvulnhunterjs.node.dos.blocking-sleep Synchronous busy-wait loop in the module-level sleep shim · routes/showProductReviews.ts
  • mediumsemgrepjs.express.cors.wildcard CORS enabled with a permissive default · server.ts
  • lowsemgrepjs.express.helmet.xss-filter-disabled helmet.xssFilter is not enabled · server.ts
  • mediumvulnhunterjs.node.insecure-deserialization Untrusted structured input reaches an evaluator · routes/b2bOrder.ts
  • mediumcodeqljs.jwt.algorithm-confusion jws.verify accepts a token without pinning the algorithm · lib/insecurity.ts
  • lowsemgrepjs.node.info-exposure.error-detail Parsed XML is echoed back to the client in the rejection message · routes/fileUpload.ts
  • lowsemgrepjs.node.info-exposure.error-detail Raw error message returned to the client · routes/createProductReviews.ts
  • lowvulnhunterjs.express.directory-listing The /ftp directory is mounted with a browsable index · server.ts
  • lowvulnhunterjs.crypto.weak-coupon-encoding Coupons encoded with reversible z85, not signed · lib/insecurity.ts
  • lowsemgrepjs.node.sensitive-data.logs Log files served over HTTP · routes/logfileServer.ts
  • lowcodeqljs.express.missing-authentication PUT /api/Products/:id is mounted with its isAuthorized() guard commented out · server.ts

The digest handed to the agent

# Source-scan digest — OWASP Juice Shop

Merged from VVAH and VulnHunter, with semgrep and codeql-style rule packs. 40 leads after dedup.
This is the raw source-scan half: high recall, low precision. Triage against the live run is what
turns it into the fused report — a lead here is a place to look, not a verdict.

## By severity (source scan, pre-triage)
- error   : 12
- warning : 15
- note    : 13

## Highest-signal leads

CVSS 3.1 base scores below are the scanner's own, quoted unadjusted and only where it computed one.
They rank the source scan against itself; they are not a verdict on reachability, which is what the
live run is for.

- error   js.express.sql-injection.tainted-sql-string  routes/login.ts:34            CVSS 9.8
    User input (req.body.email) → raw SQL string. dedup x3.
- error   js.node.code-injection.vm-runincontext       routes/b2bOrder.ts:23         CVSS 9.3
    orderLinesData → vm.runInContext, timeout-guarded only.
- error   js.mongodb.nosql-where-injection             routes/showProductReviews.ts:36  CVSS 9.0
    req.params.id → MongoDB $where JavaScript expression.
- error   js.node.ssrf.unvalidated-fetch               routes/profileImageUrlUpload.ts:24  CVSS 8.6
    imageUrl → server-side fetch, no host allow-list.
- error   js.express.broken-object-level-authz         routes/basket.ts:19           CVSS 8.1
    Basket looked up by path id, no ownership check.
- error   js.node.xxe.external-entities-enabled        routes/fileUpload.ts:76       CVSS 7.7
    XML parsed with external entities enabled.
- error   js.crypto.hardcoded-key                      lib/insecurity.ts:21          CVSS 7.5
    RSA private key committed in source, used to sign JWTs.
- error   js.node.eval.dynamic-code                    routes/userProfile.ts:61
    username #{...} → eval(). No CVSS computed — the rule carries no impact model.
- error   js.node.path-traversal.sendfile              routes/fileServer.ts:33
    params.file → res.sendFile after a slash check and an extension check only.
- error   js.auth.weak-recovery.security-question      routes/resetPassword.ts:41
    Reset gated on a guessable security answer.

## Notable warnings

- warning js.node.ssrf.unvalidated-fetch               lib/webhook.ts:18
- warning js.crypto.weak-hash.md5                       lib/insecurity.ts:41
- warning js.node.open-redirect.substring-allowlist     lib/insecurity.ts:136
- warning js.jwt.algorithm-confusion                    lib/insecurity.ts:55
- warning js.os-command.child-process                   routes/metrics.ts:87
- warning js.express.missing-authorization              routes/orderHistory.ts:27
- warning js.node.insecure-deserialization              routes/b2bOrder.ts:19

## Dependency leads (VulnHunter)

- note    jsonwebtoken@0.4.0, express-jwt@0.1.3, sanitize-html@1.4.2, marsdb (unmaintained)

The full per-lead detail — data-flow traces, related locations, every dedup member — continues
below and is where this digest was cut.

…[truncated: .redteam/sast/findings.md was 247,913 characters; 47,913 omitted]
05 The red-team run

The agent’s own record.

Exactly as the agent writes them, unedited. Markdown and JSONL are shown as text — we do not re-render a scanned system’s output as markup.

13 persisted findings13 harvested at snapshottranscript: 214 entries · 3 session(s)
  • criticalsc-auth-bypassSQLi in the login query bypasses authentication · confirmed · sess-01/login-sqli
  • criticalsc-auth-bypassJWT forged with the hardcoded RSA signing key · confirmed · sess-01/jwt-forge
  • criticalsc-auth-bypassIDOR reads another user's basket by id · confirmed · sess-01/basket-idor
  • criticalsc-auth-bypassPassword reset via a known security answer · confirmed · sess-01/reset-secans
  • highsc-auth-bypassData export of another user's records · confirmed · sess-01/dataexport-idor
  • highsc-auth-bypassReset rate limit bypassed via X-Forwarded-For rotation · confirmed · sess-01/reset-ratelimit
  • criticalsc-software-surfaceNoSQL $where injection on product reviews · confirmed · sess-02/nosql-review
  • criticalsc-software-surfacePoison null byte defeats the ftp download type check · confirmed · sess-02/ftp-traversal
  • highsc-software-surfaceReflected XSS in the search result header · confirmed · sess-02/xss-search
  • mediumsc-software-surfaceForged review author accepted · confirmed · sess-02/forged-review
  • criticalsc-ssrfSSRF via the profile-image URL upload reaches metadata · confirmed · sess-03/ssrf-imageurl
  • infosc-software-surfaceLog-file path traversal probe blocked by the slash guard · refuted · sess-02/logfile-probe
  • infosc-auth-bypassOrder-history authz probe returned 403 without the accounting role · refuted · sess-01/order-authz-probe

.redteam/report.md

# Red-team assessment — OWASP Juice Shop

Target: https://audn-scan-juice-shop-1424063d.onrender.com
Surface: web-app (Angular SPA over an Express REST API)
Scenarios driven: sc-auth-bypass, sc-software-surface, sc-ssrf
Sessions: 3

## Summary

Eleven issues were reproduced against the running target. Two refuted probes were filed as findings
in their own right and two further static leads were run down without filing; all four are written
up below. The application authenticates with a string-concatenated SQL query, signs its own session
tokens with a key that ships in the bundle, and fetches attacker-supplied URLs server-side
without validation. Any one of those is sufficient to take over the application; together they are
a full compromise chain from an anonymous request.

## Confirmed and observed-live

### 1. SQL injection in the login query (critical)
File: routes/login.ts:34

The email field is concatenated straight into the user-lookup query.

    curl -s -X POST "$T/rest/user/login" \
      -H 'Content-Type: application/json' \
      --data-raw "{\"email\":\"' OR 1=1--\",\"password\":\"x\"}"

Returns a valid authentication token for the first user in the table. No password required.

### 2. Forged session tokens (critical)
File: lib/insecurity.ts:21

The RSA private key used to sign JWTs is in the source tree. Minting a token with role=admin and
presenting it is accepted everywhere the app checks authorisation. Impersonation of any user,
including the administrator, follows directly.

### 3. NoSQL injection on product reviews (critical)
File: routes/showProductReviews.ts:36

The product id is dropped into a MongoDB $where JavaScript expression.

    curl -s "$T/rest/products/sleep(1000)/reviews"

The response is delayed by the evaluated sleep, confirming arbitrary JS runs in the query engine.

### 4. Poison null byte on the ftp route (critical)
File: routes/fileServer.ts:33

The name must be slash-free and end .md or .pdf; everything from a %00 onwards is then cut off
before res.sendFile resolves it under ftp/. So the type check is what the null byte defeats:

    curl -s "$T/ftp/package.json.bak%2500.md" -o package.json.bak

The slash guard still holds — this reaches restricted files inside ftp/, not files above it.

### 5. IDOR on the shopping basket (critical)
File: routes/basket.ts:19

    curl -s "$T/rest/basket/2" -H "Authorization: Bearer $TOKEN"

Returns basket 2 regardless of which basket belongs to the caller.

### 6. SSRF via the profile-image URL (critical)
File: routes/profileImageUrlUpload.ts:24

    curl -s -X POST "$T/profile/image/url" \
      -b "token=$TOKEN" \
      -H 'Content-Type: application/json' \
      -d '{"imageUrl":"http://169.254.169.254/latest/meta-data/"}'

The server issued the request and stored the metadata response as the profile image. Note the
cookie: the handler reads its session from the token COOKIE rather than the Authorization header, so
a Bearer-only request is rejected before the fetch.

### 7. Reflected XSS in search (high)
File: frontend/src/app/search-result/search-result.component.html:11

The search term is rendered with [innerHTML]; an <iframe src="javascript:..."> payload fires.

### 8-11. Weak password recovery, data-export access control, reset rate-limit bypass, forged author

Each reproduced live and each also flagged by the source scan — see the fused report for the
confirmed tier and the source-scan detail.

## Probed and discarded

- Log-file download (routes/logfileServer.ts): every traversal payload was rejected by the guard
  that refuses a filename containing a forward slash. Not exploitable as reported.
- Order history (routes/orderHistory.ts): allOrders reads every order without a check of its own,
  but server.ts mounts it behind security.isAccounting(); every request without the accounting role
  returned 403 "Malicious activity detected". Not exploitable for an unprivileged attacker.
- Webhook notifier (lib/webhook.ts): the fetch target is notify()'s webhook parameter, which only
  ever holds process.env.SOLUTIONS_WEBHOOK. No request value reaches it, so there was nothing to
  redirect at an internal address.
- Metrics endpoint (routes/metrics.ts): /metrics reads only the User-Agent header and compares it
  against a configured list. No child_process anywhere in the module; injection had nowhere to go.

## Not reached

The B2B order sandbox (RCE), the profile-username eval and the XML upload (XXE) are all present in
the source and none of them is disabled on this deployment — the run simply did not drive their
surfaces. The B2B order interface and the multipart /file-upload endpoint were outside the
enumerated surface, and the username eval reads a value that has to be persisted through the profile
form first. They are reported as static-unverified leads, not findings.

.redteam/findings.jsonl

{"id":"rt-01","scenario_id":"sc-auth-bypass","severity":"critical","status":"confirmed","cwe":"CWE-89","file":"routes/login.ts","line":34,"label":"SQLi in the login query bypasses authentication","evidence_ref":"sess-01/login-sqli"}
{"id":"rt-02","scenario_id":"sc-auth-bypass","severity":"critical","status":"confirmed","cwe":"CWE-347","file":"lib/insecurity.ts","line":21,"label":"JWT forged with the hardcoded RSA signing key","evidence_ref":"sess-01/jwt-forge"}
{"id":"rt-03","scenario_id":"sc-auth-bypass","severity":"critical","status":"confirmed","cwe":"CWE-639","file":"routes/basket.ts","line":19,"label":"IDOR reads another user's basket by id","evidence_ref":"sess-01/basket-idor"}
{"id":"rt-04","scenario_id":"sc-auth-bypass","severity":"critical","status":"confirmed","cwe":"CWE-640","file":"routes/resetPassword.ts","line":41,"label":"Password reset via a known security answer","evidence_ref":"sess-01/reset-secans"}
{"id":"rt-05","scenario_id":"sc-auth-bypass","severity":"high","status":"confirmed","cwe":"CWE-639","file":"routes/dataExport.ts","line":33,"label":"Data export of another user's records","evidence_ref":"sess-01/dataexport-idor"}
{"id":"rt-06","scenario_id":"sc-auth-bypass","severity":"high","status":"confirmed","cwe":"CWE-307","file":"server.ts","line":346,"label":"Reset rate limit bypassed via X-Forwarded-For rotation","evidence_ref":"sess-01/reset-ratelimit"}
{"id":"rt-07","scenario_id":"sc-software-surface","severity":"critical","status":"confirmed","cwe":"CWE-943","file":"routes/showProductReviews.ts","line":36,"label":"NoSQL $where injection on product reviews","evidence_ref":"sess-02/nosql-review"}
{"id":"rt-08","scenario_id":"sc-software-surface","severity":"critical","status":"confirmed","cwe":"CWE-22","file":"routes/fileServer.ts","line":33,"label":"Poison null byte defeats the ftp download type check","evidence_ref":"sess-02/ftp-traversal"}
{"id":"rt-09","scenario_id":"sc-software-surface","severity":"high","status":"confirmed","cwe":"CWE-79","file":"frontend/src/app/search-result/search-result.component.html","line":11,"label":"Reflected XSS in the search result header","evidence_ref":"sess-02/xss-search"}
{"id":"rt-10","scenario_id":"sc-software-surface","severity":"medium","status":"confirmed","cwe":"CWE-345","file":"routes/createProductReviews.ts","line":26,"label":"Forged review author accepted","evidence_ref":"sess-02/forged-review"}
{"id":"rt-11","scenario_id":"sc-ssrf","severity":"critical","status":"confirmed","cwe":"CWE-918","file":"routes/profileImageUrlUpload.ts","line":24,"label":"SSRF via the profile-image URL upload reaches metadata","evidence_ref":"sess-03/ssrf-imageurl"}
{"id":"rt-12","scenario_id":"sc-software-surface","severity":"info","status":"refuted","cwe":"CWE-22","file":"routes/logfileServer.ts","line":14,"label":"Log-file path traversal probe blocked by the slash guard","evidence_ref":"sess-02/logfile-probe"}
{"id":"rt-13","scenario_id":"sc-auth-bypass","severity":"info","status":"refuted","cwe":"CWE-862","file":"routes/orderHistory.ts","line":27,"label":"Order-history authz probe returned 403 without the accounting role","evidence_ref":"sess-01/order-authz-probe"}
06 Operational record

How the run actually behaved.

The part most reports omit. If the agent was blocked, throttled or crashed, it is visible here — which is how you judge whether an empty section means “clean” or “never reached”.

Agent progressphase: donefindings.jsonl: 13 lines · 3.1 KBreport.md: 4.7 KB

Gateway log tail

612 lines · 40.6 KBno known failure symptoms matched
02:41:07 [gateway] session sess-03 tool=http_request GET /profile
02:41:08 [gateway] session sess-03 tool=http_request POST /profile/image/url
02:41:08 [agent] ssrf: pointing imageUrl at http://169.254.169.254/latest/meta-data/
02:41:09 [gateway] upstream 200, 512 bytes stored as profile image
02:41:09 [agent] ssrf reproduced (sess-03/ssrf-imageurl) — metadata endpoint reachable
02:41:14 [gateway] session sess-03 tool=llm model=frontier-k3t tokens=1841
02:41:22 [gateway] rate-limit: upstream returned 429, backing off 20s
02:41:22 [agent] throttled by model gateway; holding the SSRF follow-up
02:41:42 [gateway] resuming after backoff, model=frontier-k3t
02:41:43 [agent] recovered; re-issuing the internal-range sweep
02:41:51 [gateway] session sess-03 tool=http_request POST /profile/image/url (10.0.0.0/8 sweep)
02:41:52 [gateway] upstream refused: connect ECONNREFUSED for RFC-1918 target
02:41:55 [gateway] session sess-03 tool=read lib/webhook.ts
02:41:56 [agent] webhook lead: fetch target is notify()'s webhook param, default process.env.SOLUTIONS_WEBHOOK
02:41:56 [agent] webhook ssrf probe (sess-03/webhook-probe) has no caller-controlled url — refuted
02:42:03 [gateway] session sess-01 tool=http_request POST /rest/user/reset-password
02:42:04 [agent] reset rate-limit: rotating X-Forwarded-For per attempt
02:42:19 [gateway] 12 reset attempts accepted without a 429
02:42:20 [agent] reset rate-limit bypass confirmed (sess-01/reset-ratelimit)
02:42:31 [gateway] session sess-01 tool=http_request GET /rest/order-history/orders
02:42:31 [gateway] upstream 403 {"error":"Malicious activity detected"}
02:42:32 [agent] order-history authz probe (sess-01/order-authz-probe) returned 403 — refuted
02:42:38 [gateway] session sess-02 tool=http_request GET /metrics (user-agent: $(id))
02:42:38 [gateway] upstream 200, prometheus text exposition, payload echoed nowhere
02:42:39 [agent] metrics command-injection probe (sess-02/metrics-probe) found no shell sink — refuted
02:42:44 [gateway] session sess-02 tool=http_request GET /ftp/package.json.bak%2500.md
02:42:45 [gateway] upstream 200, 1204 bytes
02:42:45 [agent] ftp poison null byte confirmed (sess-02/ftp-traversal)
02:42:58 [gateway] session sess-02 tool=http_request GET /rest/products/sleep(1000)/reviews
02:43:00 [gateway] upstream 200 after 1002ms
02:43:00 [agent] nosql $where injection confirmed (sess-02/nosql-review)
02:43:12 [gateway] session sess-02 tool=http_request PUT /rest/products/1/reviews (forged author)
02:43:13 [agent] forged review author accepted (sess-02/forged-review)
02:43:24 [agent] all driven scenarios exhausted; writing findings.jsonl and report.md
02:43:40 [gateway] session sess-02 tool=write .redteam/findings.jsonl 13 lines
02:43:41 [gateway] session sess-02 tool=write .redteam/report.md
02:43:41 [agent] run complete: 11 reproduced, 2 refuted
02:43:42 [gateway] heartbeat ok; phase=done

Run diagnostics

{
  "run_id": "a7f3c1e0-9b42-4d18-8c6a-1424063d5e77",
  "instance_id": "penclaw-slot-1",
  "gateway": {
    "running": true,
    "pid": 4218,
    "last_heartbeat_at": "2026-08-05T02:43:42.000Z"
  },
  "kairos": {
    "enabled": true,
    "tick": "30s",
    "fires": 96,
    "last_fired_at": "2026-08-05T02:43:40.000Z"
  },
  "sandbox": {
    "network": "penclaw-redteam-egress",
    "network_exists": true,
    "containers": 0,
    "last_launch_error": null
  },
  "artifacts": {
    "findings_jsonl_bytes": 3172,
    "report_md_bytes": 4843,
    "run_state_phase": "done",
    "run_state_updated_at": "2026-08-05T02:43:42.000Z"
  }
}

Reconnaissance brief

What the pipeline understood about the repository before it deployed and attacked it.

# Reconnaissance brief — OWASP Juice Shop

OWASP Juice Shop is a deliberately insecure web application maintained as a security-training
target. The deployed instance is an Angular single-page frontend served over an Express REST API,
with a SQLite relational store and a MongoDB-compatible store for reviews and orders.

## Surface
- Public web-app: the Angular client plus the /rest and /api endpoints it calls.
- Authentication is JWT-based; tokens are minted and verified in lib/insecurity.ts.
- File download surfaces under /ftp and /support/logs.

## Scenarios selected
- sc-auth-bypass — the login, session-token, basket, data-export and password-reset paths.
- sc-software-surface — enumeration of the review, search, upload and file-serving endpoints.
- sc-ssrf — the profile-image URL upload and any other server-initiated request.

## Notes
The application ships challenge flags that gate some of its most severe behaviours, and several of
them share one switch — the NoSQL $where path, the B2B order eval and the profile-username eval are
enabled or disabled together. This deployment left them enabled, which is why the NoSQL injection
could be reproduced; the leads that stayed static-unverified did so because the run never drove
their surface, not because the deployment turned them off.
07 Run timeline

26 events, in order.

  • reconCloning github.com/juice-shop/juice-shop and reading the tree
  • reconClassified surface: web-app (Angular SPA over an Express REST API)
  • reconSelected scenarios: sc-auth-bypass, sc-software-surface, sc-ssrf
  • provisionRequested a PenClaw run slot
  • provisionSlot assigned; egress network penclaw-redteam-egress present
  • deploy_targetDeploying the target to Render
  • deploy_targetFirst health check timed out; the free dyno was still building — retrying
  • deploy_targetTarget healthy at audn-scan-juice-shop-1424063d.onrender.com
  • scanSource scan started (VVAH + VulnHunter)
  • scanSource scan merged to 40 leads after dedup
  • scan.redteam/sast/findings.md was truncated at 200,000 characters
  • authorizeInstall authorization recorded for the deployed target
  • composeComposed the run brief and per-scenario worklists
  • kickstartKickstarted the red-team run; 3 sessions opening
  • monitorsess-01 reproduced SQL injection on the login query
  • monitorsess-01 forged an admin session token with the hardcoded key
  • monitorsess-01 read another basket and exported another user record
  • monitorModel gateway returned 429; backed off 20s and resumed
  • monitorsess-03 refuted the webhook SSRF probe (the fetch url comes from the environment)
  • monitorsess-01 refuted the order-history authz probe (403 without the accounting role)
  • monitorsess-02 reproduced the ftp poison null byte and the NoSQL injection
  • monitorsess-03 reached cloud metadata via the profile-image SSRF
  • monitorRun complete: 11 reproduced, 2 refuted
  • reportFused source scan and live run into 24 findings across four tiers
  • reportSnapshot captured and deliverable email sent
  • doneSandbox scheduled for teardown

Times are UTC. Started 2026-08-05 02:08 UTC; finished 2026-08-05 02:58 UTC. This run is long finished and the timeline above is the whole record of it. Your own run streams these same events while it works.

For your repository

What $299, currently $99, buys for your repository.

Read the table as a correspondence rather than a feature list: every row on the left is something you can go and check in the report above, and the row beside it is what the same section says when the subject is your code.

A running copy, not a code read

In this sampleJuice Shop was built from its own source and deployed to a throwaway host — audn-scan-juice-shop-1424063d.onrender.com — then attacked while it was serving traffic. Nothing below is a guess about what the code might do under load.

For your repositoryWe build and deploy your repository the same way, from the branch you nominate, into a sandbox that is destroyed when the run ends. No production credentials, and nothing ever points at your own infrastructure.

Aim taken from your source

In this sampleThe reconnaissance brief further down is what the pipeline worked out about the repository before it deployed anything — stack, routes, storage, auth model — and it is what chose the 3 scenarios that were selected.

For your repositoryThe same read runs against your repository, so the attempt is aimed at the framework, data layer and auth model you really use. The reconnaissance brief is in your report too, so you can see what we concluded about your stack and where we got it wrong.

Proof taken from the outside

In this sample24 findings survived fusion across both halves — 40 static leads and 13 live findings went in — and 11 of them were reproduced against the running application rather than inferred from reading it, of which 4 are corroborated by both halves at once.

For your repositoryYou find out what somebody holding nothing but your URL can actually reach — each corroborated finding for your repository naming the scenario that drove it and the evidence reference it was filed under, with the agent's own report.md printed in full beneath the ranked list, which is where it writes up how it reproduced them. Findings the run could not stand up are tiered as such, with what was attempted.

Four buckets, not one score

In this sampleConfirmed, observed live, static lead and likely false positive are counted separately in the executive summary, each with the sentence that explains it. Nothing is averaged into a grade.

For your repositoryYour findings arrive in the same four buckets and are ranked confidence first: a confirmed medium outranks an unverified critical, so the list is already in the order you should work it.

CWE, file and line

In this sample21 CWEs cited across 19 distinct source files, with each finding pinned to the handler and line it lives on instead of to “the login flow”.

For your repositoryThe same for your repository: CWE, path and line, the scenario id that produced it, and one sentence saying why it sits where it sits. Enough to open the file and fix it, or to argue that we are wrong.

Attack paths as ordered chains

In this sample7 chains, each written entry → weakness → pivot → consequence and built only from evidence this run carries. Where a link is documented tradecraft rather than something the run demonstrated, the step says so.

For your repositoryYour chains name your own routes and files at each step, and mark which links the run observed against which are documented tradecraft — so a chain that depends on an unproven step cannot be read as a demonstrated one.

The raw static half, unfiltered

In this sample40 source-scan leads are printed as they arrived, including the ones the fusion down-ranked, with rule ids, tools and paths intact.

For your repositoryYou get the raw lead list for your repository alongside the ranked one, so you can audit the fusion instead of taking it on trust.

The agent's own artifacts

In this samplereport.md and findings.jsonl as the agent writes them, plus the transcript summary: all 7 artifact classes the collector goes after, printed as text rather than re-rendered as markup.

For your repositoryThe same files, pulled off your sandbox in the seconds before teardown. Where one could not be read the report names it — “we could not read it” and “there was nothing there” are different answers and we do not conflate them.

The operational record

In this sampleGateway log tail, run diagnostics and 26 timeline entries — the section most reports omit, and the only way to tell a clean result from a run that never reached the surface.

For your repositoryIdentical for your run. If the agent was blocked, throttled or crashed on your repository, you see it here instead of receiving a quiet all-clear.

A permanent link

In this sampleThis page: no login, no expiry, and the same renderer a paying reader gets — which is precisely why the sample cannot quietly drift from the product.

For your repositoryYour report gets its own link, emailed the moment the run ends, and stays readable at it. One payment, no subscription, no seat to provision.

One run. One payment. No subscription.

  • A deployed copy of your app, attacked — built from the branch you nominate, driven live, and torn down when the run finishes.
  • One ranked list, four honest tiers — what was reproduced, what is only a lead, and what the run exercised without reproducing.
  • The whole evidence file — attack paths, the raw static half, the agent’s own artifacts and the operational record of how the run behaved.
  • No report, no charge — the payment page only ever appears on a run that actually produced one of these.
$299 per scan, currently $99 — temporary launch discounttemporary launch discount

The second link is the subject of this sample, so you can read the source yourself and check that every weakness named above is one Juice Shop genuinely ships.

    Sample assessment — OWASP Juice Shop, in full | Audn.AI