mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
refactor(compiler): remove unnecessary sanitization for safe attributes
Remove sanitization for attributes that cannot execute code (e.g. `javascript: URIs`).
(cherry picked from commit 128aef0ede)
This commit is contained in:
parent
6d9d2b83f6
commit
b8cb6cda30
12 changed files with 29 additions and 60 deletions
|
|
@ -905,10 +905,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDE
|
|||
export class HostBindingImageDir {
|
||||
constructor() {
|
||||
this.evil = 'evil';
|
||||
this.nonEvil = 'nonEvil';
|
||||
}
|
||||
}
|
||||
HostBindingImageDir.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: HostBindingImageDir, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
||||
HostBindingImageDir.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: HostBindingImageDir, isStandalone: true, selector: "img[hostBindingImgDir]", host: { properties: { "innerHtml": "evil", "attr.style": "evil", "src": "evil" } }, ngImport: i0 });
|
||||
HostBindingImageDir.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: HostBindingImageDir, isStandalone: true, selector: "img[hostBindingImgDir]", host: { properties: { "innerHtml": "evil", "attr.style": "evil", "src": "nonEvil" } }, ngImport: i0 });
|
||||
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: HostBindingImageDir, decorators: [{
|
||||
type: Directive,
|
||||
args: [{
|
||||
|
|
@ -916,7 +917,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDE
|
|||
host: {
|
||||
'[innerHtml]': 'evil',
|
||||
'[attr.style]': 'evil',
|
||||
'[src]': 'evil',
|
||||
'[src]': 'nonEvil',
|
||||
},
|
||||
}]
|
||||
}] });
|
||||
|
|
@ -969,6 +970,7 @@ export declare class HostBindingLinkDir {
|
|||
}
|
||||
export declare class HostBindingImageDir {
|
||||
evil: string;
|
||||
nonEvil: string;
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<HostBindingImageDir, never>;
|
||||
static ɵdir: i0.ɵɵDirectiveDeclaration<HostBindingImageDir, "img[hostBindingImgDir]", never, {}, {}, never, never, true, never>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,14 +7,14 @@ hostBindings: function HostBindingLinkDir_HostBindings(rf, ctx) {
|
|||
…
|
||||
hostBindings: function HostBindingImageDir_HostBindings(rf, ctx) {
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵdomProperty("innerHTML", ctx.evil, $r3$.ɵɵsanitizeHtml)("src", ctx.evil, $r3$.ɵɵsanitizeUrl);
|
||||
$r3$.ɵɵdomProperty("innerHTML", ctx.evil, $r3$.ɵɵsanitizeHtml)("src", ctx.nonEvil);
|
||||
$r3$.ɵɵattribute("style", ctx.evil, $r3$.ɵɵsanitizeStyle);
|
||||
}
|
||||
}
|
||||
…
|
||||
hostBindings: function HostBindingIframeDir_HostBindings(rf, ctx) {
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵdomProperty("innerHTML", ctx.evil, $r3$.ɵɵsanitizeHtml)("src", ctx.evil, $r3$.ɵɵsanitizeResourceUrl)("sandbox", ctx.evil, $r3$.ɵɵvalidateAttribute);
|
||||
$r3$.ɵɵdomProperty("innerHTML", ctx.evil, $r3$.ɵɵsanitizeHtml)("src", ctx.evil, i0.ɵɵsanitizeResourceUrl)("sandbox", ctx.evil, $r3$.ɵɵvalidateAttribute);
|
||||
$r3$.ɵɵattribute("style", ctx.evil, $r3$.ɵɵsanitizeStyle)("attributeName", ctx.nonEvil);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,11 +17,12 @@ export class HostBindingLinkDir {
|
|||
host: {
|
||||
'[innerHtml]': 'evil',
|
||||
'[attr.style]': 'evil',
|
||||
'[src]': 'evil',
|
||||
'[src]': 'nonEvil',
|
||||
},
|
||||
})
|
||||
export class HostBindingImageDir {
|
||||
evil = 'evil';
|
||||
nonEvil = 'nonEvil';
|
||||
}
|
||||
|
||||
@Directive({
|
||||
|
|
|
|||
|
|
@ -827,6 +827,7 @@ import * as i0 from "@angular/core";
|
|||
export class MyComponent {
|
||||
constructor() {
|
||||
this.evil = 'evil';
|
||||
this.nonEvil = 'nonEvil';
|
||||
}
|
||||
}
|
||||
MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
||||
|
|
@ -834,7 +835,7 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "
|
|||
<div [innerHtml]="evil"></div>
|
||||
<link [href]="evil" />
|
||||
<div [attr.style]="evil"></div>
|
||||
<img [src]="evil" />
|
||||
<img [src]="nonEvil" />
|
||||
<iframe [sandbox]="evil"></iframe>
|
||||
<a href="{{evil}}{{evil}}"></a>
|
||||
<div attr.style="{{evil}}{{evil}}"></div>
|
||||
|
|
@ -847,7 +848,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDE
|
|||
<div [innerHtml]="evil"></div>
|
||||
<link [href]="evil" />
|
||||
<div [attr.style]="evil"></div>
|
||||
<img [src]="evil" />
|
||||
<img [src]="nonEvil" />
|
||||
<iframe [sandbox]="evil"></iframe>
|
||||
<a href="{{evil}}{{evil}}"></a>
|
||||
<div attr.style="{{evil}}{{evil}}"></div>
|
||||
|
|
@ -861,6 +862,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDE
|
|||
import * as i0 from "@angular/core";
|
||||
export declare class MyComponent {
|
||||
evil: string;
|
||||
nonEvil: string;
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<MyComponent, never>;
|
||||
static ɵcmp: i0.ɵɵComponentDeclaration<MyComponent, "my-component", never, {}, {}, never, never, true, never>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ template: function MyComponent_Template(rf, ctx) {
|
|||
$r3$.ɵɵadvance();
|
||||
$r3$.ɵɵattribute("style", ctx.evil, $r3$.ɵɵsanitizeStyle);
|
||||
$r3$.ɵɵadvance();
|
||||
$r3$.ɵɵdomProperty("src", ctx.evil, $r3$.ɵɵsanitizeUrl);
|
||||
$r3$.ɵɵdomProperty("src", ctx.nonEvil);
|
||||
$r3$.ɵɵadvance();
|
||||
$r3$.ɵɵdomProperty("sandbox", ctx.evil, $r3$.ɵɵvalidateAttribute);
|
||||
$r3$.ɵɵadvance();
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import {Component} from '@angular/core';
|
|||
<div [innerHtml]="evil"></div>
|
||||
<link [href]="evil" />
|
||||
<div [attr.style]="evil"></div>
|
||||
<img [src]="evil" />
|
||||
<img [src]="nonEvil" />
|
||||
<iframe [sandbox]="evil"></iframe>
|
||||
<a href="{{evil}}{{evil}}"></a>
|
||||
<div attr.style="{{evil}}{{evil}}"></div>
|
||||
|
|
@ -14,4 +14,5 @@ import {Component} from '@angular/core';
|
|||
})
|
||||
export class MyComponent {
|
||||
evil = 'evil';
|
||||
nonEvil = 'nonEvil';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8497,7 +8497,7 @@ runInEachFileSystem((os: string) => {
|
|||
hostVars: 6,
|
||||
hostBindings: function UnsafeAttrsDirective_HostBindings(rf, ctx) {
|
||||
if (rf & 2) {
|
||||
i0.ɵɵattribute("href", ctx.attrHref, i0.ɵɵsanitizeUrlOrResourceUrl)("src", ctx.attrSrc, i0.ɵɵsanitizeUrlOrResourceUrl)("action", ctx.attrAction, i0.ɵɵsanitizeUrl)("profile", ctx.attrProfile, i0.ɵɵsanitizeResourceUrl)("innerHTML", ctx.attrInnerHTML, i0.ɵɵsanitizeHtml)("title", ctx.attrSafeTitle);
|
||||
i0.ɵɵattribute("href", ctx.attrHref, i0.ɵɵsanitizeUrlOrResourceUrl)("src", ctx.attrSrc, i0.ɵɵsanitizeResourceUrl)("action", ctx.attrAction, i0.ɵɵsanitizeUrl)("profile", ctx.attrProfile)("innerHTML", ctx.attrInnerHTML, i0.ɵɵsanitizeHtml)("title", ctx.attrSafeTitle);
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -32,23 +32,9 @@ export function SECURITY_SCHEMA(): {[k: string]: SecurityContext} {
|
|||
registerContext(SecurityContext.URL, [
|
||||
'*|formAction',
|
||||
'area|href',
|
||||
'area|ping',
|
||||
'audio|src',
|
||||
'a|href',
|
||||
'a|xlink:href',
|
||||
'a|ping',
|
||||
'blockquote|cite',
|
||||
'body|background',
|
||||
'del|cite',
|
||||
'form|action',
|
||||
'img|src',
|
||||
'input|src',
|
||||
'ins|cite',
|
||||
'q|cite',
|
||||
'source|src',
|
||||
'track|src',
|
||||
'video|poster',
|
||||
'video|src',
|
||||
|
||||
// MathML namespace
|
||||
// https://crsrc.org/c/third_party/blink/renderer/core/sanitizer/sanitizer.cc;l=753-768;drc=b3eb16372dcd3317d65e9e0265015e322494edcd;bpv=1;bpt=1
|
||||
|
|
@ -121,16 +107,11 @@ export function SECURITY_SCHEMA(): {[k: string]: SecurityContext} {
|
|||
]);
|
||||
|
||||
registerContext(SecurityContext.RESOURCE_URL, [
|
||||
'applet|code',
|
||||
'applet|codebase',
|
||||
'base|href',
|
||||
'embed|src',
|
||||
'frame|src',
|
||||
'head|profile',
|
||||
'html|manifest',
|
||||
'iframe|src',
|
||||
'link|href',
|
||||
'media|src',
|
||||
'object|codebase',
|
||||
'object|data',
|
||||
'script|src',
|
||||
|
|
|
|||
|
|
@ -156,7 +156,6 @@ If 'onAnything' is a directive input, make sure the directive is imported by the
|
|||
expect(registry.securityContext('p', 'innerHTML', false)).toBe(SecurityContext.HTML);
|
||||
expect(registry.securityContext('a', 'href', false)).toBe(SecurityContext.URL);
|
||||
expect(registry.securityContext('a', 'style', false)).toBe(SecurityContext.STYLE);
|
||||
expect(registry.securityContext('ins', 'cite', false)).toBe(SecurityContext.URL);
|
||||
expect(registry.securityContext('base', 'href', false)).toBe(SecurityContext.RESOURCE_URL);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1607,23 +1607,6 @@ describe('host bindings', () => {
|
|||
true,
|
||||
true,
|
||||
);
|
||||
verify(
|
||||
'blockquote',
|
||||
'cite',
|
||||
'javascript:alert(2)',
|
||||
'unsafe:javascript:alert(2)',
|
||||
bypassSanitizationTrustUrl,
|
||||
);
|
||||
verify('blockquote', 'cite', 'javascript:alert(2.1)', 'unsafe:javascript:alert(2.1)', identity);
|
||||
verify(
|
||||
'blockquote',
|
||||
'cite',
|
||||
'javascript:alert(2.2)',
|
||||
'unsafe:javascript:alert(2.2)',
|
||||
bypassSanitizationTrustHtml,
|
||||
true,
|
||||
true,
|
||||
);
|
||||
verify(
|
||||
'b',
|
||||
'innerHTML',
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ describe('property interpolation', () => {
|
|||
it('should not allow unsanitary urls in interpolated properties', () => {
|
||||
@Component({
|
||||
template: `
|
||||
<img src="{{naughty}}">
|
||||
<a href="{{naughty}}">text</a>
|
||||
`,
|
||||
standalone: false,
|
||||
})
|
||||
|
|
@ -127,15 +127,15 @@ describe('property interpolation', () => {
|
|||
TestBed.configureTestingModule({declarations: [App]});
|
||||
const fixture = TestBed.createComponent(App);
|
||||
fixture.detectChanges();
|
||||
const img: HTMLImageElement = fixture.nativeElement.querySelector('img');
|
||||
const a = fixture.nativeElement.querySelector('a');
|
||||
|
||||
expect(img.src.indexOf('unsafe:')).toBe(0);
|
||||
expect(a.href.indexOf('unsafe:')).toBe(0);
|
||||
});
|
||||
|
||||
it('should not allow unsanitary urls in interpolated properties, even if you are tricky', () => {
|
||||
@Component({
|
||||
template: `
|
||||
<img src="{{ja}}{{va}}script:{{naughty}}">
|
||||
<a href="{{ja}}{{va}}script:{{naughty}}">text</a>
|
||||
`,
|
||||
standalone: false,
|
||||
})
|
||||
|
|
@ -148,9 +148,9 @@ describe('property interpolation', () => {
|
|||
TestBed.configureTestingModule({declarations: [App]});
|
||||
const fixture = TestBed.createComponent(App);
|
||||
fixture.detectChanges();
|
||||
const img = fixture.nativeElement.querySelector('img');
|
||||
const a = fixture.nativeElement.querySelector('a');
|
||||
|
||||
expect(img.src.indexOf('unsafe:')).toBe(0);
|
||||
expect(a.href.indexOf('unsafe:')).toBe(0);
|
||||
});
|
||||
|
||||
it('should handle interpolations with 10+ values', () => {
|
||||
|
|
|
|||
|
|
@ -612,7 +612,7 @@ describe('sanitization', () => {
|
|||
selector: '[unsafeUrlHostBindingDir]',
|
||||
})
|
||||
class UnsafeUrlHostBindingDir {
|
||||
@HostBinding() cite: any = 'http://cite-dir-value';
|
||||
@HostBinding() href: any = 'http://href-dir-value';
|
||||
|
||||
constructor() {
|
||||
hostBindingDir = this;
|
||||
|
|
@ -623,7 +623,7 @@ describe('sanitization', () => {
|
|||
selector: 'sanitize-this',
|
||||
imports: [UnsafeUrlHostBindingDir],
|
||||
template: `
|
||||
<blockquote unsafeUrlHostBindingDir></blockquote>
|
||||
<a unsafeUrlHostBindingDir>text</a>
|
||||
`,
|
||||
})
|
||||
class SimpleComp {}
|
||||
|
|
@ -640,16 +640,16 @@ describe('sanitization', () => {
|
|||
],
|
||||
});
|
||||
const fixture = TestBed.createComponent(SimpleComp);
|
||||
hostBindingDir!.cite = 'http://foo';
|
||||
hostBindingDir!.href = 'http://foo';
|
||||
fixture.detectChanges();
|
||||
|
||||
const anchor = fixture.nativeElement.querySelector('blockquote')!;
|
||||
expect(anchor.getAttribute('cite')).toEqual('http://bar');
|
||||
const anchor = fixture.nativeElement.querySelector('a')!;
|
||||
expect(anchor.getAttribute('href')).toEqual('http://bar');
|
||||
|
||||
hostBindingDir!.cite = sanitizer.bypassSecurityTrustUrl('http://foo');
|
||||
hostBindingDir!.href = sanitizer.bypassSecurityTrustUrl('http://foo');
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(anchor.getAttribute('cite')).toEqual('http://foo');
|
||||
expect(anchor.getAttribute('href')).toEqual('http://foo');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue