mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
fix(core): sanitize translated attribute bindings with interpolations
Fixes that we weren't sanitizing attribute bindings with interpolations if they're marked for translation, for example: `<a href="{{evilLink}}" i18n-href></a>`.
Also adds a bit more test coverage for our sanitization.
This commit is contained in:
committed by
Matthew Beck (Berry)
parent
a55475992b
commit
b89b0a83a4
@@ -388,7 +388,7 @@ export function i18nAttributesFirstPass(tView: TView, index: number, values: str
|
|||||||
previousElementIndex,
|
previousElementIndex,
|
||||||
attrName,
|
attrName,
|
||||||
countBindings(updateOpCodes),
|
countBindings(updateOpCodes),
|
||||||
null,
|
URI_ATTRS[attrName.toLowerCase()] ? _sanitizeUrl : null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -810,18 +810,14 @@ function walkIcuTree(
|
|||||||
const hasBinding = !!attr.value.match(BINDING_REGEXP);
|
const hasBinding = !!attr.value.match(BINDING_REGEXP);
|
||||||
if (hasBinding) {
|
if (hasBinding) {
|
||||||
if (VALID_ATTRS.hasOwnProperty(lowerAttrName)) {
|
if (VALID_ATTRS.hasOwnProperty(lowerAttrName)) {
|
||||||
if (URI_ATTRS[lowerAttrName]) {
|
generateBindingUpdateOpCodes(
|
||||||
generateBindingUpdateOpCodes(
|
update,
|
||||||
update,
|
attr.value,
|
||||||
attr.value,
|
newIndex,
|
||||||
newIndex,
|
attr.name,
|
||||||
attr.name,
|
0,
|
||||||
0,
|
URI_ATTRS[lowerAttrName] ? _sanitizeUrl : null,
|
||||||
_sanitizeUrl,
|
);
|
||||||
);
|
|
||||||
} else {
|
|
||||||
generateBindingUpdateOpCodes(update, attr.value, newIndex, attr.name, 0, null);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
ngDevMode &&
|
ngDevMode &&
|
||||||
console.warn(
|
console.warn(
|
||||||
|
|||||||
@@ -3605,6 +3605,71 @@ describe('runtime i18n', () => {
|
|||||||
'translatedText value',
|
'translatedText value',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('attribute sanitization', () => {
|
||||||
|
@Component({template: ''})
|
||||||
|
class SanitizeAppComp {
|
||||||
|
url = 'javascript:alert("oh no")';
|
||||||
|
count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should sanitize translated attribute binding', () => {
|
||||||
|
const fixture = initWithTemplate(SanitizeAppComp, '<a [attr.href]="url" i18n-href></a>');
|
||||||
|
const link: HTMLAnchorElement = fixture.nativeElement.querySelector('a');
|
||||||
|
expect(link.getAttribute('href')).toMatch(/^unsafe:/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should sanitize translated property binding', () => {
|
||||||
|
const fixture = initWithTemplate(SanitizeAppComp, '<a [href]="url" i18n-href></a>');
|
||||||
|
const link: HTMLAnchorElement = fixture.nativeElement.querySelector('a');
|
||||||
|
expect(link.getAttribute('href')).toMatch(/^unsafe:/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should sanitize translated interpolation', () => {
|
||||||
|
const fixture = initWithTemplate(SanitizeAppComp, '<a href="{{url}}" i18n-href></a>');
|
||||||
|
const link: HTMLAnchorElement = fixture.nativeElement.querySelector('a');
|
||||||
|
expect(link.getAttribute('href')).toMatch(/^unsafe:/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should sanitize interpolation inside translated element', () => {
|
||||||
|
const fixture = initWithTemplate(SanitizeAppComp, `<div i18n><a href="{{url}}"></a></div>`);
|
||||||
|
const link: HTMLAnchorElement = fixture.nativeElement.querySelector('a');
|
||||||
|
expect(link.getAttribute('href')).toMatch(/^unsafe:/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should sanitize attribute binding inside translated element', () => {
|
||||||
|
const fixture = initWithTemplate(
|
||||||
|
SanitizeAppComp,
|
||||||
|
`<div i18n><a [attr.href]="url"></a></div>`,
|
||||||
|
);
|
||||||
|
const link: HTMLAnchorElement = fixture.nativeElement.querySelector('a');
|
||||||
|
expect(link.getAttribute('href')).toMatch(/^unsafe:/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should sanitize property binding inside translated element', () => {
|
||||||
|
const fixture = initWithTemplate(SanitizeAppComp, `<div i18n><a [href]="url"></a></div>`);
|
||||||
|
const link: HTMLAnchorElement = fixture.nativeElement.querySelector('a');
|
||||||
|
expect(link.getAttribute('href')).toMatch(/^unsafe:/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should sanitize property binding inside an ICU', () => {
|
||||||
|
const fixture = initWithTemplate(
|
||||||
|
SanitizeAppComp,
|
||||||
|
`<div i18n>{count, plural,
|
||||||
|
=0 {no <strong>link</strong> yet}
|
||||||
|
other {{{count}} Here is the <a href="{{url}}">link</a>!}
|
||||||
|
}</div>`,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(fixture.nativeElement.querySelector('a')).toBeFalsy();
|
||||||
|
|
||||||
|
fixture.componentInstance.count = 1;
|
||||||
|
fixture.detectChanges();
|
||||||
|
const link: HTMLAnchorElement = fixture.nativeElement.querySelector('a');
|
||||||
|
expect(link).toBeTruthy();
|
||||||
|
expect(link.getAttribute('href')).toMatch(/^unsafe:/);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function initWithTemplate(compType: Type<any>, template: string) {
|
function initWithTemplate(compType: Type<any>, template: string) {
|
||||||
|
|||||||
Reference in New Issue
Block a user