CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Dashboard
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-22610
8.50.02%

The SVG Paradox: How Angular Forgot About 'xlink:href'

Amit Schendel
Amit Schendel
Senior Security Researcher

Feb 22, 2026·6 min read·27 visits

PoC Available

Executive Summary (TL;DR)

Angular's internal security schema failed to recognize that SVG `<script>` tags can execute code via `href` attributes, treating them as harmless links. This allows XSS via data bindings in SVG templates. Patch immediately to versions 19.2.18, 20.3.16, 21.0.7, or 21.1.0-rc.0.

A high-severity Cross-Site Scripting (XSS) vulnerability in the Angular Template Compiler allows attackers to bypass Angular's strict sanitization mechanisms. By leveraging SVG-specific attributes like `href` and `xlink:href` on `<script>` elements, attackers can inject malicious executable code that the framework mistakenly categorizes as safe URLs. This affects major versions of Angular Core, including v19, v20, and v21.

The Hook: When Paranoid Frameworks Blink

Angular is famous—or infamous, depending on who you ask—for being paranoid. It is the helicopter parent of JavaScript frameworks. It holds your hand, checks your inputs, and aggressively sanitizes anything that looks even remotely like a script tag. If you try to bind a string containing <script>alert(1)</script> to [innerHTML], Angular silently nukes it from orbit. This is why we rarely see XSS in Angular applications compared to the Wild West of raw React or jQuery.

But even the most vigilant guards have blind spots. In this case, the blind spot wasn't in the HTML specification, but in the quirky, legacy-ridden world of Scalable Vector Graphics (SVG). While Angular was busy meticulously guarding the front door (<script src="...">), it left the window (<svg><script href="...">) unlatched.

CVE-2026-22610 is a failure of categorization. It’s not that Angular couldn't sanitize the input; it’s that it didn't think it needed to. The framework looked at an SVG script tag with an href attribute, shrugged, and said, "That looks like a hyperlink to me." That assumption turned a benign property binding into a high-severity Remote Code Execution vector within the victim's browser context.

The Flaw: A Case of Mistaken Identity

To understand this bug, you have to understand Angular's DOM Security Schema. Angular classifies every DOM property into security contexts: HTML, STYLE, URL, and the most dangerous of all, RESOURCE_URL.

A URL context is for things like <a href="...">. You can put a link to https://google.com, but Angular will strip out javascript:alert(1).

A RESOURCE_URL context is for things that load executable code, like <script src="..."> or <iframe src="...">. Angular locks this down tight. You cannot bind a dynamic value to a RESOURCE_URL unless you explicitly tell Angular, "I trust this value with my life," using bypassSecurityTrustResourceUrl.

Here lies the logic error. Angular correctly identified that <script src="..."> requires RESOURCE_URL security. However, in the world of SVG, you don't always use src. Historically, you used xlink:href, and in modern SVG 2 specs, you just use href.

Angular's internal schema map missed this nuance. When the compiler encountered <script [attr.href]="..."> inside an SVG, it checked its list of "dangerous" attributes. Seeing href but failing to associate it with the script tag in an executable context, it downgraded the security requirement from RESOURCE_URL to plain old URL. This downgrade is fatal. It means the sanitizer allows data: URIs, which are perfectly valid for images or links but devastating when fed to a script tag.

The Code: The Smoking Gun

Let's look at the actual code in packages/core/src/sanitization/sanitization.ts. This is where the decision to sanitize—or not to sanitize—is made.

Before the Patch:

The function getUrlSanitizer determines which sanitizer function to use based on the tag and property name. Notice the omission in the HREF_RESOURCE_TAGS set.

// The set of tags where 'href' is considered a loadable resource
const HREF_RESOURCE_TAGS = new Set(['base', 'link']); 
 
export function getUrlSanitizer(tag: string, prop: string) {
  // If it's a 'src' attribute on a known tag, sanitize as Resource URL
  if (prop === 'src' && SRC_RESOURCE_TAGS.has(tag)) {
    return ɵɵsanitizeResourceUrl;
  }
  // If it's an 'href' attribute on 'base' or 'link', sanitize as Resource URL
  if (prop === 'href' && HREF_RESOURCE_TAGS.has(tag)) {
    return ɵɵsanitizeResourceUrl;
  }
  // Fallback: Just a regular URL (allows data: URIs!)
  return ɵɵsanitizeUrl;
}

Because script was missing from HREF_RESOURCE_TAGS, the function returned ɵɵsanitizeUrl. This sanitizer allows data: protocols because they are safe for <img> or <a> tags.

After the Patch (Commit 91dc91bae4):

The Angular team realized that script tags also use href (and xlink:href) to load code.

// 'script' is added to the naughty list
const HREF_RESOURCE_TAGS = new Set(['base', 'link', 'script']);
 
export function getUrlSanitizer(tag: string, prop: string) {
  const isResource =
    (prop === 'src' && SRC_RESOURCE_TAGS.has(tag)) ||
    (prop === 'href' && HREF_RESOURCE_TAGS.has(tag)) ||
    // Explicit check for the legacy SVG attribute
    (prop === 'xlink:href' && tag === 'script'); 
 
  return isResource ? ɵɵsanitizeResourceUrl : ɵɵsanitizeUrl;
}

By forcing ɵɵsanitizeResourceUrl, Angular now throws an error if a developer tries to bind a string to these attributes without explicit trust, effectively killing the XSS vector.

The Exploit: Weaponizing SVG

Exploiting this requires a specific but not uncommon scenario: an Angular application that renders SVGs using templates where attributes are bound to user-controllable data.

Imagine a dashboard application that allows users to customize their profile widget with an "Avatar" SVG. The application code might look like this:

<!-- component.html -->
<svg width="100" height="100">
  <circle cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red" />
  <!-- The developer thinks they are binding a link to a profile script -->
  <script [attr.href]="userProfileUrl"></script>
</svg>

The developer assumes userProfileUrl is just a URL. Angular cleans URLs, right?

The Attack Chain:

  1. Injection: The attacker updates their profile settings. In the userProfileUrl field, instead of https://example.com/profile.js, they input a Data URI payload: data:text/javascript,alert(document.cookie)

  2. Rendering: The Angular compiler sees [attr.href] on a script tag. It checks its schema. It thinks, "This is a safe URL context." It validates the protocol. data: is allowed in safe URL contexts (e.g., <img src="data:...">).

  3. Execution: Angular renders the DOM:

    <script href="data:text/javascript,alert(document.cookie)"></script>

    The browser's SVG engine parses this, sees a script element with an executable href, and immediately executes the JavaScript inside the Data URI.

Boom. You have XSS. No complex bypasses, no polyglots. Just a simple logic gap in the framework.

The Impact: Why This Matters

This vulnerability is particularly dangerous because it subverts the expectations of developers. Angular developers are trained to believe that the framework handles context-aware escaping automatically. They rely on the compiler to yell at them if they do something dangerous.

With a CVSS score of 8.5, the impact is high. Successful exploitation allows for:

  • Session Hijacking: Stealing JWTs or session cookies.
  • CSRF Bypass: Performing actions on behalf of the user without their consent.
  • Data Exfiltration: Reading sensitive data rendered on the page.

Furthermore, because this is a client-side template injection issue, it can often bypass server-side WAFs that aren't looking for standard XSS payloads but might miss a data:text/javascript string hidden inside a JSON API response.

The Fix: Closing the Window

The mitigation is straightforward: upgrade. The Angular team has backported fixes to all supported active versions.

Patched Versions:

  • Angular 19: 19.2.18
  • Angular 20: 20.3.16
  • Angular 21: 21.0.7

If you are stuck on an unpatched version (and shame on you, it's 2026), you have two temporary options:

  1. Content Security Policy (CSP): This is your best safety net. A strict CSP that disallows data: in the script-src directive will prevent the browser from executing the payload, even if Angular renders it.
    Content-Security-Policy: script-src 'self'; object-src 'none';
  2. Manual Audit: Grep your codebase for [attr.href] and [attr.xlink:href] inside <svg> blocks. If you find them on <script> tags, remove them or hardcode the values.

Official Patches

AngularOfficial fix commit on GitHub
AngularGitHub Security Advisory GHSA-jrmj-c5cx-3cw6

Fix Analysis (1)

Technical Appendix

CVSS Score
8.5/ 10
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:A/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
EPSS Probability
0.02%
Top 95% most exploited

Affected Systems

Angular Framework (@angular/core)

Affected Versions Detail

Product
Affected Versions
Fixed Version
@angular/core
Angular
< 19.2.1819.2.18
@angular/core
Angular
>= 20.0.0-next.0, < 20.3.1620.3.16
@angular/core
Angular
>= 21.0.0-next.0, < 21.0.721.0.7
@angular/core
Angular
>= 21.1.0-next.0, < 21.1.0-rc.021.1.0-rc.0
AttributeDetail
CWE IDCWE-79 (Improper Neutralization of Input During Web Page Generation)
CVSS v4.08.5 (High)
Attack VectorNetwork
Attack ComplexityLow
Privileges RequiredLow
ImpactHigh (Confidentiality, Integrity, Availability)
Exploit StatusPoC Available

MITRE ATT&CK Mapping

T1190Exploit Public-Facing Application
Initial Access
T1059.007Command and Scripting Interpreter: JavaScript
Execution
CWE-79
Cross-site Scripting

The software does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.

Known Exploits & Detection

GitHubVulnerability scanner and PoC for Angular SVG XSS

Vulnerability Timeline

Fix committed to Angular repository
2026-01-06
Advisory published (GHSA-jrmj-c5cx-3cw6)
2026-01-10
NVD analysis completed
2026-01-13
Public technical analysis circulating
2026-01-28

References & Sources

  • [1]Angular Security Advisory
  • [2]CWE-79: Cross-site Scripting

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.