Authorization disclaimer. This walkthrough is exclusively for the intentionally vulnerable PortSwigger Web Security Academy lab linked below. Only test systems you own or are explicitly authorized to assess.
Overview
The lab reflects the search query inside an AngularJS-controlled region. Although angle brackets and double quotes are HTML-encoded, the browser-side framework still treats {{...}} as an expression. The vulnerable interpretation context is therefore an AngularJS template rather than ordinary HTML.
Lab objective/setup
The objective is to trigger alert(1) through GET /?search=<value>. The source identifies a surrounding ng-app directive, which causes AngularJS to process expressions in that part of the page.
What I observed
I first submitted the harmless expression {{1+1}} in the search field.

{{1+1}} to test whether reflected text is compiled as a template.The results heading displayed 2, not the literal expression. That behavior confirms client-side template evaluation and provides stronger evidence than reflection alone.

Why it is vulnerable
Encoding <, >, and quotes can prevent input from becoming new HTML markup or breaking out of an HTML attribute. It does not neutralize AngularJS interpolation syntax. Because untrusted text reaches a live template under ng-app, AngularJS evaluates the expression after the HTML parser has handled the encoded characters.
This is a client-side template injection condition that becomes DOM XSS when the expression reaches executable JavaScript behavior.
Walkthrough in the authorized PortSwigger lab
- Open the lab and locate the search input.
- Submit
{{1+1}}and verify that the page renders2. - Replace the arithmetic probe with the lab payload
{{$on.constructor('alert(1)')()}}. - Submit the search and observe the JavaScript alert.
In the payload, $on is an object available in the AngularJS scope. Accessing its constructor reaches the Function constructor; invoking it with alert(1) creates a function, and the final () executes that function. No injected <script> element is required.

Verification
The arithmetic probe establishes expression evaluation, while the alert establishes executable impact. Together they distinguish the issue from simple reflected text. The PortSwigger lab’s solved state is the final platform-level confirmation.
Impact and defensive lesson
Do not place untrusted data into framework templates that AngularJS will compile or evaluate. Keep user-controlled values outside template source, bind them as data using safe framework mechanisms, and avoid dynamically compiling attacker-influenced markup. Context-aware HTML encoding remains necessary for HTML sinks, but it cannot fix a different sink whose interpreter is AngularJS.
Retiring unsupported AngularJS applications and enforcing a restrictive Content Security Policy can reduce exposure, but neither replaces removing untrusted input from executable template contexts.
References
- PortSwigger Web Security Academy: DOM XSS in an AngularJS expression
- PortSwigger research: XSS without HTML—client-side template injection with AngularJS
Provenance
This article was rewritten in English from the local migration source named in the front matter. The three figures are controlled copies of that source’s screenshots; no additional runtime observations were introduced.