From 565ca6a1f50f83d126422471a326aae7ce623907 Mon Sep 17 00:00:00 2001
From: Georgios Kalpakas ' +
- ' ' +
- ' ' +
- '
- Super-powered by Google ©2010-2017
+ Super-powered by Google ©2010-2018
(
diff --git a/src/angular.prefix b/src/angular.prefix
index dc77488d1c01..387ae6789295 100644
--- a/src/angular.prefix
+++ b/src/angular.prefix
@@ -1,6 +1,6 @@
/**
* @license AngularJS v"NG_VERSION_FULL"
- * (c) 2010-2017 Google, Inc. http://angularjs.org
+ * (c) 2010-2018 Google, Inc. http://angularjs.org
* License: MIT
*/
(function(window) {
diff --git a/src/loader.prefix b/src/loader.prefix
index 9488eb1bef74..70f68fe8c896 100644
--- a/src/loader.prefix
+++ b/src/loader.prefix
@@ -1,6 +1,6 @@
/**
* @license AngularJS v"NG_VERSION_FULL"
- * (c) 2010-2017 Google, Inc. http://angularjs.org
+ * (c) 2010-2018 Google, Inc. http://angularjs.org
* License: MIT
*/
'use strict';
diff --git a/src/module.prefix b/src/module.prefix
index 4f6d33bd8914..499a524355a2 100644
--- a/src/module.prefix
+++ b/src/module.prefix
@@ -1,6 +1,6 @@
/**
* @license AngularJS v"NG_VERSION_FULL"
- * (c) 2010-2017 Google, Inc. http://angularjs.org
+ * (c) 2010-2018 Google, Inc. http://angularjs.org
* License: MIT
*/
(function(window, angular) {
From d253c9d45c69705e80dce519b2c393424b2d5e85 Mon Sep 17 00:00:00 2001
From: Martin Staffa text1text2
+
+
+ Large collection of elements animated in and out with ngAnimate
+
+
`;
+ });
+
+ fileList.forEach(file => {
+ const fileName = file.metadata.name.split('/').pop();
+ contentList += `${fileName}
`;
+ });
+
+ // A trailing slash in the base creates correct relative links when the url is accessed
+ // without trailing slash
+ const base = request.originalUrl.endsWith('/') ? request.originalUrl : request.originalUrl + '/';
+
+ let directoryListing = `
+ Index of ${path}
+
+ ${contentList}
`;
+
+ return response
+ .status(200)
.set({
'Cache-Control': `public, max-age=${BROWSER_CACHE_DURATION}, s-maxage=${CDN_CACHE_DURATION}`
})
- .sendFile(downloadDestination);
+ .send(directoryListing);
});
+
+ function getContent(options) {
+ return bucket.getFiles(options).then(data => {
+ const files = data[0];
+ const nextQuery = data[1];
+ const apiResponse = data[2];
+
+ if (!files.length && (!apiResponse || !apiResponse.prefixes)) {
+ return Promise.reject({
+ code: 404
+ });
+ }
+
+ fileList = fileList.concat(files);
+
+ if (apiResponse && apiResponse.prefixes) {
+ directoryList = directoryList.concat(apiResponse.prefixes);
+ }
+
+ if (nextQuery) {
+ // If the results are paged, get the next page
+ return getContent(nextQuery);
+ }
+
+ return true;
+ });
+
+ }
}
}
diff --git a/scripts/code.angularjs.org-firebase/public/index.html b/scripts/code.angularjs.org-firebase/public/index.html
deleted file mode 100644
index fe8e94ca62c8..000000000000
--- a/scripts/code.angularjs.org-firebase/public/index.html
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
- AngularJS: Scenario Test Runner
' +
- ' ' +
- '
' +
- '' +
- '
' +
- '
')($rootScope);
- $rootScope.items = [{name:'misko', gender:'male'}, {name:'felisa', gender:'female'}];
- $rootScope.$apply();
- doc.append(element);
- chain = $root.dsl.repeater('ul li');
- }));
-
- it('should get the row count', function() {
- chain.count();
- expect($root.futureResult).toEqual(2);
- });
-
- it('should return 0 if repeater doesnt match', inject(function($rootScope) {
- $rootScope.items = [];
- $rootScope.$apply();
- chain.count();
- expect($root.futureResult).toEqual(0);
- }));
-
- it('should get a row of bindings', function() {
- chain.row(1);
- expect($root.futureResult).toEqual(['felisa', 'female']);
- });
-
- it('should get a column of bindings', function() {
- chain.column('i.gender');
- expect($root.futureResult).toEqual(['male', 'female']);
- });
-
- it('should use the selector as label if none is given', function() {
- expect($root.label).toEqual('ul li');
- });
-
- it('should include the selector in paren when a label is given', function() {
- $root.dsl.repeater('mySelector', 'myLabel');
- expect($root.label).toEqual('myLabel ( ul li mySelector )');
- });
- });
-
- describe('Binding', function() {
- var compile;
-
- beforeEach(inject(function($compile, $rootScope) {
- compile = function(html, value) {
- element = $compile(html)($rootScope);
- doc.append(element);
- $rootScope.foo = {bar: value || 'some value'};
- $rootScope.$apply();
- };
- }));
-
-
- it('should select binding in interpolation', function() {
- compile('{{ foo.bar }}');
- $root.dsl.binding('foo.bar');
- expect($root.futureResult).toEqual('some value');
- });
-
- it('should select binding in multiple interpolations', function() {
- compile('{{ foo.bar }}
{{ true }}');
- $root.dsl.binding('foo.bar');
- expect($root.futureResult).toEqual('some value');
-
- $root.dsl.binding('true');
- expect($root.futureResult).toEqual('true');
- });
-
- it('should select binding by name', function() {
- compile('');
- $root.dsl.binding('foo.bar');
- expect($root.futureResult).toEqual('some value');
- });
-
- it('should select binding by regexp', function() {
- compile('some value');
- $root.dsl.binding(/^foo\..+/);
- expect($root.futureResult).toEqual('some value');
- });
-
- it('should return innerHTML for all the other elements', function() {
- compile('', 'some value');
- $root.dsl.binding('foo.bar');
- expect($root.futureResult.toLowerCase()).toEqual('some value');
- });
-
- it('should select binding in template by name', function() {
- compile('', 'bar');
- $root.dsl.binding('foo.bar');
- expect($root.futureResult).toEqual('bar');
- });
-
- it('should match bindings by substring match', function() {
- compile('', 'binding value');
- $root.dsl.binding('foo . bar');
- expect($root.futureResult).toEqual('binding value');
- });
-
- it('should return error if no bindings in document', function() {
- $root.dsl.binding('foo.bar');
- expect($root.futureError).toMatch(/did not match/);
- });
-
- it('should return error if no binding matches', function() {
- compile('some value');
- $root.dsl.binding('foo.bar');
- expect($root.futureError).toMatch(/did not match/);
- });
- });
-
- describe('Using', function() {
- it('should prefix selector in $document.elements()', function() {
- var chain;
- doc.append(
- '' +
- ''
- );
- chain = $root.dsl.using('div#test2');
- chain.input('test.input').enter('foo');
- var inputs = _jQuery('input[ng-model="test.input"]');
- expect(inputs.first().val()).toEqual('something');
- expect(inputs.last().val()).toEqual('foo');
- });
-
- it('should use the selector as label if none is given', function() {
- $root.dsl.using('mySelector');
- expect($root.label).toEqual('mySelector');
- });
-
- it('should include the selector in paren when a label is given', function() {
- $root.dsl.using('mySelector', 'myLabel');
- expect($root.label).toEqual('myLabel ( mySelector )');
- });
-
- });
-
- describe('Input', function() {
- it('should change value in text input', function(done) {
- inject(function($compile) {
- var job = createAsync(done);
- job
- .runs(function() {
- element = $compile('')($root);
- doc.append(element);
- var chain = $root.dsl.input('test.input');
- chain.enter('foo');
- expect(_jQuery('input[ng-model="test.input"]').val()).toEqual('foo');
- })
- // cleanup the event queue
- .waits(0)
- .runs(function() {
- expect($root.test.input).toBe('foo');
- })
- .done();
- job.start();
- });
- });
-
- it('should change value in text input in dash form', function() {
- doc.append('');
- var chain = $root.dsl.input('test.input');
- chain.enter('foo');
- expect(_jQuery('input[ng-model="test.input"]').val()).toEqual('foo');
- });
- it('should change value in text input in data-ng form', function() {
- doc.append('');
- var chain = $root.dsl.input('test.input');
- chain.enter('foo');
- expect(_jQuery('input[data-ng-model="test.input"]').val()).toEqual('foo');
- });
- it('should change value in text input in x-ng form', function() {
- doc.append('');
- var chain = $root.dsl.input('test.input');
- chain.enter('foo');
- expect(_jQuery('input[x-ng-model="test.input"]').val()).toEqual('foo');
- });
-
-
-
- it('should return error if no input exists', function() {
- var chain = $root.dsl.input('test.input');
- chain.enter('foo');
- expect($root.futureError).toMatch(/did not match/);
- });
-
- it('should toggle checkbox state', function() {
- doc.append('');
- expect(_jQuery('input[ng-model="test.input"]').
- prop('checked')).toBe(true);
- var chain = $root.dsl.input('test.input');
- chain.check();
- expect(_jQuery('input[ng-model="test.input"]').
- prop('checked')).toBe(false);
- $window.angular.reset();
- chain.check();
- expect(_jQuery('input[ng-model="test.input"]').
- prop('checked')).toBe(true);
- });
-
- it('should return error if checkbox did not match', function() {
- var chain = $root.dsl.input('test.input');
- chain.check();
- expect($root.futureError).toMatch(/did not match/);
- });
-
- it('should select option from radio group', function() {
- doc.append(
- '' +
- ''
- );
- // HACK! We don't know why this is sometimes false on chrome
- _jQuery('input[ng\\:model="test.input"][value="bar"]').prop('checked', true);
- expect(_jQuery('input[ng\\:model="test.input"][value="bar"]').
- prop('checked')).toBe(true);
- expect(_jQuery('input[ng\\:model="test.input"][value="foo"]').
- prop('checked')).toBe(false);
- var chain = $root.dsl.input('test.input');
- chain.select('foo');
- expect(_jQuery('input[ng\\:model="test.input"][value="bar"]').
- prop('checked')).toBe(false);
- expect(_jQuery('input[ng\\:model="test.input"][value="foo"]').
- prop('checked')).toBe(true);
- });
-
- it('should return error if radio button did not match', function() {
- var chain = $root.dsl.input('test.input');
- chain.select('foo');
- expect($root.futureError).toMatch(/did not match/);
- });
-
- describe('val', function() {
- it('should return value in text input', function() {
- doc.append('');
- $root.dsl.input('test.input').val();
- expect($root.futureResult).toEqual('something');
- });
- });
- });
-
- describe('Textarea', function() {
-
- it('should change value in textarea', function() {
- doc.append('');
- var chain = $root.dsl.input('test.textarea');
- chain.enter('foo');
- expect(_jQuery('textarea[ng-model="test.textarea"]').val()).toEqual('foo');
- });
-
- it('should return error if no textarea exists', function() {
- var chain = $root.dsl.input('test.textarea');
- chain.enter('foo');
- expect($root.futureError).toMatch(/did not match/);
- });
- });
- });
-});
diff --git a/test/ngScenario/e2e/.eslintrc.json b/test/ngScenario/e2e/.eslintrc.json
deleted file mode 100644
index 8df3459845a9..000000000000
--- a/test/ngScenario/e2e/.eslintrc.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "root": true,
- "extends": "../../../.eslintrc-node.json",
-
- "env": {
- "jasmine": true,
- "protractor": true
- }
-}
diff --git a/test/ngScenario/e2e/Runner-compiled.html b/test/ngScenario/e2e/Runner-compiled.html
deleted file mode 100644
index 530fef96233a..000000000000
--- a/test/ngScenario/e2e/Runner-compiled.html
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/test/ngScenario/e2e/Runner.html b/test/ngScenario/e2e/Runner.html
deleted file mode 100644
index f5d03ca5e4bc..000000000000
--- a/test/ngScenario/e2e/Runner.html
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
-
-
-
diff --git a/test/ngScenario/e2e/style.css b/test/ngScenario/e2e/style.css
deleted file mode 100644
index 26540becebb7..000000000000
--- a/test/ngScenario/e2e/style.css
+++ /dev/null
@@ -1,11 +0,0 @@
-th {
- text-align: left;
-}
-
-tr {
- border: 1px solid black;
-}
-
-.redbox {
- background-color: red;
-}
diff --git a/test/ngScenario/e2e/widgets-scenario.js b/test/ngScenario/e2e/widgets-scenario.js
deleted file mode 100644
index 40b176d1b30a..000000000000
--- a/test/ngScenario/e2e/widgets-scenario.js
+++ /dev/null
@@ -1,69 +0,0 @@
-/* global using: false, binding: false, input: false, select: false, repeater: false */
-'use strict';
-
-describe('widgets', function() {
- it('should verify that basic widgets work', function() {
- browser().navigateTo('widgets.html');
-
- using('#text-basic-box').input('text.basic').enter('Carlos');
- expect(binding('text.basic')).toEqual('Carlos');
- input('text.basic').enter('Carlos Santana');
- expect(binding('text.basic')).not().toEqual('Carlos Boozer');
-
- input('text.password').enter('secret');
- expect(binding('text.password')).toEqual('secret');
-
- expect(binding('text.hidden')).toEqual('hiddenValue');
-
- expect(binding('gender')).toEqual('male');
- input('gender').select('female');
- expect(using('#gender-box').binding('gender')).toEqual('female');
-
- expect(repeater('#repeater-row ul li').count()).toEqual(2);
- expect(repeater('#repeater-row ul li').row(1)).toEqual(['adam']);
- expect(repeater('#repeater-row ul li').column('name')).toEqual(['misko', 'adam']);
-
- select('select').option('B');
- expect(binding('select')).toEqual('B');
-
- select('multiselect').options('A', 'C');
- expect(binding('multiselect').fromJson()).toEqual(['A', 'C']);
-
- expect(binding('button').fromJson()).toEqual({'count': 0});
- expect(binding('form').fromJson()).toEqual({'count': 0});
-
- element('form a', '\'action\' link').click();
- expect(binding('button').fromJson()).toEqual({'count': 1});
-
- element('input[value="submit input"]', '\'submit input\' button').click();
- expect(binding('button').fromJson()).toEqual({'count': 2});
- expect(binding('form').fromJson()).toEqual({'count': 1});
-
- element('button:contains("submit button")', '\'submit button\' button').click();
- expect(binding('button').fromJson()).toEqual({'count': 2});
- expect(binding('form').fromJson()).toEqual({'count': 2});
-
- element('input[value="button"]', '\'button\' button').click();
- expect(binding('button').fromJson()).toEqual({'count': 3});
-
- element('input[type="image"]', 'form image').click();
- expect(binding('button').fromJson()).toEqual({'count': 4});
-
- /**
- * Custom value parser for futures.
- */
- function checkboxParser(value) {
- // eslint-disable-next-line no-undef
- return angular.fromJson(value.substring(value.indexOf('=') + 1));
- }
-
- input('checkbox.tea').check();
- expect(binding('checkbox').parsedWith(checkboxParser)).toEqual({coffee: false, tea: false});
- input('checkbox.coffee').check();
- expect(binding('checkbox').parsedWith(checkboxParser)).toEqual({coffee: true, tea: false});
- input('checkbox.tea').check();
- input('checkbox.tea').check();
- input('checkbox.tea').check();
- expect(binding('checkbox').parsedWith(checkboxParser)).toEqual({coffee: true, tea: true});
- });
-});
diff --git a/test/ngScenario/e2e/widgets.html b/test/ngScenario/e2e/widgets.html
deleted file mode 100644
index 5fdae881cf68..000000000000
--- a/test/ngScenario/e2e/widgets.html
+++ /dev/null
@@ -1,99 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/test/ngScenario/matchersSpec.js b/test/ngScenario/matchersSpec.js
deleted file mode 100644
index 7b68d2b9b731..000000000000
--- a/test/ngScenario/matchersSpec.js
+++ /dev/null
@@ -1,51 +0,0 @@
-'use strict';
-
-describe('angular.scenario.matchers', function() {
- var matchers;
-
- function expectMatcher(value, test) {
- delete matchers.error;
- delete matchers.future.value;
- if (isDefined(value)) {
- matchers.future.value = value;
- }
- test();
- expect(matchers.error).toBeUndefined();
- }
-
- beforeEach(function() {
- /**
- * Mock up the future system wrapped around matchers.
- *
- * @see Scenario.js#angular.scenario.matcher
- */
- matchers = {
- future: { name: 'test' }
- };
- matchers.addFuture = function(name, callback) {
- callback(function(error) {
- matchers.error = error;
- });
- };
- angular.extend(matchers, angular.scenario.matcher);
- });
-
- it('should handle basic matching', function() {
- expectMatcher(10, function() { matchers.toEqual(10); });
- expectMatcher('value', function() { matchers.toBeDefined(); });
- expectMatcher([1], function() { matchers.toBeTruthy(); });
- expectMatcher('', function() { matchers.toBeFalsy(); });
- expectMatcher(0, function() { matchers.toBeFalsy(); });
- expectMatcher('foo', function() { matchers.toMatch('.o.'); });
- expectMatcher(null, function() { matchers.toBeNull(); });
- expectMatcher([1, 2, 3], function() { matchers.toContain(2); });
- expectMatcher(3, function() { matchers.toBeLessThan(10); });
- expectMatcher(3, function() { matchers.toBeGreaterThan(-5); });
- });
-
- it('should have toHaveClass matcher', function() {
- var e = angular.element('
-
- Description
- Test
- Result
-
- Input text field
-
- basic
-
-
-
- text.basic={{text.basic}}
-
-
- password
-
- text.password={{text.password}}
-
-
- hidden
-
- text.hidden={{text.hidden}}
-
- Input selection field
-
- radio
-
- Female
-
- Male
- gender={{gender}}
-
-
- checkbox
-
- Tea
-
- Coffee
-
-
- checkbox={{checkbox}}
-
-
- select
-
-
-
- select={{select}}
-
-
- multiselect
-
-
-
- multiselect={{multiselect}}
-
- Buttons
-
- ng-change
-
ng-click
-
-
- button={{button}} form={{form}}
-
- Repeaters
-
- ng-repeat
-
-
-
-
-
- {{ key }}
Note that `$sce.RESOURCE_URL` makes a stronger statement about the URL than `$sce.URL` does (it's not just the URL that matters, but also what is at the end of it), and therefore contexts requiring values trusted for `$sce.RESOURCE_URL` can be used anywhere that values trusted for `$sce.URL` are required. |
- * | `$sce.JS` | For JavaScript that is safe to execute in your application's context. Currently, no bindings require this context. Feel free to use it in your own directives. |
+ * | `$sce.HTML` | For HTML that's safe to source into the application. The {@link ng.directive:ngBindHtml ngBindHtml} directive uses this context for bindings. If an unsafe value is encountered and the {@link ngSanitize $sanitize} module is present this will sanitize the value instead of throwing an error. |
+ * | `$sce.CSS` | For CSS that's safe to source into the application. Currently unused. Feel free to use it in your own directives. |
+ * | `$sce.MEDIA_URL` | For URLs that are safe to render as media. Is automatically converted from string by sanitizing when needed. |
+ * | `$sce.URL` | For URLs that are safe to follow as links. Is automatically converted from string by sanitizing when needed. Note that `$sce.URL` makes a stronger statement about the URL than `$sce.MEDIA_URL` does and therefore contexts requiring values trusted for `$sce.URL` can be used anywhere that values trusted for `$sce.MEDIA_URL` are required.|
+ * | `$sce.RESOURCE_URL` | For URLs that are not only safe to follow as links, but whose contents are also safe to include in your application. Examples include `ng-include`, `src` / `ngSrc` bindings for tags other than `IMG` (e.g. `IFRAME`, `OBJECT`, etc.)
Note that `$sce.RESOURCE_URL` makes a stronger statement about the URL than `$sce.URL` or `$sce.MEDIA_URL` do and therefore contexts requiring values trusted for `$sce.RESOURCE_URL` can be used anywhere that values trusted for `$sce.URL` or `$sce.MEDIA_URL` are required. |
+ * | `$sce.JS` | For JavaScript that is safe to execute in your application's context. Currently unused. Feel free to use it in your own directives. |
+ *
+ *
+ * ')($rootScope);
+ $rootScope.attr.$set('img', 'evil:foo()');
+ expect(element.attr('img')).toEqual('evil:foo()');
+ expect($rootScope.attr.img).toEqual('evil:foo()');
+ }));
+
+ it('should automatically sanitize img[srcset]', inject(function($compile, $rootScope) {
+ element = $compile('
')($rootScope);
+ $rootScope.attr.$set('srcset', 'evil:foo()');
+ expect(element.attr('srcset')).toEqual('unsafe:evil:foo()');
+ expect($rootScope.attr.srcset).toEqual('unsafe:evil:foo()');
+ }));
+
+ it('should not accept trusted values for img[srcset]', inject(function($compile, $rootScope, $sce) {
+ var trusted = $sce.trustAsMediaUrl('trustme:foo()');
+ element = $compile('
')($rootScope);
+ expect(function() {
+ $rootScope.attr.$set('srcset', trusted);
+ }).toThrowMinErr('$compile', 'srcset', 'Can\'t pass trusted values to `$set(\'srcset\', value)`: "trustme:foo()"');
+ }));
});
});
@@ -11071,91 +11135,114 @@ describe('$compile', function() {
);
});
- describe('*[src] context requirement', function() {
-
- it('should NOT require trusted values for img src', inject(function($rootScope, $compile, $sce) {
- element = $compile('
')($rootScope);
- $rootScope.testUrl = 'http://example.com/image.png';
- $rootScope.$digest();
- expect(element.attr('src')).toEqual('http://example.com/image.png');
- // But it should accept trusted values anyway.
- $rootScope.testUrl = $sce.trustAsUrl('http://example.com/image2.png');
- $rootScope.$digest();
- expect(element.attr('src')).toEqual('http://example.com/image2.png');
- }));
-
+ ['img', 'audio', 'video'].forEach(function(tag) {
// Support: IE 9 only
- // IE9 rejects the video / audio tag with "Error: Not implemented" and the source tag with
- // "Unable to get value of the property 'childNodes': object is null or undefined"
- if (msie !== 9) {
- they('should NOT require trusted values for $prop src', ['video', 'audio'],
- function(tag) {
- inject(function($rootScope, $compile, $sce) {
+ // IE9 rejects the `video` / `audio` tags with "Error: Not implemented"
+ if (msie !== 9 || tag === 'img') {
+ describe(tag + '[src] context requirement', function() {
+ it('should NOT require trusted values for whitelisted URIs', inject(function($rootScope, $compile) {
element = $compile('<' + tag + ' src="{{testUrl}}">' + tag + '>')($rootScope);
- $rootScope.testUrl = 'http://example.com/image.mp4';
+ $rootScope.testUrl = 'http://example.com/image.mp4'; // `http` is whitelisted
$rootScope.$digest();
expect(element.attr('src')).toEqual('http://example.com/image.mp4');
+ }));
+
+ it('should accept trusted values', inject(function($rootScope, $compile, $sce) {
+ // As a MEDIA_URL URL
+ element = $compile('<' + tag + ' src="{{testUrl}}">' + tag + '>')($rootScope);
+ // Some browsers complain if you try to write `javascript:` into an `img[src]`
+ // So for the test use something different
+ $rootScope.testUrl = $sce.trustAsMediaUrl('untrusted:foo()');
+ $rootScope.$digest();
+ expect(element.attr('src')).toEqual('untrusted:foo()');
- // But it should accept trusted values anyway.
- $rootScope.testUrl = $sce.trustAsUrl('http://example.com/image2.mp4');
+ // As a URL
+ element = $compile('<' + tag + ' src="{{testUrl}}">' + tag + '>')($rootScope);
+ $rootScope.testUrl = $sce.trustAsUrl('untrusted:foo()');
$rootScope.$digest();
- expect(element.attr('src')).toEqual('http://example.com/image2.mp4');
+ expect(element.attr('src')).toEqual('untrusted:foo()');
- // and trustedResourceUrls for retrocompatibility
- $rootScope.testUrl = $sce.trustAsResourceUrl('http://example.com/image3.mp4');
+ // As a RESOURCE URL
+ element = $compile('<' + tag + ' src="{{testUrl}}">' + tag + '>')($rootScope);
+ $rootScope.testUrl = $sce.trustAsResourceUrl('untrusted:foo()');
$rootScope.$digest();
- expect(element.attr('src')).toEqual('http://example.com/image3.mp4');
- });
+ expect(element.attr('src')).toEqual('untrusted:foo()');
+ }));
});
+ }
+ });
- they('should NOT require trusted values for $prop src', ['source', 'track'],
- function(tag) {
- inject(function($rootScope, $compile, $sce) {
+ // Support: IE 9 only
+ // IE 9 rejects the `source` / `track` tags with
+ // "Unable to get value of the property 'childNodes': object is null or undefined"
+ if (msie !== 9) {
+ ['source', 'track'].forEach(function(tag) {
+ describe(tag + '[src]', function() {
+ it('should NOT require trusted values for whitelisted URIs', inject(function($rootScope, $compile) {
element = $compile('')($rootScope);
- $rootScope.testUrl = 'http://example.com/image.mp4';
+ $rootScope.testUrl = 'http://example.com/image.mp4'; // `http` is whitelisted
$rootScope.$digest();
expect(element.find(tag).attr('src')).toEqual('http://example.com/image.mp4');
+ }));
- // But it should accept trusted values anyway.
- $rootScope.testUrl = $sce.trustAsUrl('http://example.com/image2.mp4');
+ it('should accept trusted values', inject(function($rootScope, $compile, $sce) {
+ // As a MEDIA_URL URL
+ element = $compile('')($rootScope);
+ $rootScope.testUrl = $sce.trustAsMediaUrl('javascript:foo()');
$rootScope.$digest();
- expect(element.find(tag).attr('src')).toEqual('http://example.com/image2.mp4');
+ expect(element.find(tag).attr('src')).toEqual('javascript:foo()');
- // and trustedResourceUrls for retrocompatibility
- $rootScope.testUrl = $sce.trustAsResourceUrl('http://example.com/image3.mp4');
+ // As a URL
+ element = $compile('')($rootScope);
+ $rootScope.testUrl = $sce.trustAsUrl('javascript:foo()');
$rootScope.$digest();
- expect(element.find(tag).attr('src')).toEqual('http://example.com/image3.mp4');
- });
+ expect(element.find(tag).attr('src')).toEqual('javascript:foo()');
+
+ // As a RESOURCE URL
+ element = $compile('')($rootScope);
+ $rootScope.testUrl = $sce.trustAsResourceUrl('javascript:foo()');
+ $rootScope.$digest();
+ expect(element.find(tag).attr('src')).toEqual('javascript:foo()');
+ }));
});
- }
- });
+ });
+ }
describe('img[src] sanitization', function() {
+ it('should accept trusted values', inject(function($rootScope, $compile, $sce) {
+ element = $compile('
')($rootScope);
+ // Some browsers complain if you try to write `javascript:` into an `img[src]`
+ // So for the test use something different
+ $rootScope.testUrl = $sce.trustAsMediaUrl('someUntrustedThing:foo();');
+ $rootScope.$digest();
+ expect(element.attr('src')).toEqual('someUntrustedThing:foo();');
+ }));
+
+ it('should sanitize concatenated values even if they are trusted', inject(function($rootScope, $compile, $sce) {
+ element = $compile('
')($rootScope);
+ $rootScope.testUrl = $sce.trustAsUrl('untrusted:foo();');
+ $rootScope.$digest();
+ expect(element.attr('src')).toEqual('unsafe:untrusted:foo();ponies');
+
+ element = $compile('
')($rootScope);
+ $rootScope.testUrl2 = $sce.trustAsUrl('xyz;');
+ $rootScope.$digest();
+ expect(element.attr('src')).toEqual('http://xyz;');
+
+ element = $compile('
')($rootScope);
+ $rootScope.testUrl3 = $sce.trustAsUrl('untrusted:foo();');
+ $rootScope.$digest();
+ expect(element.attr('src')).toEqual('unsafe:untrusted:foo();untrusted:foo();');
+ }));
+
it('should not sanitize attributes other than src', inject(function($compile, $rootScope) {
element = $compile('
')($rootScope);
$rootScope.testUrl = 'javascript:doEvilStuff()';
$rootScope.$apply();
-
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());
-
- returnVal = $compileProvider.imgSrcSanitizationWhitelist(newRe);
- expect(returnVal).toBe($compileProvider);
- expect($$sanitizeUriProvider.imgSrcSanitizationWhitelist()).toBe(newRe);
- expect($compileProvider.imgSrcSanitizationWhitelist()).toBe(newRe);
- });
- inject(function() {
- // needed to the module definition above is run...
- });
- });
-
it('should use $$sanitizeUri', function() {
var $$sanitizeUri = jasmine.createSpy('$$sanitizeUri');
module(function($provide) {
@@ -11171,55 +11258,113 @@ describe('$compile', function() {
expect($$sanitizeUri).toHaveBeenCalledWith($rootScope.testUrl, true);
});
});
+
+
+ it('should use $$sanitizeUri on concatenated trusted values', function() {
+ var $$sanitizeUri = jasmine.createSpy('$$sanitizeUri').and.returnValue('someSanitizedUrl');
+ module(function($provide) {
+ $provide.value('$$sanitizeUri', $$sanitizeUri);
+ });
+ inject(function($compile, $rootScope, $sce) {
+ element = $compile('
')($rootScope);
+ $rootScope.testUrl = $sce.trustAsUrl('javascript:foo();');
+ $rootScope.$digest();
+ expect(element.attr('src')).toEqual('someSanitizedUrl');
+
+ element = $compile('
')($rootScope);
+ $rootScope.testUrl = $sce.trustAsUrl('xyz');
+ $rootScope.$digest();
+ expect(element.attr('src')).toEqual('someSanitizedUrl');
+ });
+ });
+
+ it('should not use $$sanitizeUri with trusted values', function() {
+ var $$sanitizeUri = jasmine.createSpy('$$sanitizeUri').and.throwError('Should not have been called');
+ module(function($provide) {
+ $provide.value('$$sanitizeUri', $$sanitizeUri);
+ });
+ inject(function($compile, $rootScope, $sce) {
+ element = $compile('
')($rootScope);
+ // Assigning javascript:foo to src makes at least IE9-11 complain, so use another
+ // protocol name.
+ $rootScope.testUrl = $sce.trustAsMediaUrl('untrusted:foo();');
+ $rootScope.$apply();
+ expect(element.attr('src')).toEqual('untrusted:foo();');
+ });
+ });
});
describe('img[srcset] sanitization', function() {
-
- it('should not error if undefined', function() {
+ it('should not error if srcset is undefined', function() {
var linked = false;
module(function() {
directive('setter', valueFn(function(scope, elem, attrs) {
+ // Set srcset to a value
attrs.$set('srcset', 'http://example.com/');
expect(attrs.srcset).toBe('http://example.com/');
-
+ // Now set it to undefined
attrs.$set('srcset', undefined);
expect(attrs.srcset).toBeUndefined();
-
linked = true;
}));
});
inject(function($compile, $rootScope) {
element = $compile('
')($rootScope);
-
expect(linked).toBe(true);
expect(element.attr('srcset')).toBeUndefined();
});
});
- it('should NOT require trusted values for img srcset', inject(function($rootScope, $compile, $sce) {
+ it('should NOT require trusted values for whitelisted values', inject(function($rootScope, $compile, $sce) {
element = $compile('
')($rootScope);
- $rootScope.testUrl = 'http://example.com/image.png';
+ $rootScope.testUrl = 'http://example.com/image.png'; // `http` is whitelisted
$rootScope.$digest();
expect(element.attr('srcset')).toEqual('http://example.com/image.png');
- // But it should accept trusted values anyway.
- $rootScope.testUrl = $sce.trustAsUrl('http://example.com/image2.png');
+ }));
+
+ it('should accept trusted values, if they are also whitelisted', inject(function($rootScope, $compile, $sce) {
+ element = $compile('
')($rootScope);
+ $rootScope.testUrl = $sce.trustAsUrl('http://example.com');
$rootScope.$digest();
- expect(element.attr('srcset')).toEqual('http://example.com/image2.png');
+ expect(element.attr('srcset')).toEqual('http://example.com');
+ }));
+
+ it('does not work with trusted values', inject(function($rootScope, $compile, $sce) {
+ // A limitation of the approach used for srcset is that you cannot use `trustAsUrl`.
+ // Use trustAsHtml and ng-bind-html to work around this.
+ element = $compile('
')($rootScope);
+ $rootScope.testUrl = $sce.trustAsUrl('javascript:something');
+ $rootScope.$digest();
+ expect(element.attr('srcset')).toEqual('unsafe:javascript:something');
+
+ element = $compile('
')($rootScope);
+ $rootScope.testUrl = $sce.trustAsUrl('javascript:something');
+ $rootScope.$digest();
+ expect(element.attr('srcset')).toEqual(
+ 'unsafe:javascript:something ,unsafe:javascript:something');
}));
it('should use $$sanitizeUri', function() {
- var $$sanitizeUri = jasmine.createSpy('$$sanitizeUri');
+ var $$sanitizeUri = jasmine.createSpy('$$sanitizeUri').and.returnValue('someSanitizedUrl');
module(function($provide) {
$provide.value('$$sanitizeUri', $$sanitizeUri);
});
inject(function($compile, $rootScope) {
element = $compile('
')($rootScope);
$rootScope.testUrl = 'someUrl';
-
- $$sanitizeUri.and.returnValue('someSanitizedUrl');
$rootScope.$apply();
expect(element.attr('srcset')).toBe('someSanitizedUrl');
expect($$sanitizeUri).toHaveBeenCalledWith($rootScope.testUrl, true);
+
+ element = $compile('
')($rootScope);
+ $rootScope.testUrl = 'javascript:yay';
+ $rootScope.$apply();
+ expect(element.attr('srcset')).toEqual('someSanitizedUrl ,someSanitizedUrl');
+
+ element = $compile('
')($rootScope);
+ $rootScope.testUrl = 'script:yay, javascript:nay';
+ $rootScope.$apply();
+ expect(element.attr('srcset')).toEqual('someSanitizedUrl ,someSanitizedUrl');
});
});
@@ -11263,6 +11408,38 @@ describe('$compile', function() {
});
describe('a[href] sanitization', function() {
+ it('should NOT require trusted values for whitelisted values', inject(function($rootScope, $compile) {
+ $rootScope.testUrl = 'http://example.com/image.png'; // `http` is whitelisted
+ element = $compile('')($rootScope);
+ $rootScope.$digest();
+ expect(element.attr('href')).toEqual('http://example.com/image.png');
+
+ element = $compile('')($rootScope);
+ $rootScope.$digest();
+ expect(element.attr('ng-href')).toEqual('http://example.com/image.png');
+ }));
+
+ it('should accept trusted values for non-whitelisted values', inject(function($rootScope, $compile, $sce) {
+ $rootScope.testUrl = $sce.trustAsUrl('javascript:foo()'); // `javascript` is not whitelisted
+ element = $compile('')($rootScope);
+ $rootScope.$digest();
+ expect(element.attr('href')).toEqual('javascript:foo()');
+
+ element = $compile('')($rootScope);
+ $rootScope.$digest();
+ expect(element.attr('ng-href')).toEqual('javascript:foo()');
+ }));
+
+ it('should sanitize non-whitelisted values', inject(function($rootScope, $compile) {
+ $rootScope.testUrl = 'javascript:foo()'; // `javascript` is not whitelisted
+ element = $compile('')($rootScope);
+ $rootScope.$digest();
+ expect(element.attr('href')).toEqual('unsafe:javascript:foo()');
+
+ element = $compile('')($rootScope);
+ $rootScope.$digest();
+ expect(element.attr('href')).toEqual('unsafe:javascript:foo()');
+ }));
it('should not sanitize href on elements other than anchor', inject(function($compile, $rootScope) {
element = $compile('')($rootScope);
@@ -11272,7 +11449,7 @@ describe('$compile', function() {
expect(element.attr('href')).toBe('javascript:doEvilStuff()');
}));
- it('should not sanitize attributes other than href', inject(function($compile, $rootScope) {
+ it('should not sanitize attributes other than href/ng-href', inject(function($compile, $rootScope) {
element = $compile('')($rootScope);
$rootScope.testUrl = 'javascript:doEvilStuff()';
$rootScope.$apply();
@@ -11280,48 +11457,21 @@ 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());
-
- returnVal = $compileProvider.aHrefSanitizationWhitelist(newRe);
- expect(returnVal).toBe($compileProvider);
- expect($$sanitizeUriProvider.aHrefSanitizationWhitelist()).toBe(newRe);
- expect($compileProvider.aHrefSanitizationWhitelist()).toBe(newRe);
- });
- inject(function() {
- // needed to the module definition above is run...
- });
- });
-
it('should use $$sanitizeUri', function() {
- var $$sanitizeUri = jasmine.createSpy('$$sanitizeUri');
+ var $$sanitizeUri = jasmine.createSpy('$$sanitizeUri').and.returnValue('someSanitizedUrl');
module(function($provide) {
$provide.value('$$sanitizeUri', $$sanitizeUri);
});
inject(function($compile, $rootScope) {
element = $compile('')($rootScope);
$rootScope.testUrl = 'someUrl';
-
- $$sanitizeUri.and.returnValue('someSanitizedUrl');
$rootScope.$apply();
expect(element.attr('href')).toBe('someSanitizedUrl');
expect($$sanitizeUri).toHaveBeenCalledWith($rootScope.testUrl, false);
- });
- });
- it('should use $$sanitizeUri when declared via ng-href', function() {
- var $$sanitizeUri = jasmine.createSpy('$$sanitizeUri');
- module(function($provide) {
- $provide.value('$$sanitizeUri', $$sanitizeUri);
- });
- inject(function($compile, $rootScope) {
- element = $compile('')($rootScope);
- $rootScope.testUrl = 'someUrl';
+ $$sanitizeUri.calls.reset();
- $$sanitizeUri.and.returnValue('someSanitizedUrl');
+ element = $compile('')($rootScope);
$rootScope.$apply();
expect(element.attr('href')).toBe('someSanitizedUrl');
expect($$sanitizeUri).toHaveBeenCalledWith($rootScope.testUrl, false);
@@ -11329,72 +11479,72 @@ describe('$compile', function() {
});
it('should use $$sanitizeUri when working with svg and xlink:href', function() {
- var $$sanitizeUri = jasmine.createSpy('$$sanitizeUri');
+ var $$sanitizeUri = jasmine.createSpy('$$sanitizeUri').and.returnValue('https://clean.example.org');
module(function($provide) {
$provide.value('$$sanitizeUri', $$sanitizeUri);
});
inject(function($compile, $rootScope) {
- var elementA = $compile('
')($rootScope);
$rootScope.$digest();
expect(element.attr('src')).toBe('unsafe:javascript:alert(1);');
}));
+
+ it('should sanitize non-interpolated url', inject(function($rootScope, $compile) {
+ element = $compile('
')($rootScope);
+ $rootScope.$digest();
+ expect(element.attr('src')).toBe('unsafe:javascript:alert(1);');
+ }));
+
+ it('should interpolate the expression and bind to src with raw same-domain value', inject(function($compile, $rootScope) {
+ element = $compile('
')($rootScope);
+
+ $rootScope.$digest();
+ expect(element.attr('src')).toBeUndefined();
+
+ $rootScope.$apply(function() {
+ $rootScope.id = '/somewhere/here';
+ });
+ expect(element.attr('src')).toEqual('/somewhere/here');
+ }));
+
+ it('should interpolate a multi-part expression for img src attribute (which requires the MEDIA_URL context)', inject(function($compile, $rootScope) {
+ element = $compile('
')($rootScope);
+ expect(element.attr('src')).toBe(undefined); // URL concatenations are all-or-nothing
+ $rootScope.$apply(function() {
+ $rootScope.id = 1;
+ });
+ expect(element.attr('src')).toEqual('some/1');
+ }));
+
+ // Support: IE 9-11 only
+ if (msie) {
+ it('should update the element property as well as the attribute', inject(function($compile, $rootScope, $sce) {
+ // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
+ // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
+ // to set the property as well to achieve the desired effect
+
+ element = $compile('
')($rootScope);
+
+ $rootScope.$digest();
+ expect(element.prop('src')).toBe('');
+ dealoc(element);
+
+ element = $compile('
')($rootScope);
+
+ $rootScope.$digest();
+ expect(element.prop('src')).toMatch('/some/$');
+ dealoc(element);
+
+ element = $compile('
')($rootScope);
+ $rootScope.$apply(function() {
+ $rootScope.id = $sce.trustAsResourceUrl('http://somewhere/abc');
+ });
+ expect(element.prop('src')).toEqual('http://somewhere/abc');
+ }));
+ }
});
describe('iframe[ng-src]', function() {
@@ -68,5 +122,43 @@ describe('ngSrc', function() {
expect(element.attr('src')).toEqual('javascript:doTrustedStuff()');
}));
+
+ it('should interpolate the expression and bind to src with a trusted value', inject(function($compile, $rootScope, $sce) {
+ element = $compile('')($rootScope);
+
+ $rootScope.$digest();
+ expect(element.attr('src')).toBeUndefined();
+
+ $rootScope.$apply(function() {
+ $rootScope.id = $sce.trustAsResourceUrl('http://somewhere');
+ });
+ expect(element.attr('src')).toEqual('http://somewhere');
+ }));
+
+
+ it('should NOT interpolate a multi-part expression in a `src` attribute that requires a non-MEDIA_URL context', inject(function($compile, $rootScope) {
+ expect(function() {
+ element = $compile('')($rootScope);
+ $rootScope.$apply(function() {
+ $rootScope.id = 1;
+ });
+ }).toThrowMinErr(
+ '$interpolate', 'noconcat', 'Error while interpolating: some/{{id}}\nStrict ' +
+ 'Contextual Escaping disallows interpolations that concatenate multiple expressions ' +
+ 'when a trusted value is required. See http://docs.angularjs.org/api/ng.$sce');
+ }));
+
+
+ it('should NOT interpolate a wrongly typed expression', inject(function($compile, $rootScope, $sce) {
+ expect(function() {
+ element = $compile('')($rootScope);
+ $rootScope.$apply(function() {
+ $rootScope.id = $sce.trustAsUrl('http://somewhere');
+ });
+ element.attr('src');
+ }).toThrowMinErr(
+ '$interpolate', 'interr', 'Can\'t interpolate: {{id}}\nError: [$sce:insecurl] Blocked ' +
+ 'loading resource from url not allowed by $sceDelegate policy. URL: http://somewhere');
+ }));
});
});
diff --git a/test/ng/directive/ngSrcsetSpec.js b/test/ng/directive/ngSrcsetSpec.js
index 15aba45b5749..b8032a77cba5 100644
--- a/test/ng/directive/ngSrcsetSpec.js
+++ b/test/ng/directive/ngSrcsetSpec.js
@@ -34,5 +34,18 @@ describe('ngSrcset', function() {
element = $compile('
')($rootScope);
$rootScope.$digest();
}));
-});
+ it('should interpolate the expression and bind to srcset', inject(function($compile, $rootScope) {
+ var element = $compile('
')($rootScope);
+
+ $rootScope.$digest();
+ expect(element.attr('srcset')).toBeUndefined();
+
+ $rootScope.$apply(function() {
+ $rootScope.id = 1;
+ });
+ expect(element.attr('srcset')).toEqual('some/1 2x');
+
+ dealoc(element);
+ }));
+});
diff --git a/test/ng/interpolateSpec.js b/test/ng/interpolateSpec.js
index 2ed9b31b7f5f..43c5fc4ac275 100644
--- a/test/ng/interpolateSpec.js
+++ b/test/ng/interpolateSpec.js
@@ -1,5 +1,7 @@
'use strict';
+/* eslint-disable no-script-url */
+
describe('$interpolate', function() {
it('should return the interpolation object when there are no bindings and textOnly is undefined',
@@ -267,7 +269,9 @@ describe('$interpolate', function() {
expect(function() {
$interpolate('{{foo}}', true, sce.CSS)(scope);
- }).toThrowMinErr('$interpolate', 'interr');
+ }).toThrowMinErr(
+ '$interpolate', 'interr', 'Can\'t interpolate: {{foo}}\nError: [$sce:unsafe] ' +
+ 'Attempting to use an unsafe value in a safe context.');
}));
it('should NOT interpolate mistyped expressions', inject(function($interpolate, $rootScope) {
@@ -276,7 +280,9 @@ describe('$interpolate', function() {
expect(function() {
$interpolate('{{foo}}', true, sce.HTML)(scope);
- }).toThrowMinErr('$interpolate', 'interr');
+ }).toThrowMinErr(
+ '$interpolate', 'interr', 'Can\'t interpolate: {{foo}}\nError: [$sce:unsafe] ' +
+ 'Attempting to use an unsafe value in a safe context.');
}));
it('should interpolate trusted expressions in a regular context', inject(function($interpolate) {
@@ -291,17 +297,16 @@ describe('$interpolate', function() {
// The concatenation of trusted values does not necessarily result in a trusted value. (For
// instance, you can construct evil JS code by putting together pieces of JS strings that are by
- // themselves safe to execute in isolation.)
+ // themselves safe to execute in isolation). Therefore, some contexts disable it, such as CSS.
it('should NOT interpolate trusted expressions with multiple parts', inject(function($interpolate) {
var foo = sce.trustAsCss('foo');
var bar = sce.trustAsCss('bar');
expect(function() {
return $interpolate('{{foo}}{{bar}}', true, sce.CSS)({foo: foo, bar: bar});
}).toThrowMinErr(
- '$interpolate', 'noconcat', 'Error while interpolating: {{foo}}{{bar}}\n' +
+ '$interpolate', 'interr', 'Error while interpolating: {{foo}}{{bar}}\n' +
'Strict Contextual Escaping disallows interpolations that concatenate multiple ' +
- 'expressions when a trusted value is required. See ' +
- 'http://docs.angularjs.org/api/ng.$sce');
+ 'expressions when a trusted value is required. See http://docs.angularjs.org/api/ng.$sce');
}));
});
@@ -380,26 +385,32 @@ describe('$interpolate', function() {
describe('isTrustedContext', function() {
- it('should NOT interpolate a multi-part expression when isTrustedContext is true', inject(function($interpolate) {
- var isTrustedContext = true;
+ it('should NOT interpolate a multi-part expression when isTrustedContext is RESOURCE_URL', inject(function($sce, $interpolate) {
+ var isTrustedContext = $sce.RESOURCE_URL;
expect(function() {
- $interpolate('constant/{{var}}', true, isTrustedContext);
+ $interpolate('constant/{{var}}', true, isTrustedContext)('val');
}).toThrowMinErr(
- '$interpolate', 'noconcat', 'Error while interpolating: constant/{{var}}\nStrict ' +
- 'Contextual Escaping disallows interpolations that concatenate multiple expressions ' +
- 'when a trusted value is required. See http://docs.angularjs.org/api/ng.$sce');
+ '$interpolate', 'interr',
+ 'Can\'t interpolate: constant/{{var}}\nError: [$interpolate:noconcat] Error while ' +
+ 'interpolating: constant/{{var}}\nStrict Contextual Escaping disallows interpolations ' +
+ 'that concatenate multiple expressions when a trusted value is required. ' +
+ 'See http://docs.angularjs.org/api/ng.$sce');
expect(function() {
- $interpolate('{{var}}/constant', true, isTrustedContext);
+ $interpolate('{{var}}/constant', true, isTrustedContext)('val');
}).toThrowMinErr(
- '$interpolate', 'noconcat', 'Error while interpolating: {{var}}/constant\nStrict ' +
- 'Contextual Escaping disallows interpolations that concatenate multiple expressions ' +
- 'when a trusted value is required. See http://docs.angularjs.org/api/ng.$sce');
- expect(function() {
- $interpolate('{{foo}}{{bar}}', true, isTrustedContext);
+ '$interpolate', 'interr',
+ 'Can\'t interpolate: {{var}}/constant\nError: [$interpolate:noconcat] Error while ' +
+ 'interpolating: {{var}}/constant\nStrict Contextual Escaping disallows interpolations ' +
+ 'that concatenate multiple expressions when a trusted value is required. ' +
+ 'See http://docs.angularjs.org/api/ng.$sce');
+ expect(function() {
+ $interpolate('{{foo}}{{bar}}', true, isTrustedContext)('val');
}).toThrowMinErr(
- '$interpolate', 'noconcat', 'Error while interpolating: {{foo}}{{bar}}\nStrict ' +
- 'Contextual Escaping disallows interpolations that concatenate multiple expressions ' +
- 'when a trusted value is required. See http://docs.angularjs.org/api/ng.$sce');
+ '$interpolate', 'interr',
+ 'Can\'t interpolate: {{foo}}{{bar}}\nError: [$interpolate:noconcat] Error while ' +
+ 'interpolating: {{foo}}{{bar}}\nStrict Contextual Escaping disallows interpolations ' +
+ 'that concatenate multiple expressions when a trusted value is required. ' +
+ 'See http://docs.angularjs.org/api/ng.$sce');
}));
it('should interpolate a multi-part expression when isTrustedContext is false', inject(function($interpolate) {
@@ -407,6 +418,23 @@ describe('$interpolate', function() {
expect($interpolate('some/{{id}}')({id: 1})).toEqual('some/1');
expect($interpolate('{{foo}}{{bar}}')({foo: 1, bar: 2})).toEqual('12');
}));
+
+
+ it('should interpolate a multi-part expression when isTrustedContext is URL', inject(function($sce, $interpolate) {
+ expect($interpolate('some/{{id}}', true, $sce.URL)({})).toEqual('some/');
+ expect($interpolate('some/{{id}}', true, $sce.URL)({id: 1})).toEqual('some/1');
+ expect($interpolate('{{foo}}{{bar}}', true, $sce.URL)({foo: 1, bar: 2})).toEqual('12');
+ }));
+
+
+ it('should interpolate and sanitize a multi-part expression when isTrustedContext is URL', inject(function($sce, $interpolate) {
+ expect($interpolate('some/{{id}}', true, $sce.URL)({})).toEqual('some/');
+ expect($interpolate('some/{{id}}', true, $sce.URL)({id: 'javascript:'})).toEqual('some/javascript:');
+ expect($interpolate('{{foo}}{{bar}}', true, $sce.URL)({foo: 'javascript:', bar: 'javascript:'})).toEqual('unsafe:javascript:javascript:');
+ }));
+
+
+
});
diff --git a/test/ng/sceSpecs.js b/test/ng/sceSpecs.js
index f7c654df296a..fb169925c9ff 100644
--- a/test/ng/sceSpecs.js
+++ b/test/ng/sceSpecs.js
@@ -1,5 +1,7 @@
'use strict';
+/* eslint-disable no-script-url */
+
describe('SCE', function() {
describe('when disabled', function() {
@@ -211,7 +213,7 @@ describe('SCE', function() {
expect($sce.parseAsJs('"string"')()).toBe('string');
}));
- it('should be possible to do one-time binding', function() {
+ it('should be possible to do one-time binding on a non-concatenable context', function() {
module(provideLog);
inject(function($sce, $rootScope, log) {
$rootScope.$watch($sce.parseAsHtml('::foo'), function(value) {
@@ -236,6 +238,31 @@ describe('SCE', function() {
});
});
+ it('should be possible to do one-time binding on a concatenable context', function() {
+ module(provideLog);
+ inject(function($sce, $rootScope, log) {
+ $rootScope.$watch($sce.parseAsUrl('::foo'), function(value) {
+ log(value + '');
+ });
+
+ $rootScope.$digest();
+ expect(log).toEqual('undefined'); // initial listener call
+ log.reset();
+
+ $rootScope.foo = $sce.trustAs($sce.URL, 'trustedValue');
+ expect($rootScope.$$watchers.length).toBe(1);
+ $rootScope.$digest();
+
+ expect($rootScope.$$watchers.length).toBe(0);
+ expect(log).toEqual('trustedValue');
+ log.reset();
+
+ $rootScope.foo = $sce.trustAs($sce.URL, 'anotherTrustedValue');
+ $rootScope.$digest();
+ expect(log).toEqual(''); // watcher no longer active
+ });
+ });
+
it('should NOT parse constant non-literals', inject(function($sce) {
// Until there's a real world use case for this, we're disallowing
// constant non-literals. See $SceParseProvider.
@@ -525,6 +552,44 @@ describe('SCE', function() {
));
});
+ describe('URL-context sanitization', function() {
+ it('should sanitize values that are not whitelisted', inject(function($sce) {
+ expect($sce.getTrustedMediaUrl('javascript:foo')).toEqual('unsafe:javascript:foo');
+ expect($sce.getTrustedUrl('javascript:foo')).toEqual('unsafe:javascript:foo');
+ }));
+
+ it('should not sanitize values that are whitelisted', inject(function($sce) {
+ expect($sce.getTrustedMediaUrl('http://example.com')).toEqual('http://example.com');
+ expect($sce.getTrustedUrl('http://example.com')).toEqual('http://example.com');
+ }));
+
+ it('should not sanitize trusted values', inject(function($sce) {
+ expect($sce.getTrustedMediaUrl($sce.trustAsMediaUrl('javascript:foo'))).toEqual('javascript:foo');
+ expect($sce.getTrustedMediaUrl($sce.trustAsUrl('javascript:foo'))).toEqual('javascript:foo');
+ expect($sce.getTrustedMediaUrl($sce.trustAsResourceUrl('javascript:foo'))).toEqual('javascript:foo');
+
+ expect($sce.getTrustedUrl($sce.trustAsMediaUrl('javascript:foo'))).toEqual('unsafe:javascript:foo');
+ expect($sce.getTrustedUrl($sce.trustAsUrl('javascript:foo'))).toEqual('javascript:foo');
+ expect($sce.getTrustedUrl($sce.trustAsResourceUrl('javascript:foo'))).toEqual('javascript:foo');
+ }));
+
+ it('should use the $$sanitizeUri', function() {
+ var $$sanitizeUri = jasmine.createSpy('$$sanitizeUri').and.returnValue('someSanitizedUrl');
+ module(function($provide) {
+ $provide.value('$$sanitizeUri', $$sanitizeUri);
+ });
+ inject(function($sce) {
+ expect($sce.getTrustedMediaUrl('someUrl')).toEqual('someSanitizedUrl');
+ expect($$sanitizeUri).toHaveBeenCalledOnceWith('someUrl', true);
+
+ $$sanitizeUri.calls.reset();
+
+ expect($sce.getTrustedUrl('someUrl')).toEqual('someSanitizedUrl');
+ expect($$sanitizeUri).toHaveBeenCalledOnceWith('someUrl', false);
+ });
+ });
+ });
+
describe('sanitizing html', function() {
describe('when $sanitize is NOT available', function() {
it('should throw an exception for getTrusted(string) values', inject(function($sce) {
@@ -535,9 +600,23 @@ describe('SCE', function() {
describe('when $sanitize is available', function() {
beforeEach(function() { module('ngSanitize'); });
+
it('should sanitize html using $sanitize', inject(function($sce) {
expect($sce.getTrustedHtml('a
`;
@@ -125,11 +130,20 @@ function sendStoredFile(request, response) {
// without trailing slash
const base = request.originalUrl.endsWith('/') ? request.originalUrl : request.originalUrl + '/';
- let directoryListing = `
- Index of ${path}
-
- ${contentList}
`;
+ const directoryListing = `
+
+
+
+
+
+ Index of ${path}
+
+ ${contentList}
+
+ `;
return response
.status(200)
From 67f54b660038de2b4346b3e76d66a8dc8ccb1f9b Mon Sep 17 00:00:00 2001
From: Martin Staffa
+
+
+
{{ item }} | +
{{ item }} | +