Skip to content
This repository has been archived by the owner on Aug 29, 2023. It is now read-only.

Commit

Permalink
fix(select): block xss on md-select-label
Browse files Browse the repository at this point in the history
  • Loading branch information
jelbourn committed Nov 15, 2016
1 parent 088d2e6 commit a5a860e
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 7 deletions.
37 changes: 31 additions & 6 deletions src/components/select/select.js
Expand Up @@ -174,7 +174,8 @@ angular.module('material.components.select', [
* </div>
* </hljs>
*/
function SelectDirective($mdSelect, $mdUtil, $mdConstant, $mdTheming, $mdAria, $parse) {
function SelectDirective($mdSelect, $mdUtil, $mdConstant, $mdTheming, $mdAria, $parse, $sce,
$injector) {
var keyCodes = $mdConstant.KEY_CODE;
var NAVIGATION_KEYS = [keyCodes.SPACE, keyCodes.ENTER, keyCodes.UP_ARROW, keyCodes.DOWN_ARROW];

Expand Down Expand Up @@ -337,17 +338,41 @@ function SelectDirective($mdSelect, $mdUtil, $mdConstant, $mdTheming, $mdAria, $
mdSelectCtrl.setLabelText = function(text) {
mdSelectCtrl.setIsPlaceholder(!text);

if (attr.mdSelectedText) {
text = $parse(attr.mdSelectedText)(scope);
} else {
// Whether the select label has been given via user content rather than the internal
// template of <md-option>
var isSelectLabelFromUser = false;

if (attr.mdSelectedText && attr.mdSelectedHtml) {
throw Error('md-select cannot have both `md-selected-text` and `md-selected-html`');
}

if (attr.mdSelectedText || attr.mdSelectedHtml) {
text = $parse(attr.mdSelectedText || attr.mdSelectedHtml)(scope);
isSelectLabelFromUser = true;
} else if (!text) {
// Use placeholder attribute, otherwise fallback to the md-input-container label
var tmpPlaceholder = attr.placeholder ||
(containerCtrl && containerCtrl.label ? containerCtrl.label.text() : '');
text = text || tmpPlaceholder || '';

text = tmpPlaceholder || '';
isSelectLabelFromUser = true;
}

var target = valueEl.children().eq(0);
target.html(text);

if (attr.mdSelectedHtml) {
if (!$injector.has('$sanitize')) {
throw Error('The ngSanitize module must be loaded in order to use ' +
'md-treat-selected-text-as-html.');
}

target.html($sce.getTrustedHtml(text));
} else if (isSelectLabelFromUser) {
target.text(text);
} else {
// If we've reached this point, the text is not user-provided.
target.html(text);
}
};

mdSelectCtrl.setIsPlaceholder = function(isPlaceholder) {
Expand Down
17 changes: 16 additions & 1 deletion src/components/select/select.spec.js
Expand Up @@ -3,7 +3,7 @@ describe('<md-select>', function() {
var body, $document, $rootScope, $compile, $timeout, $material;

beforeEach(function() {
module('material.components.select', 'material.components.input');
module('material.components.select', 'material.components.input', 'ngSanitize');

inject(function($injector) {
$document = $injector.get('$document');
Expand Down Expand Up @@ -437,6 +437,21 @@ describe('<md-select>', function() {
expect(label.text()).toBe($rootScope.selectedText);
});

it('should sanitize md-selected-text', function() {
$rootScope.selectedText = '<b>Hello World</b><script>window.mdSelectXss="YES"</script>';

var select = setupSelect(
'ng-model="someVal", ' +
'md-selected-html="selectedText"', null, true).find('md-select');
var label = select.find('md-select-value');

expect(label.text()).toBe('Hello World');

// The label is loaded into a span that is the first child of the '<md-select-value>`.
expect(label[0].childNodes[0].innerHTML).toBe('<b>Hello World</b>');
expect(window.mdSelectXss).toBeUndefined();
});

it('supports rendering multiple', function() {
$rootScope.val = [1, 3];
var select = $compile('<md-input-container>' +
Expand Down

0 comments on commit a5a860e

Please sign in to comment.