Ranked by confidence first and severity second: a confirmed medium is more actionable than an unverified critical.
#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
#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
#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
#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
#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
#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
#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
#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
#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
#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
#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
#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
#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
#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
#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
#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
#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
#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
#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
#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
#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
#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
#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
#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