Repository navigation
Conversation
| selector: 'ng-icon', | ||
| template: ` | ||
| <svg class="svg"> | ||
| <use [attr.xlink:href]="'/assets/sprite.svg#' + name()"></use> |
There was a problem hiding this comment.
I was thinking about allowing #[whatever_here_icon], but I think it's better to keep it consistent with iframe[src].
Since Angular selects the sanitizer based on the element and attribute, not the URL value.
Even <iframe [src]="'#foo'"> throws today, so doing an exception just for <use> doesn't seem worth it
Also, # isn't completely safe because cloned SVG content can still have javascript: links that execute on click.
There was a problem hiding this comment.
This change is quite breaking for existing Angular applications because dynamic <use> bindings are heavily used for SVG icon components (as seen right here in DevTools).
For # fragment references specifically, a #fragment only clones an element that is already in the current document's DOM and never fetches an external resource. If an attacker were able to inject an unsanitized <a href="javascript:..."> into the current document, XSS would already be present without <use>.
Since setElementAttribute and elementPropertyInternal already pass (value, tagName, propName) to SanitizerFn (similar to ɵɵsanitizeHtml), ɵɵsanitizeResourceUrl (and ɵɵsanitizeUrlOrResourceUrl) can allow #... fragment strings when the target is <svg:use> without needing any compiler changes. Because <use [attr.href]="'#' + icon"> is so common for inline SVG sprites, we should allow #... references to reduce the breaking impact rather than forcing developers to wrap them in bypassSecurityTrustResourceUrl.
alan-agius4
left a comment
There was a problem hiding this comment.
This change is quite breaking in practice because dynamic <svg><use> bindings are widely used across Angular applications and UI libraries for SVG icons (as demonstrated by Angular DevTools breaking in this PR). To minimize unnecessary breakage and prepare this properly:
-
Allow same document fragment references (
#...) on<use>:
Binding dynamic fragment identifiers (e.g.<use [attr.href]="'#' + icon">) is a very common pattern for inline SVG sprites. Unlike document URLs, a#fragmentreference never fetches an external resource and only targets an element already present in the current DOM. SinceSanitizerFnalready receives(value, tagName, propName)at runtime (likeɵɵsanitizeHtml),ɵɵsanitizeResourceUrlandɵɵsanitizeUrlOrResourceUrlcan allow#...values on<use>. This also keeps the existing#idtest inpackages/core/test/linker/integration_spec.tspassing without changing it toxlink:title. -
Expand test coverage in
packages/core/test/acceptance/security_spec.ts:
Please also add tests covering[attr.xlink:href]rejecting untrusted document URLs, allowing values wrapped withbypassSecurityTrustResourceUrl, and allowing#fragmentvalues. -
Add a
BREAKING CHANGE:footer to the commit message:
Since dynamic<use>bindings that reference an external or same origin document path (like[attr.xlink:href]="'/assets/sprite.svg#' + name()") will now throwNG0904unless wrapped inbypassSecurityTrustResourceUrl, this is a significant breaking change. Please include aBREAKING CHANGE:note in the commit message body so this can be targeted for a major release.
2311458 to
1274311
Compare
1274311 to
7b94914
Compare
|
I'm wondering if this will only land in v23 or if it could be backported, considering that v23 is planned for next year, so LTS v20 , 21 and 22 would never get it. I think it can be an XSS vector for some users on the LTS versions, since both routes from #71618 affect browsers that are inside the baseline for those versions. Route A only needs a This reminds me a bit of GHSA-v4hv-rgfq-gp49, where |
|
|
||
| // `use` references are resource URLs. The sprite is an asset of DevTools itself and only the | ||
| // icon name varies, so the reference is marked as trusted here. | ||
| protected readonly href = computed(() => |
There was a problem hiding this comment.
Do we still need the bypass here ?
There was a problem hiding this comment.
Because we are using /assets/sprite.svg#${this.name()} which points to an assets URL and according to the above comment:
Binding dynamic fragment identifiers (e.g. <use [attr.href]="'#' + icon">) is a very common pattern for inline SVG sprites. Unlike document URLs, a #fragment reference never fetches an external resource and only targets an element already present in the current DOM.
The only exception will be when it starts with #
`<use>` clones the referenced document into the page, but both attributes had no security context, so a bound value was written as is. They are now `RESOURCE_URL`, like `iframe[src]`. A reference that cannot pull in a document is still allowed: a nullish value, which removes the attribute, and a same document fragment like `#icon`, which is how inline SVG sprites are referenced. Any other dynamic value throws NG0904 unless it is marked with `bypassSecurityTrustResourceUrl`. Static values like `href="https://github.com/assets/sprite.svg#icon"` are unaffected. Fixes angular#71618 BREAKING CHANGE: `href` and `xlink:href` bindings on `<svg:use>` are now sanitized as resource URLs. A binding that references another document, such as `[attr.xlink:href]="'/assets/sprite.svg#' + name()"`, throws NG0904 unless the value is marked with `DomSanitizer.bypassSecurityTrustResourceUrl`. Fragment references like `[attr.href]="'#' + icon"` and static attributes are unaffected.
`<use>` hrefs are now sanitized as resource URLs, so the icon marks its constant sprite path with `bypassSecurityTrustResourceUrl`.
7b94914 to
864bb95
Compare
<use>clones the referenced document into the page, but both attributes had no security context, so a bound value was written as is. They are nowRESOURCE_URL, likeiframe[src].Dynamic
<use>bindings,nullincluded, now throw NG0904 unless the value is marked withbypassSecurityTrustResourceUrl. Static values likehref="#icon"are unaffected.Fixes #71618