-
Notifications
You must be signed in to change notification settings - Fork 8.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[I18n] Compile html in angular directive #23684
[I18n] Compile html in angular directive #23684
Conversation
}) | ||
); | ||
$compile($element.contents() as any)($scope.$parent); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@azasypkin I believe now it is safe to compile html because:
- we have agreed not to use tags in
defaultMessage
. - even if some vendor pass any tags, scripts or directive in
defaultMessage
for some language, before insertingdefaultMessage
to DOM we check passed string using$sanitize
angular service which removes all unsafe tags likescript
and directives.
For example we have such text indefaultMessage
:
<span>hello</span>world
<im-dangerous-directive>I will destroy something</im-dangerous-directive>
<script>document.write('ha-ha')</script>
after using $sanitize service we will get:
<span>hello</span>world
@azasypkin this merge request is necessary for us to be able to pass angular directives via values. Now we also can pass directives but they do not work because they need to be compiled. To check that all works as expected you can:
with this code: <p>
<span i18n-id="test.test"
i18n-default-message="I just check {justToCheck}"
i18n-values="{ justToCheck: '<strong ng-click="checkDirectivesAreCompiled()">click me!</strong>' }">
</span>
</p> and in file
open dashboard page. |
💚 Build Succeeded |
ack: Looking into this one at the monent |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Checked it works as expected.
2bc299e
to
6486b93
Compare
💔 Build Failed |
retest |
💔 Build Failed |
…tml-in-i18n-directive
💚 Build Succeeded |
…tml-in-i18n-directive
💔 Build Failed |
…tml-in-i18n-directive
💔 Build Failed |
retest |
💔 Build Failed |
…tml-in-i18n-directive
💚 Build Succeeded |
💔 Build Failed |
💔 Build Failed |
retest |
💔 Build Failed |
…tml-in-i18n-directive
💚 Build Succeeded |
@@ -38,20 +42,39 @@ 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); | |||
$compile($element.contents() as any)($scope.$parent); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue: I'm still afraid of this "global" compile.... with this even $sanitize
'ed variable without usafe_
can be pretty harmful, imagine the case that looks like this (help link that is displayed somewhere else on this page will lead user to the attacker site):
<span i18n-id="kbn.management.someId"
i18n-default-message="Some text that seems safe: {val}"
i18n-values="{ val: '\{\{controller.helpLink=\'http://attacker-site/\';\'value\'\}\}' }">
</span>
Is there any way we can compile just unsafe_
pieces? Can you paste a couple of real use cases? If it's only about directives (e.g. <a ng-href\ng-click
), we can probably mark all these elements with directive with some custom data-unsafe-i18n
data attribute or class and then query only these things and compile.
I know it's becoming complex, but I feel uncomfortable with how it's currently. Basically we have 3 cases (sorted by popularity):
- Simple default message + simple values, in this case we should just use
element.text(i18n.translate(...))
-text
will escape everything for us. - Simple default message + values with HTML (
html_
prefix) - in this case we have to useelement.html
, so we shouldescape
default message (let's trylodash.escape
instead of manual solution, we have severallodash.*
packages in Kibana already anyway) + escape ordinary values (withouthtml_
prefix) +$sanitize
html_
values - Simple default message + unsafe values (
unsafe_
prefix_) - in this case we also have to useelement.html
, so again we shouldescape
default message + escape ordinary values +$sanitize
html_
values. And once we inserted them into DOM, we should call$compile
for nodes withdata-unsafe-i18n
(or something like this).
Am I over-engineering this (or missing something) @spalger @LeanidShutau?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any way we can compile just unsafe_ pieces? Can you paste a couple of real use cases? If it's only about directives (e.g. <a ng-href\ng-click), we can probably mark all these elements with directive with some custom data-unsafe-i18n data attribute or class and then query only these things and compile.
Good point. I believe we can do it. It looks like in this case we do not need to use unsafe_
prefix for values' property names.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have to be honest, I don't have enough context to say if this is over-engineering or a comprehensive solution. Since I've been asked to review this and it seems like the overall direction is still in flux I'd really like to chat about what's going on here and how/why we're doing things this way. The PR description and the linked issue have basically no details and I just don't know enough to review this...
values: $scope.values, | ||
defaultMessage: $scope.defaultMessage, | ||
values, | ||
defaultMessage: ($scope.defaultMessage || '').replace(/\</g, '<').replace(/\>/g, '>'), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
question: why do we need || ''
? I believe we shouldn't ever have empty default message?
We don't need angular directive compiling any more. $sanitize part was implemented in separate PR: #24830 Issue has been solved by refactoring: <p class="kuiText kuiVerticalRhythm">
<span
i18n-id="kbn.dashboard.addVisualizationDescription1"
i18n-default-message="Click the "
i18n-context="Part of composite label kbn.dashboard.addVisualizationDescription1 + {link} + kbn.dashboard.addVisualizationDescription2"
>
</span>
<a
i18n-id="kbn.dashboard.addVisualizationLink"
i18n-default-message="Add"
kbn-accessible-click class="kuiButton kuiButton--primary kuiButton--small"
ng-click="showAddPanel()"
aria-label="{{ 'kbn.dashboard.addVisualizationLinkAria...' | i18n: { defaultMessage: 'Add visualization' }}"
data-test-subj="emptyDashboardAddPanelButton">
</a>
<span
i18n-id="kbn.dashboard.addVisualizationDescription2"
i18n-default-message=" button in the menu bar above to add a visualization to the dashboard. {br}If you haven't set up any visualizations yet, {visitVisualizeAppLink} to create your first visualization."
i18n-values="{
br: '<br/>',
visitVisualizeAppLink: '<a class=\'kuiLink\' href=\'#/visualize\'>' + visitVisualizeAppLinkText + '</a>'}"
i18n-context="Part of composite label kbn.dashboard.addVisualizationDescription1 + {link} + kbn.dashboard.addVisualizationDescription2"
>
</span>
</p> This PR can be reopened in the future, if needed |
Resolves #23359
We need to compile html in i18n directive to be able to pass html with angular directive via values, for example
It's not secure, so we need to sanitize or escape html in
defeultMessage
or in values, that are not supposed to contain potentially dangerous html code. One of the solutions is to useunsafe_
prefix in some values keys and sanitize/escape html indefaultMessage
and all other values.