From 8d14ffcf67537aed9d344e75e32de731fc86c3ab Mon Sep 17 00:00:00 2001 From: Aliaksandr Yankouski Date: Thu, 5 Jul 2018 16:36:35 +0300 Subject: [PATCH 1/5] add ability to compile child directives --- packages/kbn-i18n/package.json | 1 + packages/kbn-i18n/src/angular/directive.ts | 21 +++++++++++++++------ packages/kbn-i18n/yarn.lock | 10 ++++++++++ 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/packages/kbn-i18n/package.json b/packages/kbn-i18n/package.json index 7ef8d10c3a916..40bff34fb11df 100644 --- a/packages/kbn-i18n/package.json +++ b/packages/kbn-i18n/package.json @@ -22,6 +22,7 @@ "@babel/preset-env": "^7.1.0", "@babel/preset-react": "^7.0.0", "@babel/preset-typescript": "^7.1.0", + "@types/angular-sanitize": "^1.3.7", "@types/intl-relativeformat": "^2.1.0", "@types/json5": "^0.0.30", "@types/react-intl": "^2.3.11", diff --git a/packages/kbn-i18n/src/angular/directive.ts b/packages/kbn-i18n/src/angular/directive.ts index f0ef9e6b58a89..6891ee6ced4f5 100644 --- a/packages/kbn-i18n/src/angular/directive.ts +++ b/packages/kbn-i18n/src/angular/directive.ts @@ -17,7 +17,7 @@ * under the License. */ -import { IDirective, IRootElementService, IScope } from 'angular'; +import { IDirective, IRootElementService, IScope, ICompileService, sanitize } from 'angular'; import { I18nServiceType } from './provider'; @@ -29,16 +29,25 @@ export function i18nDirective(i18n: I18nServiceType): IDirective { defaultMessage: '@i18nDefaultMessage', values: '=i18nValues', }, - link($scope: IScope, $element: IRootElementService) { + link( + $scope: IScope, + $element: IRootElementService, + $compile: ICompileService, + $sanitize: sanitize.ISanitizeService + ) { $scope.$watchGroup( ['id', 'defaultMessage', 'values'], ([id, defaultMessage = '', values = {}]) => { $element.html( - i18n(id, { - values, - defaultMessage, - }) + $sanitize( + i18n(id, { + values, + defaultMessage, + }) + ) ); + + $compile($element.contents())($scope.$parent); } ); }, diff --git a/packages/kbn-i18n/yarn.lock b/packages/kbn-i18n/yarn.lock index edf8dfa36f8f6..6a28cf63d73db 100644 --- a/packages/kbn-i18n/yarn.lock +++ b/packages/kbn-i18n/yarn.lock @@ -622,6 +622,16 @@ lodash "^4.17.10" to-fast-properties "^2.0.0" +"@types/angular-sanitize@^1.3.7": + version "1.3.7" + resolved "https://registry.yarnpkg.com/@types/angular-sanitize/-/angular-sanitize-1.3.7.tgz#1c4fcf5f517adccc731f3c977704c0543eb19cf9" + dependencies: + "@types/angular" "*" + +"@types/angular@*": + version "1.6.51" + resolved "https://registry.yarnpkg.com/@types/angular/-/angular-1.6.51.tgz#a67515b0ba6a2ff68894a39405c1343cbf9c36d4" + "@types/intl-relativeformat@^2.1.0": version "2.1.0" resolved "https://registry.yarnpkg.com/@types/intl-relativeformat/-/intl-relativeformat-2.1.0.tgz#3a2b0043380388f39c666665ec517e11412f1358" From 6486b930cdeac2fdb6066735bf1c7504980bf21d Mon Sep 17 00:00:00 2001 From: Leanid Shutau Date: Fri, 28 Sep 2018 15:44:05 +0300 Subject: [PATCH 2/5] Add typings and update tests --- packages/kbn-i18n/package.json | 1 - .../__snapshots__/directive.test.ts.snap | 51 +++++++++++++++++++ .../kbn-i18n/src/angular/directive.test.ts | 37 +++++++++----- packages/kbn-i18n/src/angular/directive.ts | 28 +++++----- packages/kbn-i18n/yarn.lock | 10 ---- 5 files changed, 86 insertions(+), 41 deletions(-) create mode 100644 packages/kbn-i18n/src/angular/__snapshots__/directive.test.ts.snap diff --git a/packages/kbn-i18n/package.json b/packages/kbn-i18n/package.json index 40bff34fb11df..7ef8d10c3a916 100644 --- a/packages/kbn-i18n/package.json +++ b/packages/kbn-i18n/package.json @@ -22,7 +22,6 @@ "@babel/preset-env": "^7.1.0", "@babel/preset-react": "^7.0.0", "@babel/preset-typescript": "^7.1.0", - "@types/angular-sanitize": "^1.3.7", "@types/intl-relativeformat": "^2.1.0", "@types/json5": "^0.0.30", "@types/react-intl": "^2.3.11", diff --git a/packages/kbn-i18n/src/angular/__snapshots__/directive.test.ts.snap b/packages/kbn-i18n/src/angular/__snapshots__/directive.test.ts.snap new file mode 100644 index 0000000000000..a94c31f8a1618 --- /dev/null +++ b/packages/kbn-i18n/src/angular/__snapshots__/directive.test.ts.snap @@ -0,0 +1,51 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`i18nDirective inserts correct translation html content 1`] = ` +
+ Default message +
+`; + +exports[`i18nDirective inserts correct translation html content with values 1`] = ` +
inner message\\" />' }" +> + Default message, +
+ + inner message + +
+
+`; + +exports[`i18nDirective sanitizes defaultMessage 1`] = ` +
inner message\\" />' }" +> + Dangerous default message, +
+ inner message +
+
+`; diff --git a/packages/kbn-i18n/src/angular/directive.test.ts b/packages/kbn-i18n/src/angular/directive.test.ts index 20bcab4e67cda..7bc6886a25839 100644 --- a/packages/kbn-i18n/src/angular/directive.test.ts +++ b/packages/kbn-i18n/src/angular/directive.test.ts @@ -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); @@ -43,38 +44,46 @@ describe('i18nDirective', () => { ); test('inserts correct translation html content', () => { - const id = 'id'; - const defaultMessage = 'default-message'; + const element = angular.element( + `
` + ); + + compile(element)(scope); + scope.$digest(); + expect(element[0]).toMatchSnapshot(); + }); + + test('sanitizes defaultMessage', () => { const element = angular.element( `
` ); compile(element)(scope); scope.$digest(); - expect(element.html()).toEqual(defaultMessage); + expect(element[0]).toMatchSnapshot(); }); test('inserts correct translation html content with values', () => { - const id = 'id'; - const defaultMessage = 'default-message {word}'; - const compiledContent = 'default-message word'; - const element = angular.element( `
` ); compile(element)(scope); scope.$digest(); - expect(element.html()).toEqual(compiledContent); + expect(element[0]).toMatchSnapshot(); }); }); diff --git a/packages/kbn-i18n/src/angular/directive.ts b/packages/kbn-i18n/src/angular/directive.ts index 6891ee6ced4f5..672d7fde3cde7 100644 --- a/packages/kbn-i18n/src/angular/directive.ts +++ b/packages/kbn-i18n/src/angular/directive.ts @@ -17,11 +17,15 @@ * under the License. */ -import { IDirective, IRootElementService, IScope, ICompileService, sanitize } from 'angular'; +import { ICompileService, IDirective, IRootElementService, IScope } from 'angular'; import { I18nServiceType } from './provider'; -export function i18nDirective(i18n: I18nServiceType): IDirective { +export function i18nDirective( + i18n: I18nServiceType, + $compile: ICompileService, + $sanitize: (html: string) => string +): IDirective { return { restrict: 'A', scope: { @@ -29,25 +33,17 @@ export function i18nDirective(i18n: I18nServiceType): IDirective { defaultMessage: '@i18nDefaultMessage', values: '=i18nValues', }, - link( - $scope: IScope, - $element: IRootElementService, - $compile: ICompileService, - $sanitize: sanitize.ISanitizeService - ) { + link($scope: IScope, $element: IRootElementService) { $scope.$watchGroup( ['id', 'defaultMessage', 'values'], ([id, defaultMessage = '', values = {}]) => { $element.html( - $sanitize( - i18n(id, { - values, - defaultMessage, - }) - ) + i18n(id, { + values, + defaultMessage: $sanitize(defaultMessage), + }) ); - - $compile($element.contents())($scope.$parent); + $compile($element.contents() as any)($scope.$parent); } ); }, diff --git a/packages/kbn-i18n/yarn.lock b/packages/kbn-i18n/yarn.lock index 6a28cf63d73db..edf8dfa36f8f6 100644 --- a/packages/kbn-i18n/yarn.lock +++ b/packages/kbn-i18n/yarn.lock @@ -622,16 +622,6 @@ lodash "^4.17.10" to-fast-properties "^2.0.0" -"@types/angular-sanitize@^1.3.7": - version "1.3.7" - resolved "https://registry.yarnpkg.com/@types/angular-sanitize/-/angular-sanitize-1.3.7.tgz#1c4fcf5f517adccc731f3c977704c0543eb19cf9" - dependencies: - "@types/angular" "*" - -"@types/angular@*": - version "1.6.51" - resolved "https://registry.yarnpkg.com/@types/angular/-/angular-1.6.51.tgz#a67515b0ba6a2ff68894a39405c1343cbf9c36d4" - "@types/intl-relativeformat@^2.1.0": version "2.1.0" resolved "https://registry.yarnpkg.com/@types/intl-relativeformat/-/intl-relativeformat-2.1.0.tgz#3a2b0043380388f39c666665ec517e11412f1358" From 81b29c06d5cb2403a1bb4684a3ee919f621020e3 Mon Sep 17 00:00:00 2001 From: Leanid Shutau Date: Tue, 16 Oct 2018 17:35:09 +0300 Subject: [PATCH 3/5] Sanitize safe values --- .../__snapshots__/directive.test.ts.snap | 18 ++++++++++++++++-- .../kbn-i18n/src/angular/directive.test.ts | 19 +++++++++++++++++-- packages/kbn-i18n/src/angular/directive.ts | 15 ++++++++++++++- 3 files changed, 47 insertions(+), 5 deletions(-) diff --git a/packages/kbn-i18n/src/angular/__snapshots__/directive.test.ts.snap b/packages/kbn-i18n/src/angular/__snapshots__/directive.test.ts.snap index de9d98fe40c26..73ec63825b027 100644 --- a/packages/kbn-i18n/src/angular/__snapshots__/directive.test.ts.snap +++ b/packages/kbn-i18n/src/angular/__snapshots__/directive.test.ts.snap @@ -17,9 +17,9 @@ exports[`i18nDirective inserts correct translation html content with values 2`] exports[`i18nDirective sanitizes defaultMessage 1`] = `
inner message\\" />' }" + i18n-values="{ unsafe_value: '
inner message\\" />' }" > Dangerous default message,
`; + +exports[`i18nDirective sanitizes safe value 1`] = ` +
' }" +> + Default message, +
+
+`; diff --git a/packages/kbn-i18n/src/angular/directive.test.ts b/packages/kbn-i18n/src/angular/directive.test.ts index 843d381dc4a00..527fe1c9bd6bc 100644 --- a/packages/kbn-i18n/src/angular/directive.test.ts +++ b/packages/kbn-i18n/src/angular/directive.test.ts @@ -62,8 +62,23 @@ describe('i18nDirective', () => { const element = angular.element( `
` + ); + + compile(element)(scope); + scope.$digest(); + + expect(element[0]).toMatchSnapshot(); + }); + + test('sanitizes safe value', () => { + const element = angular.element( + `
` ); diff --git a/packages/kbn-i18n/src/angular/directive.ts b/packages/kbn-i18n/src/angular/directive.ts index 37ed877c21e47..b5e655f119ac6 100644 --- a/packages/kbn-i18n/src/angular/directive.ts +++ b/packages/kbn-i18n/src/angular/directive.ts @@ -58,9 +58,22 @@ function setHtmlContent( $sanitize: (html: string) => string, i18n: I18nServiceType ) { + const values = Object.entries($scope.values || {}).reduce( + (result, [key, value]) => { + if (key.startsWith('unsafe_')) { + result[key] = value; + } else { + result[key] = $sanitize(value); + } + + return result; + }, + {} as Record + ); + $element.html( i18n($scope.id, { - values: $scope.values, + values, defaultMessage: $sanitize($scope.defaultMessage), }) ); From de46c9b215e5640815b1cf150d0af9ffa8c82ba7 Mon Sep 17 00:00:00 2001 From: Leanid Shutau Date: Wed, 17 Oct 2018 17:13:58 +0300 Subject: [PATCH 4/5] Escape html tags in defaultMessage --- packages/kbn-i18n/src/angular/directive.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kbn-i18n/src/angular/directive.ts b/packages/kbn-i18n/src/angular/directive.ts index b5e655f119ac6..935dfd66ab3d4 100644 --- a/packages/kbn-i18n/src/angular/directive.ts +++ b/packages/kbn-i18n/src/angular/directive.ts @@ -74,7 +74,7 @@ function setHtmlContent( $element.html( i18n($scope.id, { values, - defaultMessage: $sanitize($scope.defaultMessage), + defaultMessage: ($scope.defaultMessage || '').replace(/\/g, '>'), }) ); } From 61c4f2b543113f46f7abf1b462d9cc9f0e21b82e Mon Sep 17 00:00:00 2001 From: Leanid Shutau Date: Wed, 17 Oct 2018 17:48:45 +0300 Subject: [PATCH 5/5] Fix tests --- .../kbn-i18n/src/angular/__snapshots__/directive.test.ts.snap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kbn-i18n/src/angular/__snapshots__/directive.test.ts.snap b/packages/kbn-i18n/src/angular/__snapshots__/directive.test.ts.snap index 73ec63825b027..e7d7cf208b0ab 100644 --- a/packages/kbn-i18n/src/angular/__snapshots__/directive.test.ts.snap +++ b/packages/kbn-i18n/src/angular/__snapshots__/directive.test.ts.snap @@ -27,7 +27,7 @@ exports[`i18nDirective sanitizes defaultMessage 1`] = ` i18n-default-message="inner message" i18n-id="id2" > - inner message + <script></script>inner message
`;