Skip to content
This repository was archived by the owner on Sep 5, 2024. It is now read-only.

Commit 6a56450

Browse files
committed
fix(dialog): break content into textContent and htmlContent to
help keep users from accidentally introducing an XSS vector.
1 parent fe84405 commit 6a56450

File tree

10 files changed

+130
-113
lines changed

10 files changed

+130
-113
lines changed

config/karma.conf.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ module.exports = function(config) {
3434
'node_modules/angular/angular.js',
3535
'node_modules/angular-animate/angular-animate.js',
3636
'node_modules/angular-aria/angular-aria.js',
37+
'node_modules/angular-sanitize/angular-sanitize.js',
3738
'node_modules/angular-mocks/angular-mocks.js',
3839
'test/angular-material-mocks.js',
3940
'test/angular-material-spec.js'

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"angular-animate": "1.4.7",
1616
"angular-aria": "1.4.7",
1717
"angular-messages": "1.4.7",
18+
"angular-sanitize": "1.4.7",
1819
"angular-mocks": "1.4.7",
1920
"angular-route": "1.4.7",
2021
"angularytics": "^0.3.0",
@@ -71,4 +72,4 @@
7172
"merge-base": "git merge-base $(npm run -s current-branch) origin/master",
7273
"squash": "git rebase -i $(npm run -s merge-base)"
7374
}
74-
}
75+
}

src/components/dialog/demoBasicUsage/script.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ angular.module('dialogDemo1', ['ngMaterial'])
1212
.parent(angular.element(document.querySelector('#popupContainer')))
1313
.clickOutsideToClose(true)
1414
.title('This is an alert title')
15-
.content('You can specify some description text in here.')
15+
.textContent('You can specify some description text in here.')
1616
.ariaLabel('Alert Dialog Demo')
1717
.ok('Got it!')
1818
.targetEvent(ev)
@@ -23,7 +23,7 @@ angular.module('dialogDemo1', ['ngMaterial'])
2323
// Appending dialog to document.body to cover sidenav in docs app
2424
var confirm = $mdDialog.confirm()
2525
.title('Would you like to delete your debt?')
26-
.content('All of the banks have agreed to <span class="debt-be-gone">forgive</span> you your debts.')
26+
.textContent('All of the banks have agreed to forgive you your debts.')
2727
.ariaLabel('Lucky day')
2828
.targetEvent(ev)
2929
.ok('Please do it!')

src/components/dialog/demoBasicUsage/style.css

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,6 @@
22
position:relative;
33
}
44

5-
.debt-be-gone {
6-
font-weight: bold;
7-
color:blue;
8-
text-decoration: underline;
9-
}
10-
115
.footer {
126
width:100%;
137
text-align: center;

src/components/dialog/dialog.js

Lines changed: 41 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -294,10 +294,12 @@ function MdDialogDirective($$rAF, $mdTheming, $mdDialog) {
294294
*
295295
* @returns {obj} an `$mdDialogPreset` with the chainable configuration methods:
296296
*
297-
* - $mdDialogPreset#title(string) - sets title to string
298-
* - $mdDialogPreset#content(string) - sets content / message to string
299-
* - $mdDialogPreset#ok(string) - sets okay button text to string
300-
* - $mdDialogPreset#theme(string) - sets the theme of the dialog
297+
* - $mdDialogPreset#title(string) - Sets the alert title.
298+
* - $mdDialogPreset#textContent(string) - Sets the alert message.
299+
* - $mdDialogPreset#htmlContent(string) - Sets the alert message as HTML. Requires ngSanitize
300+
* module to be loaded. HTML is not run through Angular's compiler.
301+
* - $mdDialogPreset#ok(string) - Sets the alert "Okay" button text.
302+
* - $mdDialogPreset#theme(string) - Sets the theme of the alert dialog.
301303
*
302304
*/
303305

@@ -313,11 +315,13 @@ function MdDialogDirective($$rAF, $mdTheming, $mdDialog) {
313315
*
314316
* Additionally, it supports the following methods:
315317
*
316-
* - $mdDialogPreset#title(string) - sets title to string
317-
* - $mdDialogPreset#content(string) - sets content / message to string
318-
* - $mdDialogPreset#ok(string) - sets okay button text to string
319-
* - $mdDialogPreset#cancel(string) - sets cancel button text to string
320-
* - $mdDialogPreset#theme(string) - sets the theme of the dialog
318+
* - $mdDialogPreset#title(string) - Sets the confirm title.
319+
* - $mdDialogPreset#textContent(string) - Sets the confirm message.
320+
* - $mdDialogPreset#htmlContent(string) - Sets the confirm message as HTML. Requires ngSanitize
321+
* module to be loaded. HTML is not run through Angular's compiler.
322+
* - $mdDialogPreset#ok(string) - Sets the confirm "Okay" button text.
323+
* - $mdDialogPreset#cancel(string) - Sets the confirm "Cancel" button text.
324+
* - $mdDialogPreset#theme(string) - Sets the theme of the confirm dialog.
321325
*
322326
*/
323327

@@ -332,7 +336,9 @@ function MdDialogDirective($$rAF, $mdTheming, $mdDialog) {
332336
* `confirm()`, or an options object with the following properties:
333337
* - `templateUrl` - `{string=}`: The url of a template that will be used as the content
334338
* of the dialog.
335-
* - `template` - `{string=}`: Same as templateUrl, except this is an actual template string.
339+
* - `template` - `{string=}`: HTML template to show in the dialog. This **must** be trusted HTML
340+
* with respect to Angular's [$sce service](https://docs.angularjs.org/api/ng/service/$sce).
341+
* This template should **never** be constructed with any kind of user input or user data.
336342
* - `autoWrap` - `{boolean=}`: Whether or not to automatically wrap the template with a
337343
* `<md-dialog>` tag if one is not provided. Defaults to true. Can be disabled if you provide a
338344
* custom dialog directive.
@@ -416,11 +422,11 @@ function MdDialogProvider($$interimElementProvider) {
416422
options: dialogDefaultOptions
417423
})
418424
.addPreset('alert', {
419-
methods: ['title', 'content', 'ariaLabel', 'ok', 'theme', 'css'],
425+
methods: ['title', 'htmlContent', 'textContent', 'ariaLabel', 'ok', 'theme', 'css'],
420426
options: advancedDialogOptions
421427
})
422428
.addPreset('confirm', {
423-
methods: ['title', 'content', 'ariaLabel', 'ok', 'cancel', 'theme', 'css'],
429+
methods: ['title', 'htmlContent', 'textContent', 'ariaLabel', 'ok', 'cancel', 'theme', 'css'],
424430
options: advancedDialogOptions
425431
});
426432

@@ -431,7 +437,11 @@ function MdDialogProvider($$interimElementProvider) {
431437
'<md-dialog md-theme="{{ dialog.theme }}" aria-label="{{ dialog.ariaLabel }}" ng-class="dialog.css">',
432438
' <md-dialog-content class="md-dialog-content" role="document" tabIndex="-1">',
433439
' <h2 class="md-title">{{ dialog.title }}</h2>',
434-
' <div class="md-dialog-content-body" md-template="::dialog.mdContent"></div>',
440+
' <div ng-if="::dialog.mdHtmlContent" class="md-dialog-content-body" ',
441+
' ng-bind-html="::dialog.mdHtmlContent"></div>',
442+
' <div ng-if="::!dialog.mdHtmlContent" class="md-dialog-content-body">',
443+
' <p>{{::dialog.mdTextContent}}</p>',
444+
' </div>',
435445
' </md-dialog-content>',
436446
' <md-dialog-actions>',
437447
' <md-button ng-if="dialog.$type == \'confirm\'"' +
@@ -459,11 +469,12 @@ function MdDialogProvider($$interimElementProvider) {
459469
}
460470

461471
/* @ngInject */
462-
function dialogDefaultOptions($mdDialog, $mdAria, $mdUtil, $mdConstant, $animate, $document, $window, $rootElement, $log) {
472+
function dialogDefaultOptions($mdDialog, $mdAria, $mdUtil, $mdConstant, $animate, $document, $window, $rootElement, $log, $injector) {
463473
return {
464474
hasBackdrop: true,
465475
isolateScope: true,
466476
onShow: onShow,
477+
onShowing: beforeShow,
467478
onRemove: onRemove,
468479
clickOutsideToClose: false,
469480
escapeToClose: true,
@@ -489,14 +500,25 @@ function MdDialogProvider($$interimElementProvider) {
489500
}
490501
};
491502

492-
/**
493-
* Show method for dialogs
494-
*/
503+
function beforeShow(scope, element, options, controller) {
504+
if (controller) {
505+
controller.mdHtmlContent = controller.htmlContent || options.htmlContent || '';
506+
controller.mdTextContent = controller.textContent || options.textContent || '';
507+
508+
if (controller.mdHtmlContent && !$injector.has('$sanitize')) {
509+
throw Error('The ngSanitize module must be loaded in order to use htmlContent.');
510+
}
511+
512+
if (controller.mdHtmlContent && controller.mdTextContent) {
513+
throw Error('md-dialog cannot have both `htmlContent` and `textContent`');
514+
}
515+
}
516+
}
517+
518+
/** Show method for dialogs */
495519
function onShow(scope, element, options, controller) {
496520
angular.element($document[0].body).addClass('md-dialog-is-showing');
497521

498-
wrapSimpleContent();
499-
500522
captureParentAndFromToElements(options);
501523
configureAria(element.find('md-dialog'), options);
502524
showBackdrop(scope, element, options);
@@ -548,27 +570,6 @@ function MdDialogProvider($$interimElementProvider) {
548570
return angular.element(closeButton);
549571
}
550572
}
551-
552-
/**
553-
* Wrap any simple content [specified via .content("")] in <p></p> tags.
554-
* otherwise accept HTML content within the dialog content area...
555-
* NOTE: Dialog uses the md-template directive to safely inject HTML content.
556-
*/
557-
function wrapSimpleContent() {
558-
if ( controller ) {
559-
var HTML_END_TAG = /<\/[\w-]*>/gm;
560-
var content = controller.content || options.content || "";
561-
562-
var hasHTML = HTML_END_TAG.test(content);
563-
if (!hasHTML) {
564-
content = $mdUtil.supplant("<p>{0}</p>", [content]);
565-
}
566-
567-
// Publish updated dialog content body... to be compiled by mdTemplate directive
568-
controller.mdContent = content;
569-
}
570-
}
571-
572573
}
573574

574575
/**

0 commit comments

Comments
 (0)