[I18n] Sanitize angular directive message before inserting it to DOM (#24830)

* [I18n] Sanitize message before inserting it to DOM

* Add tests
This commit is contained in:
Leanid Shutau 2018-11-01 11:37:19 +03:00 committed by GitHub
parent f05bb942fa
commit 2fb2bda157
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 107 additions and 9 deletions

View file

@ -3,3 +3,45 @@
exports[`i18nDirective inserts correct translation html content with values 1`] = `"default-message word"`;
exports[`i18nDirective inserts correct translation html content with values 2`] = `"default-message anotherWord"`;
exports[`i18nDirective sanitizes message before inserting it to DOM 1`] = `
<div
class="ng-scope ng-isolate-scope"
i18n-default-message="Default message, {value}"
i18n-id="id"
i18n-values="{ value: '<div ng-click=\\"dangerousAction()\\"></div>' }"
>
Default message,
<div />
</div>
`;
exports[`i18nDirective sanitizes onclick attribute 1`] = `
<div
class="ng-scope ng-isolate-scope"
i18n-default-message="Default {one} onclick=alert(1) {two} message"
i18n-id="id"
i18n-values="{ one: '<span', two: '>Press</span>' }"
>
Default
<span>
Press
</span>
message
</div>
`;
exports[`i18nDirective sanitizes onmouseover attribute 1`] = `
<div
class="ng-scope ng-isolate-scope"
i18n-default-message="Default {value} message"
i18n-id="id"
i18n-values="{ value: '<span onmouseover=\\"alert(1)\\">Press</span>' }"
>
Default
<span>
Press
</span>
message
</div>
`;

View file

@ -19,12 +19,13 @@
import angular from 'angular';
import 'angular-mocks';
import 'angular-sanitize';
import { i18nDirective } from './directive';
import { I18nProvider } from './provider';
angular
.module('app', [])
.module('app', ['ngSanitize'])
.provider('i18n', I18nProvider)
.directive('i18nId', i18nDirective);
@ -82,4 +83,49 @@ describe('i18nDirective', () => {
expect(element.html()).toMatchSnapshot();
});
test('sanitizes message before inserting it to DOM', () => {
const element = angular.element(
`<div
i18n-id="id"
i18n-default-message="Default message, {value}"
i18n-values="{ value: '<div ng-click=&quot;dangerousAction()&quot;></div>' }"
/>`
);
compile(element)(scope);
scope.$digest();
expect(element[0]).toMatchSnapshot();
});
test('sanitizes onclick attribute', () => {
const element = angular.element(
`<div
i18n-id="id"
i18n-default-message="Default {one} onclick=alert(1) {two} message"
i18n-values="{ one: '<span', two: '>Press</span>' }"
/>`
);
compile(element)(scope);
scope.$digest();
expect(element[0]).toMatchSnapshot();
});
test('sanitizes onmouseover attribute', () => {
const element = angular.element(
`<div
i18n-id="id"
i18n-default-message="Default {value} message"
i18n-values="{ value: '<span onmouseover=&quot;alert(1)&quot;>Press</span>' }"
/>`
);
compile(element)(scope);
scope.$digest();
expect(element[0]).toMatchSnapshot();
});
});

View file

@ -27,7 +27,10 @@ interface I18nScope extends IScope {
id: string;
}
export function i18nDirective(i18n: I18nServiceType): IDirective<I18nScope> {
export function i18nDirective(
i18n: I18nServiceType,
$sanitize: (html: string) => string
): IDirective<I18nScope> {
return {
restrict: 'A',
scope: {
@ -38,20 +41,27 @@ export function i18nDirective(i18n: I18nServiceType): IDirective<I18nScope> {
link($scope, $element) {
if ($scope.values) {
$scope.$watchCollection('values', () => {
setHtmlContent($element, $scope, i18n);
setHtmlContent($element, $scope, $sanitize, i18n);
});
} else {
setHtmlContent($element, $scope, i18n);
setHtmlContent($element, $scope, $sanitize, i18n);
}
},
};
}
function setHtmlContent($element: IRootElementService, $scope: I18nScope, i18n: I18nServiceType) {
function setHtmlContent(
$element: IRootElementService,
$scope: I18nScope,
$sanitize: (html: string) => string,
i18n: I18nServiceType
) {
$element.html(
i18n($scope.id, {
values: $scope.values,
defaultMessage: $scope.defaultMessage,
})
$sanitize(
i18n($scope.id, {
values: $scope.values,
defaultMessage: $scope.defaultMessage,
})
)
);
}