mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-16 23:30:23 +00:00
fix($sanitize): Use same whitelist mechanism as $compile does.
`$sanitize` now uses the same mechanism as `$compile` to validate uris. By this, the validation in `$sanitize` is more general and can be configured in the same way as the one in `$compile`. Changes - Creates the new private service `$$sanitizeUri`. - Moves related specs from `compileSpec.js` into `sanitizeUriSpec.js`. - Refactors the `linky` filter to be less dependent on `$sanitize` internal functions. Fixes #3748.
This commit is contained in:
parent
68ceb17272
commit
333523483f
9 changed files with 561 additions and 350 deletions
1
angularFiles.js
vendored
1
angularFiles.js
vendored
|
|
@ -27,6 +27,7 @@ angularFiles = {
|
|||
'src/ng/parse.js',
|
||||
'src/ng/q.js',
|
||||
'src/ng/rootScope.js',
|
||||
'src/ng/sanitizeUri.js',
|
||||
'src/ng/sce.js',
|
||||
'src/ng/sniffer.js',
|
||||
'src/ng/timeout.js',
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@
|
|||
$ParseProvider,
|
||||
$RootScopeProvider,
|
||||
$QProvider,
|
||||
$$SanitizeUriProvider,
|
||||
$SceProvider,
|
||||
$SceDelegateProvider,
|
||||
$SnifferProvider,
|
||||
|
|
@ -136,6 +137,10 @@ function publishExternalAPI(angular){
|
|||
|
||||
angularModule('ng', ['ngLocale'], ['$provide',
|
||||
function ngModule($provide) {
|
||||
// $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
|
||||
$provide.provider({
|
||||
$$sanitizeUri: $$SanitizeUriProvider
|
||||
});
|
||||
$provide.provider('$compile', $CompileProvider).
|
||||
directive({
|
||||
a: htmlAnchorDirective,
|
||||
|
|
|
|||
|
|
@ -493,14 +493,12 @@ var $compileMinErr = minErr('$compile');
|
|||
*
|
||||
* @description
|
||||
*/
|
||||
$CompileProvider.$inject = ['$provide'];
|
||||
function $CompileProvider($provide) {
|
||||
$CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];
|
||||
function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
var hasDirectives = {},
|
||||
Suffix = 'Directive',
|
||||
COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,
|
||||
CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/,
|
||||
aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
|
||||
imgSrcSanitizationWhitelist = /^\s*(https?|ftp|file):|data:image\//;
|
||||
CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/;
|
||||
|
||||
// Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
|
||||
// The assumption is that future DOM event attribute names will begin with
|
||||
|
|
@ -584,10 +582,11 @@ function $CompileProvider($provide) {
|
|||
*/
|
||||
this.aHrefSanitizationWhitelist = function(regexp) {
|
||||
if (isDefined(regexp)) {
|
||||
aHrefSanitizationWhitelist = regexp;
|
||||
$$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp);
|
||||
return this;
|
||||
} else {
|
||||
return $$sanitizeUriProvider.aHrefSanitizationWhitelist();
|
||||
}
|
||||
return aHrefSanitizationWhitelist;
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -614,18 +613,18 @@ function $CompileProvider($provide) {
|
|||
*/
|
||||
this.imgSrcSanitizationWhitelist = function(regexp) {
|
||||
if (isDefined(regexp)) {
|
||||
imgSrcSanitizationWhitelist = regexp;
|
||||
$$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp);
|
||||
return this;
|
||||
} else {
|
||||
return $$sanitizeUriProvider.imgSrcSanitizationWhitelist();
|
||||
}
|
||||
return imgSrcSanitizationWhitelist;
|
||||
};
|
||||
|
||||
|
||||
this.$get = [
|
||||
'$injector', '$interpolate', '$exceptionHandler', '$http', '$templateCache', '$parse',
|
||||
'$controller', '$rootScope', '$document', '$sce', '$animate',
|
||||
'$controller', '$rootScope', '$document', '$sce', '$animate', '$$sanitizeUri',
|
||||
function($injector, $interpolate, $exceptionHandler, $http, $templateCache, $parse,
|
||||
$controller, $rootScope, $document, $sce, $animate) {
|
||||
$controller, $rootScope, $document, $sce, $animate, $$sanitizeUri) {
|
||||
|
||||
var Attributes = function(element, attr) {
|
||||
this.$$element = element;
|
||||
|
|
@ -730,16 +729,7 @@ function $CompileProvider($provide) {
|
|||
// sanitize a[href] and img[src] values
|
||||
if ((nodeName === 'A' && key === 'href') ||
|
||||
(nodeName === 'IMG' && key === 'src')) {
|
||||
// NOTE: urlResolve() doesn't support IE < 8 so we don't sanitize for that case.
|
||||
if (!msie || msie >= 8 ) {
|
||||
normalizedVal = urlResolve(value).href;
|
||||
if (normalizedVal !== '') {
|
||||
if ((key === 'href' && !normalizedVal.match(aHrefSanitizationWhitelist)) ||
|
||||
(key === 'src' && !normalizedVal.match(imgSrcSanitizationWhitelist))) {
|
||||
this[key] = value = 'unsafe:' + normalizedVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
this[key] = value = $$sanitizeUri(value, key === 'src');
|
||||
}
|
||||
|
||||
if (writeAttr !== false) {
|
||||
|
|
|
|||
74
src/ng/sanitizeUri.js
Normal file
74
src/ng/sanitizeUri.js
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
'use strict';
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Private service to sanitize uris for links and images. Used by $compile and $sanitize.
|
||||
*/
|
||||
function $$SanitizeUriProvider() {
|
||||
var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
|
||||
imgSrcSanitizationWhitelist = /^\s*(https?|ftp|file):|data:image\//;
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Retrieves or overrides the default regular expression that is used for whitelisting of safe
|
||||
* urls during a[href] sanitization.
|
||||
*
|
||||
* The sanitization is a security measure aimed at prevent XSS attacks via html links.
|
||||
*
|
||||
* Any url about to be assigned to a[href] via data-binding is first normalized and turned into
|
||||
* an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
|
||||
* regular expression. If a match is found, the original url is written into the dom. Otherwise,
|
||||
* the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
|
||||
*
|
||||
* @param {RegExp=} regexp New regexp to whitelist urls with.
|
||||
* @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
|
||||
* chaining otherwise.
|
||||
*/
|
||||
this.aHrefSanitizationWhitelist = function(regexp) {
|
||||
if (isDefined(regexp)) {
|
||||
aHrefSanitizationWhitelist = regexp;
|
||||
return this;
|
||||
}
|
||||
return aHrefSanitizationWhitelist;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Retrieves or overrides the default regular expression that is used for whitelisting of safe
|
||||
* urls during img[src] sanitization.
|
||||
*
|
||||
* The sanitization is a security measure aimed at prevent XSS attacks via html links.
|
||||
*
|
||||
* Any url about to be assigned to img[src] via data-binding is first normalized and turned into
|
||||
* an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
|
||||
* regular expression. If a match is found, the original url is written into the dom. Otherwise,
|
||||
* the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
|
||||
*
|
||||
* @param {RegExp=} regexp New regexp to whitelist urls with.
|
||||
* @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
|
||||
* chaining otherwise.
|
||||
*/
|
||||
this.imgSrcSanitizationWhitelist = function(regexp) {
|
||||
if (isDefined(regexp)) {
|
||||
imgSrcSanitizationWhitelist = regexp;
|
||||
return this;
|
||||
}
|
||||
return imgSrcSanitizationWhitelist;
|
||||
};
|
||||
|
||||
this.$get = function() {
|
||||
return function sanitizeUri(uri, isImage) {
|
||||
var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist;
|
||||
var normalizedVal;
|
||||
// NOTE: urlResolve() doesn't support IE < 8 so we don't sanitize for that case.
|
||||
if (!msie || msie >= 8 ) {
|
||||
normalizedVal = urlResolve(uri).href;
|
||||
if (normalizedVal !== '' && !normalizedVal.match(regex)) {
|
||||
return 'unsafe:'+normalizedVal;
|
||||
}
|
||||
}
|
||||
return uri;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
/* global htmlSanitizeWriter: false */
|
||||
/* global sanitizeText: false */
|
||||
|
||||
/**
|
||||
* @ngdoc filter
|
||||
|
|
@ -100,7 +100,7 @@
|
|||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
angular.module('ngSanitize').filter('linky', function() {
|
||||
angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
|
||||
var LINKY_URL_REGEXP =
|
||||
/((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>]/,
|
||||
MAILTO_REGEXP = /^mailto:/;
|
||||
|
|
@ -110,28 +110,40 @@ angular.module('ngSanitize').filter('linky', function() {
|
|||
var match;
|
||||
var raw = text;
|
||||
var html = [];
|
||||
// TODO(vojta): use $sanitize instead
|
||||
var writer = htmlSanitizeWriter(html);
|
||||
var url;
|
||||
var i;
|
||||
var properties = {};
|
||||
if (angular.isDefined(target)) {
|
||||
properties.target = target;
|
||||
}
|
||||
while ((match = raw.match(LINKY_URL_REGEXP))) {
|
||||
// We can not end in these as they are sometimes found at the end of the sentence
|
||||
url = match[0];
|
||||
// if we did not match ftp/http/mailto then assume mailto
|
||||
if (match[2] == match[3]) url = 'mailto:' + url;
|
||||
i = match.index;
|
||||
writer.chars(raw.substr(0, i));
|
||||
properties.href = url;
|
||||
writer.start('a', properties);
|
||||
writer.chars(match[0].replace(MAILTO_REGEXP, ''));
|
||||
writer.end('a');
|
||||
addText(raw.substr(0, i));
|
||||
addLink(url, match[0].replace(MAILTO_REGEXP, ''));
|
||||
raw = raw.substring(i + match[0].length);
|
||||
}
|
||||
writer.chars(raw);
|
||||
return html.join('');
|
||||
addText(raw);
|
||||
return $sanitize(html.join(''));
|
||||
|
||||
function addText(text) {
|
||||
if (!text) {
|
||||
return;
|
||||
}
|
||||
html.push(sanitizeText(text));
|
||||
}
|
||||
|
||||
function addLink(url, text) {
|
||||
html.push('<a ');
|
||||
if (angular.isDefined(target)) {
|
||||
html.push('target="');
|
||||
html.push(target);
|
||||
html.push('" ');
|
||||
}
|
||||
html.push('href="');
|
||||
html.push(url);
|
||||
html.push('">');
|
||||
addText(text);
|
||||
html.push('</a>');
|
||||
}
|
||||
};
|
||||
});
|
||||
}]);
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@ var $sanitizeMinErr = angular.$$minErr('$sanitize');
|
|||
* it into the returned string, however, since our parser is more strict than a typical browser
|
||||
* parser, it's possible that some obscure input, which would be recognized as valid HTML by a
|
||||
* browser, won't make it through the sanitizer.
|
||||
* The whitelist is configured using the functions `aHrefSanitizationWhitelist` and
|
||||
* `imgSrcSanitizationWhitelist` of {@link ng.$compileProvider `$compileProvider`}.
|
||||
*
|
||||
* @param {string} html Html input.
|
||||
* @returns {string} Sanitized html.
|
||||
|
|
@ -128,11 +130,24 @@ var $sanitizeMinErr = angular.$$minErr('$sanitize');
|
|||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
var $sanitize = function(html) {
|
||||
function $SanitizeProvider() {
|
||||
this.$get = ['$$sanitizeUri', function($$sanitizeUri) {
|
||||
return function(html) {
|
||||
var buf = [];
|
||||
htmlParser(html, htmlSanitizeWriter(buf, function(uri, isImage) {
|
||||
return !/^unsafe/.test($$sanitizeUri(uri, isImage));
|
||||
}));
|
||||
return buf.join('');
|
||||
};
|
||||
}];
|
||||
}
|
||||
|
||||
function sanitizeText(chars) {
|
||||
var buf = [];
|
||||
htmlParser(html, htmlSanitizeWriter(buf));
|
||||
return buf.join('');
|
||||
};
|
||||
var writer = htmlSanitizeWriter(buf, angular.noop);
|
||||
writer.chars(chars);
|
||||
return buf.join('');
|
||||
}
|
||||
|
||||
|
||||
// Regular Expressions for parsing tags and attributes
|
||||
|
|
@ -145,7 +160,6 @@ var START_TAG_REGEXP =
|
|||
COMMENT_REGEXP = /<!--(.*?)-->/g,
|
||||
DOCTYPE_REGEXP = /<!DOCTYPE([^>]*?)>/i,
|
||||
CDATA_REGEXP = /<!\[CDATA\[(.*?)]]>/g,
|
||||
URI_REGEXP = /^((ftp|https?):\/\/|mailto:|tel:|#)/i,
|
||||
// Match everything outside of normal chars and " (quote character)
|
||||
NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g;
|
||||
|
||||
|
|
@ -353,8 +367,18 @@ function htmlParser( html, handler ) {
|
|||
*/
|
||||
var hiddenPre=document.createElement("pre");
|
||||
function decodeEntities(value) {
|
||||
hiddenPre.innerHTML=value.replace(/</g,"<");
|
||||
return hiddenPre.innerText || hiddenPre.textContent || '';
|
||||
if (!value) {
|
||||
return '';
|
||||
}
|
||||
// Note: IE8 does not preserve spaces at the start/end of innerHTML
|
||||
var spaceRe = /^(\s*)([\s\S]*?)(\s*)$/;
|
||||
var parts = spaceRe.exec(value);
|
||||
parts[0] = '';
|
||||
if (parts[2]) {
|
||||
hiddenPre.innerHTML=parts[2].replace(/</g,"<");
|
||||
parts[2] = hiddenPre.innerText || hiddenPre.textContent;
|
||||
}
|
||||
return parts.join('');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -384,7 +408,7 @@ function encodeEntities(value) {
|
|||
* comment: function(text) {}
|
||||
* }
|
||||
*/
|
||||
function htmlSanitizeWriter(buf){
|
||||
function htmlSanitizeWriter(buf, uriValidator){
|
||||
var ignore = false;
|
||||
var out = angular.bind(buf, buf.push);
|
||||
return {
|
||||
|
|
@ -398,7 +422,9 @@ function htmlSanitizeWriter(buf){
|
|||
out(tag);
|
||||
angular.forEach(attrs, function(value, key){
|
||||
var lkey=angular.lowercase(key);
|
||||
if (validAttrs[lkey]===true && (uriAttrs[lkey]!==true || value.match(URI_REGEXP))) {
|
||||
var isImage = (tag === 'img' && lkey === 'src') || (lkey === 'background');
|
||||
if (validAttrs[lkey] === true &&
|
||||
(uriAttrs[lkey] !== true || uriValidator(value, isImage))) {
|
||||
out(' ');
|
||||
out(key);
|
||||
out('="');
|
||||
|
|
@ -430,4 +456,4 @@ function htmlSanitizeWriter(buf){
|
|||
|
||||
|
||||
// define ngSanitize module and register $sanitize service
|
||||
angular.module('ngSanitize', []).value('$sanitize', $sanitize);
|
||||
angular.module('ngSanitize', []).provider('$sanitize', $SanitizeProvider);
|
||||
|
|
|
|||
|
|
@ -3834,6 +3834,7 @@ describe('$compile', function() {
|
|||
|
||||
|
||||
describe('img[src] sanitization', function() {
|
||||
|
||||
it('should NOT require trusted values for img src', inject(function($rootScope, $compile, $sce) {
|
||||
element = $compile('<img src="{{testUrl}}"></img>')($rootScope);
|
||||
$rootScope.testUrl = 'http://example.com/image.png';
|
||||
|
|
@ -3845,127 +3846,6 @@ describe('$compile', function() {
|
|||
expect(element.attr('src')).toEqual('http://example.com/image2.png');
|
||||
}));
|
||||
|
||||
it('should sanitize javascript: urls', inject(function($compile, $rootScope) {
|
||||
element = $compile('<img src="{{testUrl}}"></a>')($rootScope);
|
||||
$rootScope.testUrl = "javascript:doEvilStuff()";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('src')).toBe('unsafe:javascript:doEvilStuff()');
|
||||
}));
|
||||
|
||||
it('should sanitize non-image data: urls', inject(function($compile, $rootScope) {
|
||||
element = $compile('<img src="{{testUrl}}"></a>')($rootScope);
|
||||
$rootScope.testUrl = "data:application/javascript;charset=US-ASCII,alert('evil!');";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('src')).toBe("unsafe:data:application/javascript;charset=US-ASCII,alert('evil!');");
|
||||
$rootScope.testUrl = "data:,foo";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('src')).toBe("unsafe:data:,foo");
|
||||
}));
|
||||
|
||||
|
||||
it('should not sanitize data: URIs for images', inject(function($compile, $rootScope) {
|
||||
element = $compile('<img src="{{dataUri}}"></img>')($rootScope);
|
||||
|
||||
// image data uri
|
||||
// ref: http://probablyprogramming.com/2009/03/15/the-tiniest-gif-ever
|
||||
$rootScope.dataUri = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('src')).toBe('data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==');
|
||||
}));
|
||||
|
||||
|
||||
// Fails on IE <= 10 with "TypeError: Access is denied" when trying to set img[src]
|
||||
if (!msie || msie > 10) {
|
||||
it('should sanitize mailto: urls', inject(function($compile, $rootScope) {
|
||||
element = $compile('<img src="{{testUrl}}"></a>')($rootScope);
|
||||
$rootScope.testUrl = "mailto:foo@bar.com";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('src')).toBe('unsafe:mailto:foo@bar.com');
|
||||
}));
|
||||
}
|
||||
|
||||
it('should sanitize obfuscated javascript: urls', inject(function($compile, $rootScope) {
|
||||
element = $compile('<img src="{{testUrl}}"></img>')($rootScope);
|
||||
|
||||
// case-sensitive
|
||||
$rootScope.testUrl = "JaVaScRiPt:doEvilStuff()";
|
||||
$rootScope.$apply();
|
||||
expect(element[0].src).toBe('unsafe:javascript:doEvilStuff()');
|
||||
|
||||
// tab in protocol
|
||||
$rootScope.testUrl = "java\u0009script:doEvilStuff()";
|
||||
$rootScope.$apply();
|
||||
expect(element[0].src).toMatch(/(http:\/\/|unsafe:javascript:doEvilStuff\(\))/);
|
||||
|
||||
// space before
|
||||
$rootScope.testUrl = " javascript:doEvilStuff()";
|
||||
$rootScope.$apply();
|
||||
expect(element[0].src).toBe('unsafe:javascript:doEvilStuff()');
|
||||
|
||||
// ws chars before
|
||||
$rootScope.testUrl = " \u000e javascript:doEvilStuff()";
|
||||
$rootScope.$apply();
|
||||
expect(element[0].src).toMatch(/(http:\/\/|unsafe:javascript:doEvilStuff\(\))/);
|
||||
|
||||
// post-fixed with proper url
|
||||
$rootScope.testUrl = "javascript:doEvilStuff(); http://make.me/look/good";
|
||||
$rootScope.$apply();
|
||||
expect(element[0].src).toBeOneOf(
|
||||
'unsafe:javascript:doEvilStuff(); http://make.me/look/good',
|
||||
'unsafe:javascript:doEvilStuff();%20http://make.me/look/good'
|
||||
);
|
||||
}));
|
||||
|
||||
it('should sanitize ng-src bindings as well', inject(function($compile, $rootScope) {
|
||||
element = $compile('<img ng-src="{{testUrl}}"></img>')($rootScope);
|
||||
$rootScope.testUrl = "javascript:doEvilStuff()";
|
||||
$rootScope.$apply();
|
||||
|
||||
expect(element[0].src).toBe('unsafe:javascript:doEvilStuff()');
|
||||
}));
|
||||
|
||||
|
||||
it('should not sanitize valid urls', inject(function($compile, $rootScope) {
|
||||
element = $compile('<img src="{{testUrl}}"></img>')($rootScope);
|
||||
|
||||
$rootScope.testUrl = "foo/bar";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('src')).toBe('foo/bar');
|
||||
|
||||
$rootScope.testUrl = "/foo/bar";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('src')).toBe('/foo/bar');
|
||||
|
||||
$rootScope.testUrl = "../foo/bar";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('src')).toBe('../foo/bar');
|
||||
|
||||
$rootScope.testUrl = "#foo";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('src')).toBe('#foo');
|
||||
|
||||
$rootScope.testUrl = "http://foo.com/bar";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('src')).toBe('http://foo.com/bar');
|
||||
|
||||
$rootScope.testUrl = " http://foo.com/bar";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('src')).toBe(' http://foo.com/bar');
|
||||
|
||||
$rootScope.testUrl = "https://foo.com/bar";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('src')).toBe('https://foo.com/bar');
|
||||
|
||||
$rootScope.testUrl = "ftp://foo.com/bar";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('src')).toBe('ftp://foo.com/bar');
|
||||
|
||||
$rootScope.testUrl = "file:///foo/bar.html";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('src')).toBe('file:///foo/bar.html');
|
||||
}));
|
||||
|
||||
|
||||
it('should not sanitize attributes other than src', inject(function($compile, $rootScope) {
|
||||
element = $compile('<img title="{{testUrl}}"></img>')($rootScope);
|
||||
$rootScope.testUrl = "javascript:doEvilStuff()";
|
||||
|
|
@ -3974,141 +3854,42 @@ describe('$compile', function() {
|
|||
expect(element.attr('title')).toBe('javascript:doEvilStuff()');
|
||||
}));
|
||||
|
||||
it('should use $$sanitizeUriProvider for reconfiguration of the src whitelist', function() {
|
||||
module(function($compileProvider, $$sanitizeUriProvider) {
|
||||
var newRe = /javascript:/,
|
||||
returnVal;
|
||||
expect($compileProvider.imgSrcSanitizationWhitelist()).toBe($$sanitizeUriProvider.imgSrcSanitizationWhitelist());
|
||||
|
||||
it('should allow reconfiguration of the src whitelist', function() {
|
||||
module(function($compileProvider) {
|
||||
expect($compileProvider.imgSrcSanitizationWhitelist() instanceof RegExp).toBe(true);
|
||||
var returnVal = $compileProvider.imgSrcSanitizationWhitelist(/javascript:/);
|
||||
returnVal = $compileProvider.imgSrcSanitizationWhitelist(newRe);
|
||||
expect(returnVal).toBe($compileProvider);
|
||||
expect($$sanitizeUriProvider.imgSrcSanitizationWhitelist()).toBe(newRe);
|
||||
expect($compileProvider.imgSrcSanitizationWhitelist()).toBe(newRe);
|
||||
});
|
||||
|
||||
inject(function($compile, $rootScope) {
|
||||
element = $compile('<img src="{{testUrl}}"></img>')($rootScope);
|
||||
|
||||
// Fails on IE <= 11 with "TypeError: Object doesn't support this property or method" when
|
||||
// trying to set img[src]
|
||||
if (!msie || msie > 11) {
|
||||
$rootScope.testUrl = "javascript:doEvilStuff()";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('src')).toBe('javascript:doEvilStuff()');
|
||||
}
|
||||
|
||||
$rootScope.testUrl = "http://recon/figured";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('src')).toBe('unsafe:http://recon/figured');
|
||||
inject(function() {
|
||||
// needed to the module definition above is run...
|
||||
});
|
||||
});
|
||||
|
||||
it('should use $$sanitizeUri', function() {
|
||||
var $$sanitizeUri = jasmine.createSpy('$$sanitizeUri');
|
||||
module(function($provide) {
|
||||
$provide.value('$$sanitizeUri', $$sanitizeUri);
|
||||
});
|
||||
inject(function($compile, $rootScope) {
|
||||
element = $compile('<img src="{{testUrl}}"></img>')($rootScope);
|
||||
$rootScope.testUrl = "someUrl";
|
||||
|
||||
$$sanitizeUri.andReturn('someSanitizedUrl');
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('src')).toBe('someSanitizedUrl');
|
||||
expect($$sanitizeUri).toHaveBeenCalledWith($rootScope.testUrl, true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('a[href] sanitization', function() {
|
||||
|
||||
it('should sanitize javascript: urls', inject(function($compile, $rootScope) {
|
||||
element = $compile('<a href="{{testUrl}}"></a>')($rootScope);
|
||||
$rootScope.testUrl = "javascript:doEvilStuff()";
|
||||
$rootScope.$apply();
|
||||
|
||||
expect(element.attr('href')).toBe('unsafe:javascript:doEvilStuff()');
|
||||
}));
|
||||
|
||||
|
||||
it('should sanitize data: urls', inject(function($compile, $rootScope) {
|
||||
element = $compile('<a href="{{testUrl}}"></a>')($rootScope);
|
||||
$rootScope.testUrl = "data:evilPayload";
|
||||
$rootScope.$apply();
|
||||
|
||||
expect(element.attr('href')).toBe('unsafe:data:evilPayload');
|
||||
}));
|
||||
|
||||
|
||||
it('should sanitize obfuscated javascript: urls', inject(function($compile, $rootScope) {
|
||||
element = $compile('<a href="{{testUrl}}"></a>')($rootScope);
|
||||
|
||||
// case-sensitive
|
||||
$rootScope.testUrl = "JaVaScRiPt:doEvilStuff()";
|
||||
$rootScope.$apply();
|
||||
expect(element[0].href).toBe('unsafe:javascript:doEvilStuff()');
|
||||
|
||||
// tab in protocol
|
||||
$rootScope.testUrl = "java\u0009script:doEvilStuff()";
|
||||
$rootScope.$apply();
|
||||
expect(element[0].href).toMatch(/(http:\/\/|unsafe:javascript:doEvilStuff\(\))/);
|
||||
|
||||
// space before
|
||||
$rootScope.testUrl = " javascript:doEvilStuff()";
|
||||
$rootScope.$apply();
|
||||
expect(element[0].href).toBe('unsafe:javascript:doEvilStuff()');
|
||||
|
||||
// ws chars before
|
||||
$rootScope.testUrl = " \u000e javascript:doEvilStuff()";
|
||||
$rootScope.$apply();
|
||||
expect(element[0].href).toMatch(/(http:\/\/|unsafe:javascript:doEvilStuff\(\))/);
|
||||
|
||||
// post-fixed with proper url
|
||||
$rootScope.testUrl = "javascript:doEvilStuff(); http://make.me/look/good";
|
||||
$rootScope.$apply();
|
||||
expect(element[0].href).toBeOneOf(
|
||||
'unsafe:javascript:doEvilStuff(); http://make.me/look/good',
|
||||
'unsafe:javascript:doEvilStuff();%20http://make.me/look/good'
|
||||
);
|
||||
}));
|
||||
|
||||
|
||||
it('should sanitize ngHref bindings as well', inject(function($compile, $rootScope) {
|
||||
element = $compile('<a ng-href="{{testUrl}}"></a>')($rootScope);
|
||||
$rootScope.testUrl = "javascript:doEvilStuff()";
|
||||
$rootScope.$apply();
|
||||
|
||||
expect(element[0].href).toBe('unsafe:javascript:doEvilStuff()');
|
||||
}));
|
||||
|
||||
|
||||
it('should not sanitize valid urls', inject(function($compile, $rootScope) {
|
||||
element = $compile('<a href="{{testUrl}}"></a>')($rootScope);
|
||||
|
||||
$rootScope.testUrl = "foo/bar";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('href')).toBe('foo/bar');
|
||||
|
||||
$rootScope.testUrl = "/foo/bar";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('href')).toBe('/foo/bar');
|
||||
|
||||
$rootScope.testUrl = "../foo/bar";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('href')).toBe('../foo/bar');
|
||||
|
||||
$rootScope.testUrl = "#foo";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('href')).toBe('#foo');
|
||||
|
||||
$rootScope.testUrl = "http://foo/bar";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('href')).toBe('http://foo/bar');
|
||||
|
||||
$rootScope.testUrl = " http://foo/bar";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('href')).toBe(' http://foo/bar');
|
||||
|
||||
$rootScope.testUrl = "https://foo/bar";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('href')).toBe('https://foo/bar');
|
||||
|
||||
$rootScope.testUrl = "ftp://foo/bar";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('href')).toBe('ftp://foo/bar');
|
||||
|
||||
$rootScope.testUrl = "mailto:foo@bar.com";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('href')).toBe('mailto:foo@bar.com');
|
||||
|
||||
$rootScope.testUrl = "file:///foo/bar.html";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('href')).toBe('file:///foo/bar.html');
|
||||
}));
|
||||
|
||||
|
||||
it('should not sanitize href on elements other than anchor', inject(function($compile, $rootScope) {
|
||||
element = $compile('<div href="{{testUrl}}"></div>')($rootScope);
|
||||
$rootScope.testUrl = "javascript:doEvilStuff()";
|
||||
|
|
@ -4117,7 +3898,6 @@ describe('$compile', function() {
|
|||
expect(element.attr('href')).toBe('javascript:doEvilStuff()');
|
||||
}));
|
||||
|
||||
|
||||
it('should not sanitize attributes other than href', inject(function($compile, $rootScope) {
|
||||
element = $compile('<a title="{{testUrl}}"></a>')($rootScope);
|
||||
$rootScope.testUrl = "javascript:doEvilStuff()";
|
||||
|
|
@ -4126,26 +3906,38 @@ describe('$compile', function() {
|
|||
expect(element.attr('title')).toBe('javascript:doEvilStuff()');
|
||||
}));
|
||||
|
||||
it('should use $$sanitizeUriProvider for reconfiguration of the href whitelist', function() {
|
||||
module(function($compileProvider, $$sanitizeUriProvider) {
|
||||
var newRe = /javascript:/,
|
||||
returnVal;
|
||||
expect($compileProvider.aHrefSanitizationWhitelist()).toBe($$sanitizeUriProvider.aHrefSanitizationWhitelist());
|
||||
|
||||
it('should allow reconfiguration of the href whitelist', function() {
|
||||
module(function($compileProvider) {
|
||||
expect($compileProvider.aHrefSanitizationWhitelist() instanceof RegExp).toBe(true);
|
||||
var returnVal = $compileProvider.aHrefSanitizationWhitelist(/javascript:/);
|
||||
returnVal = $compileProvider.aHrefSanitizationWhitelist(newRe);
|
||||
expect(returnVal).toBe($compileProvider);
|
||||
expect($$sanitizeUriProvider.aHrefSanitizationWhitelist()).toBe(newRe);
|
||||
expect($compileProvider.aHrefSanitizationWhitelist()).toBe(newRe);
|
||||
});
|
||||
|
||||
inject(function($compile, $rootScope) {
|
||||
element = $compile('<a href="{{testUrl}}"></a>')($rootScope);
|
||||
|
||||
$rootScope.testUrl = "javascript:doEvilStuff()";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('href')).toBe('javascript:doEvilStuff()');
|
||||
|
||||
$rootScope.testUrl = "http://recon/figured";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('href')).toBe('unsafe:http://recon/figured');
|
||||
inject(function() {
|
||||
// needed to the module definition above is run...
|
||||
});
|
||||
});
|
||||
|
||||
it('should use $$sanitizeUri', function() {
|
||||
var $$sanitizeUri = jasmine.createSpy('$$sanitizeUri');
|
||||
module(function($provide) {
|
||||
$provide.value('$$sanitizeUri', $$sanitizeUri);
|
||||
});
|
||||
inject(function($compile, $rootScope) {
|
||||
element = $compile('<a href="{{testUrl}}"></a>')($rootScope);
|
||||
$rootScope.testUrl = "someUrl";
|
||||
|
||||
$$sanitizeUri.andReturn('someSanitizedUrl');
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('href')).toBe('someSanitizedUrl');
|
||||
expect($$sanitizeUri).toHaveBeenCalledWith($rootScope.testUrl, false);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('interpolation on HTML DOM event handler attributes onclick, onXYZ, formaction', function() {
|
||||
|
|
|
|||
230
test/ng/sanitizeUriSpec.js
Normal file
230
test/ng/sanitizeUriSpec.js
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
'use strict';
|
||||
|
||||
describe('sanitizeUri', function() {
|
||||
var sanitizeHref, sanitizeImg, sanitizeUriProvider, testUrl;
|
||||
beforeEach(function() {
|
||||
module(function(_$$sanitizeUriProvider_) {
|
||||
sanitizeUriProvider = _$$sanitizeUriProvider_;
|
||||
});
|
||||
inject(function($$sanitizeUri) {
|
||||
sanitizeHref = function(uri) {
|
||||
return $$sanitizeUri(uri, false);
|
||||
};
|
||||
sanitizeImg = function(uri) {
|
||||
return $$sanitizeUri(uri, true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function isEvilInCurrentBrowser(uri) {
|
||||
var a = document.createElement('a');
|
||||
a.setAttribute('href', uri);
|
||||
return a.href.substring(0, 4) !== 'http';
|
||||
}
|
||||
|
||||
describe('img[src] sanitization', function() {
|
||||
|
||||
it('should sanitize javascript: urls', function() {
|
||||
testUrl = "javascript:doEvilStuff()";
|
||||
expect(sanitizeImg(testUrl)).toBe('unsafe:javascript:doEvilStuff()');
|
||||
});
|
||||
|
||||
it('should sanitize non-image data: urls', function() {
|
||||
testUrl = "data:application/javascript;charset=US-ASCII,alert('evil!');";
|
||||
expect(sanitizeImg(testUrl)).toBe("unsafe:data:application/javascript;charset=US-ASCII,alert('evil!');");
|
||||
|
||||
testUrl = "data:,foo";
|
||||
expect(sanitizeImg(testUrl)).toBe("unsafe:data:,foo");
|
||||
});
|
||||
|
||||
it('should not sanitize data: URIs for images', function() {
|
||||
// image data uri
|
||||
// ref: http://probablyprogramming.com/2009/03/15/the-tiniest-gif-ever
|
||||
testUrl = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
|
||||
expect(sanitizeImg(testUrl)).toBe('data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==');
|
||||
});
|
||||
|
||||
it('should sanitize mailto: urls', function() {
|
||||
testUrl = "mailto:foo@bar.com";
|
||||
expect(sanitizeImg(testUrl)).toBe('unsafe:mailto:foo@bar.com');
|
||||
});
|
||||
|
||||
it('should sanitize obfuscated javascript: urls', function() {
|
||||
// case-sensitive
|
||||
testUrl = "JaVaScRiPt:doEvilStuff()";
|
||||
expect(sanitizeImg(testUrl)).toBe('unsafe:javascript:doEvilStuff()');
|
||||
|
||||
// tab in protocol
|
||||
testUrl = "java\u0009script:doEvilStuff()";
|
||||
if (isEvilInCurrentBrowser(testUrl)) {
|
||||
expect(sanitizeImg(testUrl)).toEqual('unsafe:javascript:doEvilStuff()');
|
||||
}
|
||||
|
||||
// space before
|
||||
testUrl = " javascript:doEvilStuff()";
|
||||
expect(sanitizeImg(testUrl)).toBe('unsafe:javascript:doEvilStuff()');
|
||||
|
||||
// ws chars before
|
||||
testUrl = " \u000e javascript:doEvilStuff()";
|
||||
if (isEvilInCurrentBrowser(testUrl)) {
|
||||
expect(sanitizeImg(testUrl)).toEqual('unsafe:javascript:doEvilStuff()');
|
||||
}
|
||||
|
||||
// post-fixed with proper url
|
||||
testUrl = "javascript:doEvilStuff(); http://make.me/look/good";
|
||||
expect(sanitizeImg(testUrl)).toBeOneOf(
|
||||
'unsafe:javascript:doEvilStuff(); http://make.me/look/good',
|
||||
'unsafe:javascript:doEvilStuff();%20http://make.me/look/good'
|
||||
);
|
||||
});
|
||||
|
||||
it('should sanitize ng-src bindings as well', function() {
|
||||
testUrl = "javascript:doEvilStuff()";
|
||||
expect(sanitizeImg(testUrl)).toBe('unsafe:javascript:doEvilStuff()');
|
||||
});
|
||||
|
||||
|
||||
it('should not sanitize valid urls', function() {
|
||||
testUrl = "foo/bar";
|
||||
expect(sanitizeImg(testUrl)).toBe('foo/bar');
|
||||
|
||||
testUrl = "/foo/bar";
|
||||
expect(sanitizeImg(testUrl)).toBe('/foo/bar');
|
||||
|
||||
testUrl = "../foo/bar";
|
||||
expect(sanitizeImg(testUrl)).toBe('../foo/bar');
|
||||
|
||||
testUrl = "#foo";
|
||||
expect(sanitizeImg(testUrl)).toBe('#foo');
|
||||
|
||||
testUrl = "http://foo.com/bar";
|
||||
expect(sanitizeImg(testUrl)).toBe('http://foo.com/bar');
|
||||
|
||||
testUrl = " http://foo.com/bar";
|
||||
expect(sanitizeImg(testUrl)).toBe(' http://foo.com/bar');
|
||||
|
||||
testUrl = "https://foo.com/bar";
|
||||
expect(sanitizeImg(testUrl)).toBe('https://foo.com/bar');
|
||||
|
||||
testUrl = "ftp://foo.com/bar";
|
||||
expect(sanitizeImg(testUrl)).toBe('ftp://foo.com/bar');
|
||||
|
||||
testUrl = "file:///foo/bar.html";
|
||||
expect(sanitizeImg(testUrl)).toBe('file:///foo/bar.html');
|
||||
});
|
||||
|
||||
|
||||
it('should allow reconfiguration of the src whitelist', function() {
|
||||
var returnVal;
|
||||
expect(sanitizeUriProvider.imgSrcSanitizationWhitelist() instanceof RegExp).toBe(true);
|
||||
returnVal = sanitizeUriProvider.imgSrcSanitizationWhitelist(/javascript:/);
|
||||
expect(returnVal).toBe(sanitizeUriProvider);
|
||||
|
||||
testUrl = "javascript:doEvilStuff()";
|
||||
expect(sanitizeImg(testUrl)).toBe('javascript:doEvilStuff()');
|
||||
|
||||
testUrl = "http://recon/figured";
|
||||
expect(sanitizeImg(testUrl)).toBe('unsafe:http://recon/figured');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('a[href] sanitization', function() {
|
||||
|
||||
it('should sanitize javascript: urls', inject(function() {
|
||||
testUrl = "javascript:doEvilStuff()";
|
||||
expect(sanitizeHref(testUrl)).toBe('unsafe:javascript:doEvilStuff()');
|
||||
}));
|
||||
|
||||
|
||||
it('should sanitize data: urls', inject(function() {
|
||||
testUrl = "data:evilPayload";
|
||||
expect(sanitizeHref(testUrl)).toBe('unsafe:data:evilPayload');
|
||||
}));
|
||||
|
||||
|
||||
it('should sanitize obfuscated javascript: urls', inject(function() {
|
||||
// case-sensitive
|
||||
testUrl = "JaVaScRiPt:doEvilStuff()";
|
||||
expect(sanitizeHref(testUrl)).toBe('unsafe:javascript:doEvilStuff()');
|
||||
|
||||
// tab in protocol
|
||||
testUrl = "java\u0009script:doEvilStuff()";
|
||||
if (isEvilInCurrentBrowser(testUrl)) {
|
||||
expect(sanitizeHref(testUrl)).toEqual('unsafe:javascript:doEvilStuff()');
|
||||
}
|
||||
|
||||
// space before
|
||||
testUrl = " javascript:doEvilStuff()";
|
||||
expect(sanitizeHref(testUrl)).toBe('unsafe:javascript:doEvilStuff()');
|
||||
|
||||
// ws chars before
|
||||
testUrl = " \u000e javascript:doEvilStuff()";
|
||||
if (isEvilInCurrentBrowser(testUrl)) {
|
||||
expect(sanitizeHref(testUrl)).toEqual('unsafe:javascript:doEvilStuff()');
|
||||
}
|
||||
|
||||
// post-fixed with proper url
|
||||
testUrl = "javascript:doEvilStuff(); http://make.me/look/good";
|
||||
expect(sanitizeHref(testUrl)).toBeOneOf(
|
||||
'unsafe:javascript:doEvilStuff(); http://make.me/look/good',
|
||||
'unsafe:javascript:doEvilStuff();%20http://make.me/look/good'
|
||||
);
|
||||
}));
|
||||
|
||||
|
||||
it('should sanitize ngHref bindings as well', inject(function() {
|
||||
testUrl = "javascript:doEvilStuff()";
|
||||
expect(sanitizeHref(testUrl)).toBe('unsafe:javascript:doEvilStuff()');
|
||||
}));
|
||||
|
||||
|
||||
it('should not sanitize valid urls', inject(function() {
|
||||
testUrl = "foo/bar";
|
||||
expect(sanitizeHref(testUrl)).toBe('foo/bar');
|
||||
|
||||
testUrl = "/foo/bar";
|
||||
expect(sanitizeHref(testUrl)).toBe('/foo/bar');
|
||||
|
||||
testUrl = "../foo/bar";
|
||||
expect(sanitizeHref(testUrl)).toBe('../foo/bar');
|
||||
|
||||
testUrl = "#foo";
|
||||
expect(sanitizeHref(testUrl)).toBe('#foo');
|
||||
|
||||
testUrl = "http://foo/bar";
|
||||
expect(sanitizeHref(testUrl)).toBe('http://foo/bar');
|
||||
|
||||
testUrl = " http://foo/bar";
|
||||
expect(sanitizeHref(testUrl)).toBe(' http://foo/bar');
|
||||
|
||||
testUrl = "https://foo/bar";
|
||||
expect(sanitizeHref(testUrl)).toBe('https://foo/bar');
|
||||
|
||||
testUrl = "ftp://foo/bar";
|
||||
expect(sanitizeHref(testUrl)).toBe('ftp://foo/bar');
|
||||
|
||||
testUrl = "mailto:foo@bar.com";
|
||||
expect(sanitizeHref(testUrl)).toBe('mailto:foo@bar.com');
|
||||
|
||||
testUrl = "file:///foo/bar.html";
|
||||
expect(sanitizeHref(testUrl)).toBe('file:///foo/bar.html');
|
||||
}));
|
||||
|
||||
it('should allow reconfiguration of the href whitelist', function() {
|
||||
var returnVal;
|
||||
expect(sanitizeUriProvider.aHrefSanitizationWhitelist() instanceof RegExp).toBe(true);
|
||||
returnVal = sanitizeUriProvider.aHrefSanitizationWhitelist(/javascript:/);
|
||||
expect(returnVal).toBe(sanitizeUriProvider);
|
||||
|
||||
testUrl = "javascript:doEvilStuff()";
|
||||
expect(sanitizeHref(testUrl)).toBe('javascript:doEvilStuff()');
|
||||
|
||||
testUrl = "http://recon/figured";
|
||||
expect(sanitizeHref(testUrl)).toBe('unsafe:http://recon/figured');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
|
@ -5,12 +5,15 @@ describe('HTML', function() {
|
|||
var expectHTML;
|
||||
|
||||
beforeEach(module('ngSanitize'));
|
||||
|
||||
beforeEach(inject(function($sanitize) {
|
||||
beforeEach(function() {
|
||||
expectHTML = function(html){
|
||||
return expect($sanitize(html));
|
||||
var sanitize;
|
||||
inject(function($sanitize) {
|
||||
sanitize = $sanitize;
|
||||
});
|
||||
return expect(sanitize(html));
|
||||
};
|
||||
}));
|
||||
});
|
||||
|
||||
describe('htmlParser', function() {
|
||||
if (angular.isUndefined(window.htmlParser)) return;
|
||||
|
|
@ -183,13 +186,22 @@ describe('HTML', function() {
|
|||
toEqual('');
|
||||
});
|
||||
|
||||
it('should keep spaces as prefix/postfix', function() {
|
||||
expectHTML(' a ').toEqual(' a ');
|
||||
});
|
||||
|
||||
it('should allow multiline strings', function() {
|
||||
expectHTML('\na\n').toEqual(' a\ ');
|
||||
});
|
||||
|
||||
describe('htmlSanitizerWriter', function() {
|
||||
if (angular.isUndefined(window.htmlSanitizeWriter)) return;
|
||||
|
||||
var writer, html;
|
||||
var writer, html, uriValidator;
|
||||
beforeEach(function() {
|
||||
html = '';
|
||||
writer = htmlSanitizeWriter({push:function(text){html+=text;}});
|
||||
uriValidator = jasmine.createSpy('uriValidator');
|
||||
writer = htmlSanitizeWriter({push:function(text){html+=text;}}, uriValidator);
|
||||
});
|
||||
|
||||
it('should write basic HTML', function() {
|
||||
|
|
@ -258,41 +270,106 @@ describe('HTML', function() {
|
|||
});
|
||||
});
|
||||
|
||||
describe('isUri', function() {
|
||||
|
||||
function isUri(value) {
|
||||
return value.match(URI_REGEXP);
|
||||
}
|
||||
|
||||
it('should be URI', function() {
|
||||
expect(isUri('http://abc')).toBeTruthy();
|
||||
expect(isUri('HTTP://abc')).toBeTruthy();
|
||||
expect(isUri('https://abc')).toBeTruthy();
|
||||
expect(isUri('HTTPS://abc')).toBeTruthy();
|
||||
expect(isUri('ftp://abc')).toBeTruthy();
|
||||
expect(isUri('FTP://abc')).toBeTruthy();
|
||||
expect(isUri('mailto:me@example.com')).toBeTruthy();
|
||||
expect(isUri('MAILTO:me@example.com')).toBeTruthy();
|
||||
expect(isUri('tel:123-123-1234')).toBeTruthy();
|
||||
expect(isUri('TEL:123-123-1234')).toBeTruthy();
|
||||
expect(isUri('#anchor')).toBeTruthy();
|
||||
describe('uri validation', function() {
|
||||
it('should call the uri validator', function() {
|
||||
writer.start('a', {href:'someUrl'}, false);
|
||||
expect(uriValidator).toHaveBeenCalledWith('someUrl', false);
|
||||
uriValidator.reset();
|
||||
writer.start('img', {src:'someImgUrl'}, false);
|
||||
expect(uriValidator).toHaveBeenCalledWith('someImgUrl', true);
|
||||
uriValidator.reset();
|
||||
writer.start('someTag', {src:'someNonUrl'}, false);
|
||||
expect(uriValidator).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not be URI', function() {
|
||||
expect(isUri('')).toBeFalsy();
|
||||
expect(isUri('javascript:alert')).toBeFalsy();
|
||||
it('should drop non valid uri attributes', function() {
|
||||
uriValidator.andReturn(false);
|
||||
writer.start('a', {href:'someUrl'}, false);
|
||||
expect(html).toEqual('<a>');
|
||||
|
||||
html = '';
|
||||
uriValidator.andReturn(true);
|
||||
writer.start('a', {href:'someUrl'}, false);
|
||||
expect(html).toEqual('<a href="someUrl">');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('uri checking', function() {
|
||||
beforeEach(function() {
|
||||
this.addMatchers({
|
||||
toBeValidUrl: function() {
|
||||
var sanitize;
|
||||
inject(function($sanitize) {
|
||||
sanitize = $sanitize;
|
||||
});
|
||||
var input = '<a href="'+this.actual+'"></a>';
|
||||
return sanitize(input) === input;
|
||||
},
|
||||
toBeValidImageSrc: function() {
|
||||
var sanitize;
|
||||
inject(function($sanitize) {
|
||||
sanitize = $sanitize;
|
||||
});
|
||||
var input = '<img src="'+this.actual+'"/>';
|
||||
return sanitize(input) === input;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('javascript URL attribute', function() {
|
||||
beforeEach(function() {
|
||||
this.addMatchers({
|
||||
toBeValidUrl: function() {
|
||||
return URI_REGEXP.exec(this.actual);
|
||||
}
|
||||
});
|
||||
it('should use $$sanitizeUri for links', function() {
|
||||
var $$sanitizeUri = jasmine.createSpy('$$sanitizeUri');
|
||||
module(function($provide) {
|
||||
$provide.value('$$sanitizeUri', $$sanitizeUri);
|
||||
});
|
||||
inject(function() {
|
||||
$$sanitizeUri.andReturn('someUri');
|
||||
|
||||
expectHTML('<a href="someUri"></a>').toEqual('<a href="someUri"></a>');
|
||||
expect($$sanitizeUri).toHaveBeenCalledWith('someUri', false);
|
||||
|
||||
$$sanitizeUri.andReturn('unsafe:someUri');
|
||||
expectHTML('<a href="someUri"></a>').toEqual('<a></a>');
|
||||
});
|
||||
});
|
||||
|
||||
it('should use $$sanitizeUri for links', function() {
|
||||
var $$sanitizeUri = jasmine.createSpy('$$sanitizeUri');
|
||||
module(function($provide) {
|
||||
$provide.value('$$sanitizeUri', $$sanitizeUri);
|
||||
});
|
||||
inject(function() {
|
||||
$$sanitizeUri.andReturn('someUri');
|
||||
|
||||
expectHTML('<img src="someUri"/>').toEqual('<img src="someUri"/>');
|
||||
expect($$sanitizeUri).toHaveBeenCalledWith('someUri', true);
|
||||
|
||||
$$sanitizeUri.andReturn('unsafe:someUri');
|
||||
expectHTML('<img src="someUri"/>').toEqual('<img/>');
|
||||
});
|
||||
});
|
||||
|
||||
it('should be URI', function() {
|
||||
expect('').toBeValidUrl();
|
||||
expect('http://abc').toBeValidUrl();
|
||||
expect('HTTP://abc').toBeValidUrl();
|
||||
expect('https://abc').toBeValidUrl();
|
||||
expect('HTTPS://abc').toBeValidUrl();
|
||||
expect('ftp://abc').toBeValidUrl();
|
||||
expect('FTP://abc').toBeValidUrl();
|
||||
expect('mailto:me@example.com').toBeValidUrl();
|
||||
expect('MAILTO:me@example.com').toBeValidUrl();
|
||||
expect('tel:123-123-1234').toBeValidUrl();
|
||||
expect('TEL:123-123-1234').toBeValidUrl();
|
||||
expect('#anchor').toBeValidUrl();
|
||||
expect('/page1.md').toBeValidUrl();
|
||||
});
|
||||
|
||||
it('should not be URI', function() {
|
||||
expect('javascript:alert').not.toBeValidUrl();
|
||||
});
|
||||
|
||||
describe('javascript URLs', function() {
|
||||
it('should ignore javascript:', function() {
|
||||
expect('JavaScript:abc').not.toBeValidUrl();
|
||||
expect(' \n Java\n Script:abc').not.toBeValidUrl();
|
||||
|
|
@ -318,15 +395,19 @@ describe('HTML', function() {
|
|||
});
|
||||
|
||||
it('should ignore hex encoded whitespace javascript:', function() {
|
||||
expect('jav	ascript:alert("A");').not.toBeValidUrl();
|
||||
expect('jav
ascript:alert("B");').not.toBeValidUrl();
|
||||
expect('jav
 ascript:alert("C");').not.toBeValidUrl();
|
||||
expect('jav\u0000ascript:alert("D");').not.toBeValidUrl();
|
||||
expect('java\u0000\u0000script:alert("D");').not.toBeValidUrl();
|
||||
expect('  java\u0000\u0000script:alert("D");').not.toBeValidUrl();
|
||||
expect('jav	ascript:alert();').not.toBeValidUrl();
|
||||
expect('jav
ascript:alert();').not.toBeValidUrl();
|
||||
expect('jav
 ascript:alert();').not.toBeValidUrl();
|
||||
expect('jav\u0000ascript:alert();').not.toBeValidUrl();
|
||||
expect('java\u0000\u0000script:alert();').not.toBeValidUrl();
|
||||
expect('  java\u0000\u0000script:alert();').not.toBeValidUrl();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('sanitizeText', function() {
|
||||
it('should escape text', function() {
|
||||
expect(sanitizeText('a<div>&</div>c')).toEqual('a<div>&</div>c');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue