Technical appendix — verified findings
Each finding includes severity, CVSS v3.1 score, CWE reference, the evidence we captured, reproduction steps, and a specific fix. This is the section you forward to whoever maintains the site.
Critical
CVSS 9.8
CWE-89 · SQL Injection
Manually verified
Authentication bypass via SQL injection in login
The login form builds its database query by pasting the email straight into the SQL string. Supplying ' OR 1=1-- as the email turns the query into one that always matches the first user — the administrator — and logs in with no valid password.
Evidence — request / response
# Request (email field carries the injection)
POST /rest/user/login HTTP/1.1
Host: owasp-juice.shop
Content-Type: application/json
{"email":"' OR 1=1--","password":"x"}
# Response — authenticated token issued for the admin account
HTTP/1.1 200 OK
{"authentication":{
"token":"eyJhbGci...",
"umail":"[email protected]"
}}
Reproduction
- Open the login page.
- Enter
' OR 1=1-- in the email field and any value in the password field.
- Submit. The server returns a valid session token for
[email protected] — full administrative access, no password required.
Fix: Never concatenate user input into SQL. Use parameterized queries / prepared statements (bound parameters) for the login lookup. With the ORM, pass the email as a bound value rather than building the WHERE clause as a string. Add server-side input validation on the email field as defense in depth.
Critical
CVSS 8.1
CWE-639 · IDOR / Broken Access Control
Manually verified
Broken access control — read any user's basket by ID
The basket endpoint returns whatever basket ID you ask for without checking that it belongs to the logged-in user. Authenticated as a normal test account (basket id=2), requesting id=1 returns another user's basket contents.
Evidence — request / response
# Logged in as our test user (owns basket 2). We request basket 1.
GET /rest/basket/1 HTTP/1.1
Host: owasp-juice.shop
Authorization: Bearer eyJhbGci...<test-user-token>
# Response — another user's basket returned. No ownership check.
HTTP/1.1 200 OK
{"status":"success","data":{
"id":1,"UserId":1,
"Products":[{"name":"Apple Juice (1000ml)","quantity":3}]
}}
Reproduction
- Log in with any account and note your own basket ID.
- Send a GET to
/rest/basket/<a different ID> with your own token.
- The server returns the other user's basket instead of rejecting the request. We confirmed this read-only and did not alter any data.
Fix: On every basket request, verify the requested basket's UserId matches the authenticated user's ID before returning data. Reject with 403 otherwise. Apply the same ownership check to every object-ID endpoint (orders, addresses, feedback).
High
CVSS 7.5
CWE-548 · Exposed Directory
Manually verified
Unauthenticated file directory at /ftp
Requesting /ftp returns a full directory listing with no login. Sensitive files are downloadable, including a KeePass password database (.kdbx) and backup/config files. The /robots.txt entry pointed straight at it.
Evidence — request / response
GET /ftp HTTP/1.1
Host: owasp-juice.shop
HTTP/1.1 200 OK
<ul>
<li><a href="/ftp/coupons_2013.md.bak">coupons_2013.md.bak</a></li>
<li><a href="/ftp/eastere.gg">eastere.gg</a></li>
<li><a href="/ftp/encrypt.pyc">encrypt.pyc</a></li>
</ul>
Reproduction
- Browse to
https://owasp-juice.shop/ftp with no authentication.
- The directory listing renders. Files are individually downloadable.
- Confirmed the listing and a single file fetch only — we did not download or retain sensitive contents.
Fix: Remove the /ftp directory from the web root or place it behind authentication. Disable directory listing at the server level. Move any secrets (password databases, backups) out of web-served paths entirely. Remove the /ftp hint from robots.txt.
Medium
CVSS 6.1
CWE-79 · Reflected XSS
Manually verified
Reflected XSS in the search parameter
The q search parameter is reflected into the page without encoding, so an <iframe>/script payload in the URL executes in the visitor's browser. A crafted link sent to a customer could run script in their session.
Evidence — request / response
GET /#/search?q=<iframe src="javascript:alert(`xss`)"> HTTP/1.1
Host: owasp-juice.shop
# The payload is reflected into the DOM unescaped and executes.
Result: alert dialog fires in the browser; payload rendered verbatim.
Reproduction
- Load the search route with the iframe/script payload in the
q parameter.
- The script executes in the page context — confirmed with a harmless alert, no data touched.
Fix: HTML-encode all user input before rendering it. Use the framework's built-in output binding instead of injecting raw HTML. Add a Content-Security-Policy as a second line of defense (see next finding).
Medium
CVSS 5.3
CWE-942 / CWE-693 · CORS + Missing CSP
Manually verified
Wildcard CORS and no Content-Security-Policy
API responses carry Access-Control-Allow-Origin: *, so any website can read them from a victim's browser. There is also no Content-Security-Policy, so if a script does slip in (see the XSS above) the browser has nothing telling it to refuse.
Evidence — response headers
GET /rest/products/search?q=apple HTTP/1.1
Host: owasp-juice.shop
HTTP/1.1 200 OK
Access-Control-Allow-Origin: * # any origin can read this
Content-Security-Policy: <not set> # no XSS backstop
X-Content-Type-Options: <not set>
Reproduction
- Request any API endpoint and inspect the response headers.
- Confirm
Access-Control-Allow-Origin: * is present and no CSP header is returned.
Fix: Replace the wildcard with your own domain(s): Access-Control-Allow-Origin: https://yourdomain.com. Add a Content-Security-Policy (start with default-src 'self' and loosen as needed) and X-Content-Type-Options: nosniff.
What didn't make the report: the automated scanners also flagged several "potential" issues — a suspected outdated library and two header warnings — that we could not reproduce by hand or that turned out to be false positives. Those are logged internally and excluded here. The five findings above are the ones we confirmed are real.