Bug 1406278: Part 3 - Use subject principal as triggering principal in <script> "src" attribute. r?bz
MozReview-Commit-ID: KwGIE4t7KUx
--- a/dom/html/HTMLScriptElement.cpp
+++ b/dom/html/HTMLScriptElement.cpp
@@ -164,19 +164,19 @@ HTMLScriptElement::SetDefer(bool aDefer,
bool
HTMLScriptElement::Defer()
{
return GetBoolAttr(nsGkAtoms::defer);
}
void
-HTMLScriptElement::SetSrc(const nsAString& aSrc, ErrorResult& rv)
+HTMLScriptElement::SetSrc(const nsAString& aSrc, nsIPrincipal& aPrincipal, ErrorResult& rv)
{
- rv = SetAttrHelper(nsGkAtoms::src, aSrc);
+ SetHTMLAttr(nsGkAtoms::src, aSrc, aPrincipal, rv);
}
void
HTMLScriptElement::SetType(const nsAString& aType, ErrorResult& rv)
{
SetHTMLAttr(nsGkAtoms::type, aType, rv);
}
@@ -237,16 +237,21 @@ HTMLScriptElement::AfterSetAttr(int32_t
const nsAttrValue* aValue,
const nsAttrValue* aOldValue,
nsIPrincipal* aSubjectPrincipal,
bool aNotify)
{
if (nsGkAtoms::async == aName && kNameSpaceID_None == aNamespaceID) {
mForceAsync = false;
}
+ if (nsGkAtoms::src == aName && kNameSpaceID_None == aNamespaceID) {
+ mSrcTriggeringPrincipal = nsContentUtils::GetAttrTriggeringPrincipal(
+ this, aValue ? aValue->GetStringValue() : EmptyString(),
+ aSubjectPrincipal);
+ }
return nsGenericHTMLElement::AfterSetAttr(aNamespaceID, aName, aValue,
aOldValue, aSubjectPrincipal, aNotify);
}
NS_IMETHODIMP
HTMLScriptElement::GetInnerHTML(nsAString& aInnerHTML)
{
if (!nsContentUtils::GetNodeTextContent(this, false, aInnerHTML, fallible)) {
--- a/dom/html/HTMLScriptElement.h
+++ b/dom/html/HTMLScriptElement.h
@@ -62,17 +62,21 @@ public:
nsIPrincipal* aSubjectPrincipal,
bool aNotify) override;
// WebIDL
void SetText(const nsAString& aValue, ErrorResult& rv);
void SetCharset(const nsAString& aCharset, ErrorResult& rv);
void SetDefer(bool aDefer, ErrorResult& rv);
bool Defer();
- void SetSrc(const nsAString& aSrc, ErrorResult& rv);
+ void SetSrc(const nsAString& aSrc, nsIPrincipal& aPrincipal, ErrorResult& rv);
+ void GetSrc(nsString& aSrc, nsIPrincipal&)
+ {
+ GetSrc(aSrc);
+ };
void SetType(const nsAString& aType, ErrorResult& rv);
void SetHtmlFor(const nsAString& aHtmlFor, ErrorResult& rv);
void SetEvent(const nsAString& aEvent, ErrorResult& rv);
void GetCrossOrigin(nsAString& aResult)
{
// Null for both missing and invalid defaults is ok, since we
// always parse to an enum value, so we don't need an invalid
// default, and we _want_ the missing default to be null.
--- a/dom/script/ScriptLoadRequest.h
+++ b/dom/script/ScriptLoadRequest.h
@@ -175,16 +175,17 @@ public:
// Holds the SRI serialized hash and the script bytecode for non-inline
// scripts.
mozilla::Vector<uint8_t> mScriptBytecode;
uint32_t mBytecodeOffset; // Offset of the bytecode in mScriptBytecode
uint32_t mJSVersion;
nsCOMPtr<nsIURI> mURI;
+ nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
nsCOMPtr<nsIPrincipal> mOriginPrincipal;
nsAutoCString mURL; // Keep the URI's filename alive during off thread parsing.
int32_t mLineNo;
const mozilla::CORSMode mCORSMode;
const mozilla::dom::SRIMetadata mIntegrity;
mozilla::net::ReferrerPolicy mReferrerPolicy;
// Holds the Cache information, which is used to register the bytecode
--- a/dom/script/ScriptLoader.cpp
+++ b/dom/script/ScriptLoader.cpp
@@ -746,16 +746,17 @@ ScriptLoader::StartFetchingModuleAndDepe
MOZ_ASSERT(aURI);
RefPtr<ModuleLoadRequest> childRequest =
new ModuleLoadRequest(aRequest->mElement, aRequest->mJSVersion,
aRequest->mCORSMode, aRequest->mIntegrity, this);
childRequest->mIsTopLevel = false;
childRequest->mURI = aURI;
+ childRequest->mTriggeringPrincipal = aRequest->mTriggeringPrincipal;
childRequest->mIsInline = false;
childRequest->mReferrerPolicy = aRequest->mReferrerPolicy;
childRequest->mParent = aRequest;
aRequest->mImports.AppendElement(childRequest);
if (LOG_ENABLED()) {
nsAutoCString url1;
aRequest->mURI->GetAsciiSpec(url1);
@@ -1014,25 +1015,27 @@ ScriptLoader::StartLoad(ScriptLoadReques
securityFlags |= nsILoadInfo::SEC_COOKIES_SAME_ORIGIN;
} else if (aRequest->mCORSMode == CORS_USE_CREDENTIALS) {
securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
}
}
securityFlags |= nsILoadInfo::SEC_ALLOW_CHROME;
nsCOMPtr<nsIChannel> channel;
- nsresult rv = NS_NewChannel(getter_AddRefs(channel),
- aRequest->mURI,
- context,
- securityFlags,
- contentPolicyType,
- loadGroup,
- prompter,
- nsIRequest::LOAD_NORMAL |
- nsIChannel::LOAD_CLASSIFY_URI);
+ nsresult rv = NS_NewChannelWithTriggeringPrincipal(
+ getter_AddRefs(channel),
+ aRequest->mURI,
+ context,
+ aRequest->mTriggeringPrincipal,
+ securityFlags,
+ contentPolicyType,
+ loadGroup,
+ prompter,
+ nsIRequest::LOAD_NORMAL |
+ nsIChannel::LOAD_CLASSIFY_URI);
NS_ENSURE_SUCCESS(rv, rv);
// To avoid decoding issues, the JSVersion is explicitly guarded here, and the
// build-id is part of the JSBytecodeMimeType constant.
aRequest->mCacheInfo = nullptr;
nsCOMPtr<nsICacheInfoChannel> cic(do_QueryInterface(channel));
if (cic && nsContentUtils::IsBytecodeCacheEnabled() &&
@@ -1367,19 +1370,25 @@ ScriptLoader::ProcessScriptElement(nsISc
if (mDocument->GetDocumentURI()) {
mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
}
SRICheck::IntegrityMetadata(integrity, sourceUri, mReporter,
&sriMetadata);
}
}
+ nsCOMPtr<nsIPrincipal> principal = aElement->GetScriptURITriggeringPrincipal();
+ if (!principal) {
+ principal = scriptContent->NodePrincipal();
+ }
+
request = CreateLoadRequest(scriptKind, aElement, version, ourCORSMode,
sriMetadata);
request->mURI = scriptURI;
+ request->mTriggeringPrincipal = Move(principal);
request->mIsInline = false;
request->mReferrerPolicy = ourRefPolicy;
// keep request->mScriptFromHead to false so we don't treat non preloaded
// scripts as blockers for full page load. See bug 792438.
rv = StartLoad(request);
if (NS_FAILED(rv)) {
const char* message = "ScriptSourceLoadFailed";
@@ -3099,16 +3108,17 @@ ScriptLoader::PreloadURI(nsIURI* aURI, c
}
SRICheck::IntegrityMetadata(aIntegrity, sourceUri, mReporter, &sriMetadata);
}
RefPtr<ScriptLoadRequest> request =
CreateLoadRequest(ScriptKind::Classic, nullptr, 0,
Element::StringToCORSMode(aCrossOrigin), sriMetadata);
request->mURI = aURI;
+ request->mTriggeringPrincipal = mDocument->NodePrincipal();
request->mIsInline = false;
request->mReferrerPolicy = aReferrerPolicy;
request->mScriptFromHead = aScriptFromHead;
request->mPreloadAsAsync = aAsync;
request->mPreloadAsDefer = aDefer;
nsresult rv = StartLoad(request);
if (NS_FAILED(rv)) {
--- a/dom/script/nsIScriptElement.h
+++ b/dom/script/nsIScriptElement.h
@@ -61,16 +61,22 @@ public:
* this is assumed to be an inline script element.
*/
nsIURI* GetScriptURI()
{
NS_PRECONDITION(mFrozen, "Not ready for this call yet!");
return mUri;
}
+ nsIPrincipal* GetScriptURITriggeringPrincipal()
+ {
+ NS_PRECONDITION(mFrozen, "Not ready for this call yet!");
+ return mSrcTriggeringPrincipal;
+ }
+
/**
* Script source text for inline script elements.
*/
virtual void GetScriptText(nsAString& text) = 0;
virtual void GetScriptCharset(nsAString& charset) = 0;
/**
@@ -317,16 +323,21 @@ protected:
mozilla::dom::FromParser mParserCreated;
/**
* The effective src (or null if no src).
*/
nsCOMPtr<nsIURI> mUri;
/**
+ * The triggering principal for the src URL.
+ */
+ nsCOMPtr<nsIPrincipal> mSrcTriggeringPrincipal;
+
+ /**
* The creator parser of a non-defer, non-async parser-inserted script.
*/
nsWeakPtr mCreatorParser;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsIScriptElement, NS_ISCRIPTELEMENT_IID)
#endif // nsIScriptElement_h___
--- a/dom/webidl/HTMLScriptElement.webidl
+++ b/dom/webidl/HTMLScriptElement.webidl
@@ -5,17 +5,17 @@
*
* The origin of this IDL file is
* http://www.whatwg.org/specs/web-apps/current-work/#the-script-element
* http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
*/
[HTMLConstructor]
interface HTMLScriptElement : HTMLElement {
- [CEReactions, SetterThrows]
+ [CEReactions, NeedsSubjectPrincipal, SetterThrows]
attribute DOMString src;
[CEReactions, SetterThrows]
attribute DOMString type;
[CEReactions, SetterThrows, Pref="dom.moduleScripts.enabled"]
attribute boolean noModule;
[CEReactions, SetterThrows]
attribute DOMString charset;
[CEReactions, SetterThrows]
--- a/toolkit/components/extensions/test/mochitest/test_ext_web_accessible_resources.html
+++ b/toolkit/components/extensions/test/mochitest/test_ext_web_accessible_resources.html
@@ -202,17 +202,19 @@ add_task(async function test_web_accessi
window.addEventListener("message", function rcv(event) {
browser.runtime.sendMessage("script-ran");
window.removeEventListener("message", rcv);
});
testImageLoading(browser.extension.getURL("image.png"), "loaded");
let testScriptElement = document.createElement("script");
- testScriptElement.setAttribute("src", browser.extension.getURL("test_script.js"));
+ // Set the src via wrappedJSObject so the load is triggered with the
+ // content page's principal rather than ours.
+ testScriptElement.wrappedJSObject.setAttribute("src", browser.extension.getURL("test_script.js"));
document.head.appendChild(testScriptElement);
browser.runtime.sendMessage("script-loaded");
}
function testScript() {
window.postMessage("test-script-loaded", "*");
}
@@ -294,17 +296,19 @@ add_task(async function test_web_accessi
browser.test.sendMessage("background-ready");
}
function content() {
testImageLoading("http://example.com/tests/toolkit/components/extensions/test/mochitest/file_image_bad.png", "blocked");
testImageLoading(browser.extension.getURL("image.png"), "loaded");
let testScriptElement = document.createElement("script");
- testScriptElement.setAttribute("src", browser.extension.getURL("test_script.js"));
+ // Set the src via wrappedJSObject so the load is triggered with the
+ // content page's principal rather than ours.
+ testScriptElement.wrappedJSObject.setAttribute("src", browser.extension.getURL("test_script.js"));
document.head.appendChild(testScriptElement);
window.addEventListener("message", event => {
browser.runtime.sendMessage(event.data);
});
}
function testScript() {
--- a/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_triggeringPrincipal.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_triggeringPrincipal.js
@@ -430,16 +430,21 @@ add_task(async function test_contentscri
element: ["img", {}],
src: "img.png",
},
{
element: ["img", {}],
src: "imgset.png",
srcAttr: "srcset",
},
+ {
+ element: ["script", {}],
+ src: "script.js",
+ liveSrc: false,
+ },
];
/**
* A set of sources for which each of the above tests is expected to
* generate one request, if each of the properties in the value object
* matches the value of the same property in the test object.
*/
const SOURCES = {