',
)($rootScope);
expect(attrs.title).toBeUndefined();
expect(attrs.$attr.title).toBeUndefined();
expect(attrs.ngPropTitle).toBe("12");
expect(attrs.$attr.ngPropTitle).toBe("ng-prop-title");
expect(attrs.superTitle).toBeUndefined();
expect(attrs.$attr.superTitle).toBeUndefined();
expect(attrs.ngPropSuperTitle).toBe("34");
expect(attrs.$attr.ngPropSuperTitle).toBe("ng-prop-super-title");
expect(attrs.myCamelTitle).toBeUndefined();
expect(attrs.$attr.myCamelTitle).toBeUndefined();
expect(attrs.ngPropMyCamelTitle).toBe("56");
expect(attrs.$attr.ngPropMyCamelTitle).toBe("ng-prop-my-camel-title");
});
it("should not conflict with (ng-attr-)attribute mappings of the same name", () => {
let attrs;
compileProvider.directive(
"attrExposer",
valueFn({
link($scope, $element, $attrs) {
attrs = $attrs;
},
}),
);
$compile(
'
',
)($rootScope);
expect(attrs.title).toBe("foo");
expect(attrs.$attr.title).toBe("title");
expect(attrs.$attr.ngPropTitle).toBe("ng-prop-title");
});
it("should disallow property binding to onclick", () => {
// All event prop bindings are disallowed.
expect(() => {
$compile('
');
}).toThrowError(/nodomevents/);
expect(() => {
$compile('
');
}).toThrowError(/nodomevents/);
});
it("should process property bindings in pre-linking phase at priority 100", () => {
compileProvider.directive("propLog", () => ({
compile($element, $attrs) {
logs.push(`compile=${$element[0].myName}`);
return {
pre($scope, $element, $attrs) {
logs.push(`preLinkP0=${$element[0].myName}`);
$rootScope.name = "pre0";
},
post($scope, $element, $attrs) {
logs.push(`postLink=${$element[0].myName}`);
$rootScope.name = "post0";
},
};
},
}));
compileProvider.directive("propLogHighPriority", () => ({
priority: 101,
compile() {
return {
pre($scope, $element, $attrs) {
logs.push(`preLinkP101=${$element[0].myName}`);
$rootScope.name = "pre101";
},
};
},
}));
const element = $compile(
'
',
)($rootScope);
$rootScope.name = "loader";
$rootScope.$apply();
logs.push(`digest=${element[0].myName}`);
expect(logs.join("; ")).toEqual(
"compile=undefined; preLinkP101=undefined; preLinkP0=pre101; postLink=pre101; digest=loader",
);
});
describe("img[src] sanitization", () => {
it("should accept trusted values", () => {
const 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();");
expect(element[0].src).toEqual("someuntrustedthing:foo();");
});
it("should use $$sanitizeUri", () => {
const $$sanitizeUri = jasmine
.createSpy("$$sanitizeUri")
.and.returnValue("someSanitizedUrl");
createInjector([
"myModule",
($provide) => {
$provide.value("$$sanitizeUri", $$sanitizeUri);
},
]).invoke((_$compile_, _$rootScope_) => {
$compile = _$compile_;
$rootScope = _$rootScope_;
});
const element = $compile('
')($rootScope);
$rootScope.testUrl = "someUrl";
$rootScope.$apply();
expect(element[0].src).toMatch(/^http:\/\/.*\/someSanitizedUrl$/);
expect($$sanitizeUri).toHaveBeenCalledWith($rootScope.testUrl, true);
});
it("should not use $$sanitizeUri with trusted values", () => {
const $$sanitizeUri = jasmine
.createSpy("$$sanitizeUri")
.and.throwError("Should not have been called");
createInjector([
"myModule",
($provide) => {
$provide.value("$$sanitizeUri", $$sanitizeUri);
},
]).invoke((_$compile_, _$rootScope_, _$sce_) => {
$compile = _$compile_;
$rootScope = _$rootScope_;
$sce = _$sce_;
});
const 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[0].src).toBe("untrusted:foo();");
});
});
describe("a[href] sanitization", () => {
it("should NOT require trusted values for trusted URI values", () => {
$rootScope.testUrl = "http://example.com/image.png"; // `http` is trusted
let element = $compile('
')($rootScope);
expect(element[0].href).toEqual("http://example.com/image.png");
element = $compile('
')($rootScope);
expect(element[0].href).toEqual("http://example.com/image.png");
});
it("should accept trusted values for non-trusted URI values", () => {
$rootScope.testUrl = $sce.trustAsUrl("javascript:foo()"); // `javascript` is not trusted
let element = $compile('
')($rootScope);
expect(element[0].href).toEqual("javascript:foo()");
element = $compile('
')($rootScope);
expect(element[0].href).toEqual("javascript:foo()");
});
it("should sanitize non-trusted values", () => {
$rootScope.testUrl = "javascript:foo()"; // `javascript` is not trusted
let element = $compile('
')($rootScope);
expect(element[0].href).toEqual("unsafe:javascript:foo()");
element = $compile('
')($rootScope);
expect(element[0].href).toEqual("unsafe:javascript:foo()");
});
it("should not sanitize href on elements other than anchor", () => {
const element = $compile('
')(
$rootScope,
);
$rootScope.testUrl = "javascript:doEvilStuff()";
$rootScope.$apply();
expect(element[0].href).toBe("javascript:doEvilStuff()");
});
it("should not sanitize properties other then those configured", () => {
const element = $compile('
')($rootScope);
$rootScope.testUrl = "javascript:doEvilStuff()";
$rootScope.$apply();
expect(element[0].title).toBe("javascript:doEvilStuff()");
});
it("should use $$sanitizeUri", () => {
const $$sanitizeUri = jasmine
.createSpy("$$sanitizeUri")
.and.returnValue("someSanitizedUrl");
createInjector([
"myModule",
($provide) => {
$provide.value("$$sanitizeUri", $$sanitizeUri);
},
]).invoke((_$compile_, _$rootScope_) => {
$compile = _$compile_;
$rootScope = _$rootScope_;
});
let element = $compile('
')($rootScope);
$rootScope.testUrl = "someUrl";
$rootScope.$apply();
expect(element[0].href).toMatch(/^http:\/\/.*\/someSanitizedUrl$/);
expect($$sanitizeUri).toHaveBeenCalledWith($rootScope.testUrl, false);
$$sanitizeUri.calls.reset();
element = $compile('
')($rootScope);
$rootScope.$apply();
expect(element[0].href).toMatch(/^http:\/\/.*\/someSanitizedUrl$/);
expect($$sanitizeUri).toHaveBeenCalledWith($rootScope.testUrl, false);
});
it("should not have endless digests when given arrays in concatenable context", () => {
const element = $compile(
'
' +
"
",
)($rootScope);
$rootScope.testUrl = [1];
$rootScope.testUrl = [];
$rootScope.testUrl = { a: "b" };
$rootScope.testUrl = {};
});
});
describe("iframe[src]", () => {
beforeEach(() => {
createInjector(["myModule"]).invoke(
(_$compile_, _$rootScope_, _$sce_) => {
$compile = _$compile_;
$rootScope = _$rootScope_;
$sce = _$sce_;
},
);
});
it("should pass through src properties for the same domain", () => {
const element = $compile('
')(
$rootScope,
);
$rootScope.testUrl = "different_page";
$rootScope.$apply();
expect(element[0].src).toMatch(/\/different_page$/);
});
it("should clear out src properties for a different domain", () => {
const element = $compile('
')(
$rootScope,
);
$rootScope.testUrl = "http://a.different.domain.example.com";
expect(() => {
$rootScope.$apply();
}).toThrowError(/insecurl/);
});
it("should clear out JS src properties", () => {
const element = $compile('
')(
$rootScope,
);
$rootScope.testUrl = "javascript:alert(1);";
expect(() => {
$rootScope.$apply();
}).toThrowError(/insecurl/);
});
it("should clear out non-resource_url src properties", () => {
const element = $compile('
')(
$rootScope,
);
$rootScope.testUrl = $sce.trustAsUrl("javascript:doTrustedStuff()");
expect(() => {
$rootScope.$apply();
}).toThrowError(/insecurl/);
});
it("should pass through $sce.trustAs() values in src properties", () => {
const element = $compile('
')(
$rootScope,
);
$rootScope.testUrl = $sce.trustAsResourceUrl(
"javascript:doTrustedStuff()",
);
$rootScope.$apply();
expect(element[0].src).toEqual("javascript:doTrustedStuff()");
});
});
describe("base[href]", () => {
beforeEach(() => {
createInjector(["myModule"]).invoke(
(_$compile_, _$rootScope_, _$sce_) => {
$compile = _$compile_;
$rootScope = _$rootScope_;
$sce = _$sce_;
},
);
});
it("should be a RESOURCE_URL context", () => {
const element = $compile('
')($rootScope);
$rootScope.testUrl = $sce.trustAsResourceUrl("https://example.com/");
$rootScope.$apply();
expect(element[0].href).toContain("https://example.com/");
$rootScope.testUrl = "https://not.example.com/";
expect(() => {
$rootScope.$apply();
}).toThrowError(/insecurl/);
});
});
describe("form[action]", () => {
beforeEach(() => {
createInjector(["myModule"]).invoke(
(_$compile_, _$rootScope_, _$sce_) => {
$compile = _$compile_;
$rootScope = _$rootScope_;
$sce = _$sce_;
},
);
});
it("should pass through action property for the same domain", () => {
const element = $compile('
')(
$rootScope,
);
$rootScope.testUrl = "different_page";
$rootScope.$apply();
expect(element[0].action).toMatch(/\/different_page$/);
});
it("should clear out action property for a different domain", () => {
const element = $compile('
')(
$rootScope,
);
$rootScope.testUrl = "http://a.different.domain.example.com";
expect(() => {
$rootScope.$apply();
}).toThrowError(/insecurl/);
});
it("should clear out JS action property", () => {
const element = $compile('
')(
$rootScope,
);
$rootScope.testUrl = "javascript:alert(1);";
expect(() => {
$rootScope.$apply();
}).toThrowError(/insecurl/);
});
it("should clear out non-resource_url action property", () => {
const element = $compile('
')(
$rootScope,
);
$rootScope.testUrl = $sce.trustAsUrl("javascript:doTrustedStuff()");
expect(() => {
$rootScope.$apply();
}).toThrowError(/insecurl/);
});
it("should pass through $sce.trustAsResourceUrl() values in action property", () => {
const element = $compile('
')(
$rootScope,
);
$rootScope.testUrl = $sce.trustAsResourceUrl(
"javascript:doTrustedStuff()",
);
$rootScope.$apply();
expect(element[0].action).toEqual("javascript:doTrustedStuff()");
});
});
describe("link[href]", () => {
beforeEach(() => {
createInjector(["myModule"]).invoke(
(_$compile_, _$rootScope_, _$sce_) => {
$compile = _$compile_;
$rootScope = _$rootScope_;
$sce = _$sce_;
},
);
});
it("should reject invalid RESOURCE_URLs", () => {
const element = $compile(
'
',
)($rootScope);
$rootScope.testUrl = "https://evil.example.org/css.css";
expect(() => {
$rootScope.$apply();
}).toThrowError(/insecurl/);
});
it("should accept valid RESOURCE_URLs", () => {
const element = $compile(
'
',
)($rootScope);
$rootScope.testUrl = "./css1.css";
$rootScope.$apply();
expect(element[0].href).toContain("css1.css");
$rootScope.testUrl = $sce.trustAsResourceUrl(
"https://elsewhere.example.org/css2.css",
);
$rootScope.$apply();
expect(element[0].href).toContain(
"https://elsewhere.example.org/css2.css",
);
});
});
describe("*[innerHTML]", () => {
describe("SCE disabled", () => {
beforeEach(() => {
dealoc(document.getElementById("dummy"));
window.angular
.bootstrap(document.getElementById("dummy"), [
"myModule",
($sceProvider) => {
$sceProvider.enabled(false);
},
])
.invoke((_$compile_, _$rootScope_, _$sce_) => {
$compile = _$compile_;
$rootScope = _$rootScope_;
$sce = _$sce_;
});
});
it("should set html", () => {
const element = $compile('
')(
$rootScope,
);
$rootScope.html = '
hello
';
expect(element.html()).toEqual('
hello
');
});
it("should update html", () => {
const element = $compile('
')(
$rootScope,
);
$rootScope.html = "hello";
expect(element.html()).toEqual("hello");
$rootScope.html = "goodbye";
expect(element.html()).toEqual("goodbye");
});
it("should one-time bind if the expression starts with two colons", () => {
const element = $compile('
')(
$rootScope,
);
$rootScope.html = '
hello
';
expect($rootScope.$$watchers.length).toEqual(1);
expect(element.text()).toEqual("hello");
expect($rootScope.$$watchers.length).toEqual(0);
$rootScope.html = '
hello
';
expect(element.text()).toEqual("hello");
});
});
describe("SCE enabled", () => {
beforeEach(() => {
createInjector([
"myModule",
($sceProvider) => {
$sceProvider.enabled(true);
},
]).invoke((_$compile_, _$rootScope_, _$sce_) => {
$compile = _$compile_;
$rootScope = _$rootScope_;
$sce = _$sce_;
});
});
it("should NOT set html for untrusted values", () => {
const element = $compile('
')(
$rootScope,
);
$rootScope.html = '
hello
';
expect(() => {}).toThrowError(/unsafe/);
});
it("should NOT set html for wrongly typed values", () => {
const element = $compile('
')(
$rootScope,
);
$rootScope.html = $sce.trustAsCss('
hello
');
expect(() => {}).toThrowError(/unsafe/);
});
it("should set html for trusted values", () => {
const element = $compile('
')(
$rootScope,
);
$rootScope.html = $sce.trustAsHtml('
hello
');
expect(element.html()).toEqual('
hello
');
});
it("should update html", () => {
const element = $compile('
')(
$rootScope,
);
$rootScope.html = $sce.trustAsHtml("hello");
expect(element.html()).toEqual("hello");
$rootScope.html = $sce.trustAsHtml("goodbye");
expect(element.html()).toEqual("goodbye");
});
it("should not cause infinite recursion for trustAsHtml object watches", () => {
// Ref: https://github.com/angular/angular.js/issues/3932
// If the binding is a function that creates a new value on every call via trustAs, we'll
// trigger an infinite digest if we don't take care of it.
const element = $compile(
'
',
)($rootScope);
$rootScope.getHtml = function () {
return $sce.trustAsHtml('
hello
');
};
expect(element.html()).toEqual('
hello
');
});
it("should handle custom $sce objects", () => {
function MySafeHtml(val) {
this.val = val;
}
createInjector([
"myModule",
($provide) => {
$provide.decorator("$sce", ($delegate) => {
$delegate.trustAsHtml = function (html) {
return new MySafeHtml(html);
};
$delegate.getTrusted = function (type, mySafeHtml) {
return mySafeHtml && mySafeHtml.val;
};
$delegate.valueOf = function (v) {
return v instanceof MySafeHtml ? v.val : v;
};
return $delegate;
});
},
]).invoke((_$compile_, _$rootScope_, _$sce_) => {
$compile = _$compile_;
$rootScope = _$rootScope_;
$sce = _$sce_;
});
// Ref: https://github.com/angular/angular.js/issues/14526
// Previous code used toString for change detection, which fails for custom objects
// that don't override toString.
const element = $compile(
'
',
)($rootScope);
let html = "hello";
$rootScope.getHtml = function () {
return $sce.trustAsHtml(html);
};
expect(element.html()).toEqual("hello");
html = "goodbye";
expect(element.html()).toEqual("goodbye");
});
});
});
describe("*[style]", () => {
it("should NOT set style for untrusted values", () => {
const element = $compile('
')($rootScope);
$rootScope.style = "margin-left: 10px";
expect(() => {}).toThrowError(/unsafe/);
});
it("should NOT set style for wrongly typed values", () => {
const element = $compile('
')($rootScope);
$rootScope.style = $sce.trustAsHtml("margin-left: 10px");
expect(() => {}).toThrowError(/unsafe/);
});
it("should set style for trusted values", () => {
const element = $compile('
')($rootScope);
$rootScope.style = $sce.trustAsCss("margin-left: 10px");
expect(element[0].style["margin-left"]).toEqual("10px");
});
});
});