agentggagentgg
Back to all findings
MEDIUMconfirmedxssxss3b3afbf2762e

DOM XSS via search query parameter rendered with [innerHTML] after bypassSecurityTrustHtml

The URL query parameter `q` is wrapped with `DomSanitizer.bypassSecurityTrustHtml` and bound to `[innerHTML]`, allowing arbitrary HTML/script execution from a crafted link.

Filefrontend/src/app/search-result/search-result.component.html
Lines1111
Confidence
95%
File statusvalidated
Details

In search-result.component.html line 11, the template renders:

<span id="searchValue" [innerHTML]="searchValue"></span>

The searchValue field is populated in search-result.component.ts (filterTable(), lines 132–155) directly from the q query parameter:

let queryParam: string = this.route.snapshot.queryParams.q
if (queryParam) {
  queryParam = queryParam.trim()
  ...
  this.searchValue = this.sanitizer.bypassSecurityTrustHtml(queryParam)
}

Because the value is explicitly marked SafeHtml via bypassSecurityTrustHtml, Angular's default HTML sanitization is disabled and [innerHTML] injects the raw attacker-controlled string into the DOM. This is the classic Juice Shop localXssChallenge sink — any HTML/JS payload supplied in ?q= is executed in the victim's browser.

Proof of concept

Visit https://<host>/#/search?q=<iframe%20src="javascript:alert(xss)"> (or similar payload). The iframe element is written verbatim into the #searchValue span and the script executes in the victim's session context.

Impact

Reflected/DOM XSS: an attacker can craft a link that, when followed by an authenticated user, executes arbitrary JavaScript with the user's privileges (steal JWT/localStorage tokens, perform actions as the user, deface the page). No authentication required to trigger; only victim navigation.

Validation
confirmed

The template at line 11 binds [innerHTML]="searchValue" where searchValue is wrapped by DomSanitizer.bypassSecurityTrustHtml(queryParam) using the raw q query parameter. Bypassing Angular's sanitizer and injecting attacker-controlled HTML into innerHTML is a textbook DOM XSS sink, exploitable by any victim clicking a crafted #/search?q=<payload> link. Per scope rules, Juice Shop's training nature is irrelevant — this is a real reflected/DOM XSS bug.

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 q query parameter is passed through DomSanitizer.bypassSecurityTrustHtml and bound to [innerHTML] on the #searchValue span in search-result.component.html line 11, so any crafted URL like /#/search?q=<iframe src="javascript:..."> executes attacker JS in the victim's origin — reachable remotely (AV:N), with no preconditions (AC:L), without authentication (PR:N), but requiring the victim to follow the link (UI:R). Scope is Changed because the XSS executes script in the browser security context of the visiting user, crossing from the web-app component into the user's browser/session authority (token theft from localStorage, actions as the victim). C and I are Low because the attacker gains access to whatever the victim's session exposes (JWT, page DOM, ability to perform user actions) rather than full system data/integrity; availability impact within the app is negligible (A:N).

References