agentggagentgg
Back to all findings
MEDIUMconfirmedxssxssa6f39535fee5

Reflected XSS via bypassSecurityTrustHtml on order tracking result

Order-tracking result interpolates the untrusted `orderId` value into an HTML string and pushes it through `bypassSecurityTrustHtml`, which the template then renders via `[innerHtml]`, enabling arbitrary script execution.

Filefrontend/src/app/track-result/track-result.component.ts
Lines4848
Confidence
90%
File statusvalidated
Details

In frontend/src/app/track-result/track-result.component.ts line 48:

this.orderId = this.route.snapshot.queryParams.id
this.trackOrderService.find(this.orderId).subscribe((results) => {
  this.results.orderNo = this.sanitizer.bypassSecurityTrustHtml(`<code>${results.data[0].orderId}</code>`)
  ...
})

The sink (bypassSecurityTrustHtml) is bound to the page in track-result.component.html line 9:

<h1><span translate>TITLE_SEARCH_RESULTS</span> - <span [innerHtml]="results.orderNo"></span></h1>

results.data[0].orderId originates from the response of GET /rest/track-order/<id>, where <id> is user-controlled via the ?id= query parameter (this.orderId = this.route.snapshot.queryParams.id). Angular’s bypassSecurityTrustHtml explicitly disables sanitization, so the interpolated value is rendered as raw HTML. No DOMPurify/sanitize-html is applied. An attacker can craft a URL whose id parameter (or whose stored order record’s orderId field) contains markup such as <img src=x onerror=alert(document.cookie)> and the payload will execute in the victim’s browser when they visit the resulting track-result page.

This matches the brief’s Angular [innerHTML] / bypassSecurityTrustHtml true-positive pattern: an bypassSecurityTrust* sink fed an untrusted, interpolated string with no sanitizer in between.

Proof of concept
  1. Submit a feedback/order with an orderId containing HTML, or otherwise control the id query parameter / response payload. For the simplest stored case: have any field returned as results.data[0].orderId contain <img src=x onerror=alert(1)>.
  2. Send the victim to: https://<host>/#/track-result?id=<crafted-orderId> (or any URL that causes the back-end to return an order whose orderId contains the payload).
  3. The component executes bypassSecurityTrustHtml('<code><img src=x onerror=alert(1)></code>') and the template renders it via [innerHtml], triggering script execution in the victim's session.
Impact

Cross-site scripting on an authenticated route. An attacker who can influence the orderId value returned by /rest/track-order/<id> (or convince a victim to follow a crafted track-result URL) gains arbitrary JavaScript execution in the victim’s browser, allowing session-token theft from localStorage/cookies, account takeover, CSRF-style actions on behalf of the user, or further client-side exploitation. No authentication required for the attacker to host/share the link; the victim need only follow it.

Validation
confirmed

Line 48 calls this.sanitizer.bypassSecurityTrustHtml(\<code>${results.data[0].orderId}</code>\) and the template binds it via [innerHtml]. The orderId originates from this.route.snapshot.queryParams.id, which the Juice Shop track-order backend echoes back in results.data[0].orderId, so an attacker can craft a URL like /#/track-result?id=<img src=x onerror=alert(1)> to achieve reflected XSS. bypassSecurityTrustHtml explicitly disables Angular's sanitization, and no DOMPurify or equivalent is applied, making the exploit chain reachable from untrusted input.

CVSS 3.1
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N
Base score: 6.1 · MEDIUM

The sink at line 48 (bypassSecurityTrustHtml(<code>${results.data[0].orderId}</code>)) is fed from this.route.snapshot.queryParams.id with no auth gate on reaching the route, so any attacker can craft a ?id= URL and share it (AV:N, PR:N, AC:L). Exploitation requires the victim to click the crafted link, so UI:R. Per CVSS 3.1 guidance, XSS that executes script in the victim's browser typically crosses the security authority from the vulnerable web app component into the user's browser/session, hence Scope:C, with C:L/I:L (attacker can steal session tokens from localStorage/cookies and perform actions as the user, but impact is bounded by what that user can do in the browser) and A:N (no availability impact).

References