@ANGULAR/UPGRADE · GHSA-ffj9-42qx-f38h · Package
FIX → 22.2.4 · 21.2.26 · 20.3.35 · End of Support / LTS expired; will not be patched
In hybrid Angular and AngularJS apps, @angular/upgrade's downgradeComponent() compiles dynamic host element content as an AngularJS template when a co-located directive like ng-bind has written user data into it first. An attacker who controls that data can run arbitrary JavaScript in the victim's browser under the application's origin.
first public Nov 9, 2026 · 1 source
Could this affect your app?
Free first check from your website. Connect your repo for the full answer.
Only hybrid applications that use @angular/upgrade's downgradeComponent() to embed Angular components inside AngularJS templates are exposed. The risk requires a ng-bind, ng-bind-html or ng-transclude directive on a downgraded component's host element and untrusted user data rendered there. Pure Angular apps are not affected.
Not affected if no-downgradecomponent, no-host-directives, pure-angular, static-content-only
Who cares angular, angularjs, hybrid-apps, web-frontend
downgradeComponent usage and for ng-bind, , or on the same element as a downgraded component's host tag.A Cross-Site Scripting (XSS) vulnerability was identified in @angular/upgrade when using downgradeComponent() in hybrid Angular / AngularJS applications.
When an Angular component is downgraded via downgradeComponent() for use inside an AngularJS (1.x) template, DowngradeComponentAdapter.compileContents() prepares the element's child DOM nodes for content projection (<ng-content>) by passing them to the AngularJS $compile() service.
Previously, compileContents() read and compiled the live child nodes of the host element (this.element.contents()) at the time the downgraded Angular component instance was created, rather than capturing the original static template child nodes prior to linking.
Although the directive registered by downgradeComponent() specifies terminal: true, it uses the default AngularJS directive priority (0). Consequently, other priority- AngularJS directives placed on the same host element (such as , , or ) still execute.
Check it with your coding agent
It reads this page and tells you if your code is affected.
Are you affected?
Ask from here. InfraStack checks the affected versions against your stack and answers in your inbox.
InfraStack checks the affected versions against your stack and answers in Slack, with the evidence and the fix.
ng-bind-htmlng-transcludenpm ls @angular/upgrade; versions below 20.3.35, 21.2.26 or 22.2.4 in their branch are affected.@angular/upgrade are not affected.Upgrade to 22.2.4 · 21.2.26 · 20.3.35 · End of Support / LTS expired; will not be patched
@angular/upgrade 22.2.4 (22.x line)@angular/upgrade 21.2.26 (21.x line)@angular/upgrade 20.3.35 (20.x line)19.2.25 and below: no fix, end of support; the sources say it will not be patched.0ng-bindng-bind-htmlng-transcludeWhen a co-located directive writes user-bound data or inserts already-linked transcluded DOM nodes into the downgraded host element before compileContents() runs (such as during asynchronous downgradeModule() bootstrapping or when ngTransclude's post-link executes prior to the downgraded directive's post-link), compileContents() passes that dynamic content to $compile(). Any AngularJS interpolation ({{ ... }}) or directive syntax (including class-based directives like class="ng-bind: ..." that survive $sanitize in ng-bind-html) present in the data is compiled and executed as an AngularJS template expression.
Successful exploitation allows an attacker who controls data rendered within the downgraded element to execute arbitrary JavaScript in the victim user's browser within the context of the application's origin (Cross-Site Scripting).
@angular/upgrade (downgradeComponent()) are not vulnerable.An application is affected if the following conditions are met:
@angular/upgrade (downgradeComponent()) to embed Angular components inside AngularJS templates.ng-bind, ng-bind-html, or ng-transclude) is placed directly on a downgraded component's host element and runs before downgradeComponent() compiles the element's contents.<user-badge ng-bind="$ctrl.user.name"></user-badge>
If $ctrl.user.name contains an AngularJS template expression (e.g., {{constructor.constructor('alert(document.domain)')()}}), the co-located directive writes the value into <user-badge> during linking, and downgradeComponent() subsequently passes the host element's contents to $compile(), evaluating the expression.
The issue is resolved by capturing a clone of the downgraded element's static template child nodes inside the directive's controller phase (prior to any directive preLink or postLink execution) and only passing those static template nodes to $compile(). Any DOM nodes dynamically inserted at link time when the original template was empty are projected directly without invoking $compile().
22.2.421.2.2620.3.35If you cannot immediately upgrade to a patched version, do not place ng-bind, ng-bind-html, or ng-transclude directly on a downgraded component's host element.
<user-badge ng-bind="$ctrl.user.name"></user-badge>), move the binding onto an inner child element (<user-badge><span ng-bind="$ctrl.user.name"></span></user-badge>) or pass the value via an Angular @Input() property binding (<user-badge [name]="$ctrl.user.name"></user-badge>).ng-transclude directly on the downgraded host element (<app-card ng-transclude></app-card>), place ng-transclude on a wrapper child element inside the downgraded component (<app-card><div ng-transclude></div></app-card>).