Bug 1173171 - Disable external jar: via preference. r=jduell
authorJeff Gibat <jgibat@isecpartners.com>
Tue, 04 Aug 2015 23:55:00 -0400
changeset 287970 6ee1f299da22b5f8da7f4aaa31b5f15ed13593cc
parent 287969 6ee70eb03b02e2b57e5cdd1bdc0dedbe295c55a5
child 287971 72341fb355fa66c054b6a60ba35b2f87cbf65dfe
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjduell
bugs1173171
milestone42.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1173171 - Disable external jar: via preference. r=jduell
modules/libjar/nsJARChannel.cpp
modules/libjar/nsJARChannel.h
modules/libjar/test/mochitest/mochitest.ini
modules/libjar/test/mochitest/test_bug1173171.html
modules/libpref/init/all.js
--- a/modules/libjar/nsJARChannel.cpp
+++ b/modules/libjar/nsJARChannel.cpp
@@ -199,20 +199,23 @@ nsJARChannel::nsJARChannel()
     , mContentLength(-1)
     , mLoadFlags(LOAD_NORMAL)
     , mStatus(NS_OK)
     , mIsPending(false)
     , mIsUnsafe(true)
     , mOpeningRemote(false)
     , mSynthesizedStreamLength(0)
     , mForceNoIntercept(false)
+    , mBlockRemoteFiles(false)
 {
     if (!gJarProtocolLog)
         gJarProtocolLog = PR_NewLogModule("nsJarProtocol");
 
+    mBlockRemoteFiles = Preferences::GetBool("network.jar.block-remote-files", false);
+
     // hold an owning reference to the jar handler
     NS_ADDREF(gJarHandler);
 }
 
 nsJARChannel::~nsJARChannel()
 {
     NS_ReleaseOnMainThread(mLoadInfo);
 
@@ -1001,16 +1004,23 @@ nsJARChannel::ContinueAsyncOpen()
         mListener = nullptr;
         return rv;
     }
 
     nsCOMPtr<nsIChannel> channel;
 
     if (!mJarFile) {
         // Not a local file...
+
+        // Check preferences to see if all remote jar support should be disabled
+        if (mBlockRemoteFiles) {
+            mIsUnsafe = true;
+            return NS_ERROR_UNSAFE_CONTENT_TYPE;
+        }
+
         // kick off an async download of the base URI...
         nsCOMPtr<nsIStreamListener> downloader = new MemoryDownloader(this);
         uint32_t loadFlags =
             mLoadFlags & ~(LOAD_DOCUMENT_URI | LOAD_CALL_CONTENT_SNIFFERS);
         rv = NS_NewChannelInternal(getter_AddRefs(channel),
                                    mJarBaseURI,
                                    mLoadInfo,
                                    mLoadGroup,
@@ -1180,16 +1190,20 @@ nsJARChannel::OnDownloadComplete(MemoryD
                 mIsUnsafe = unsafe;
             }
         }
 
         channel->GetContentDispositionHeader(mContentDispositionHeader);
         mContentDisposition = NS_GetContentDispositionFromHeader(mContentDispositionHeader, this);
     }
 
+    // This is a defense-in-depth check for the preferences to see if all remote jar
+    // support should be disabled. This check may not be needed.
+    MOZ_RELEASE_ASSERT(!mBlockRemoteFiles);
+
     if (NS_SUCCEEDED(status) && mIsUnsafe &&
         !Preferences::GetBool("network.jar.open-unsafe-types", false)) {
         status = NS_ERROR_UNSAFE_CONTENT_TYPE;
     }
 
     if (NS_SUCCEEDED(status)) {
         // Refuse to unpack view-source: jars even if open-unsafe-types is set.
         nsCOMPtr<nsIViewSourceChannel> viewSource = do_QueryInterface(channel);
--- a/modules/libjar/nsJARChannel.h
+++ b/modules/libjar/nsJARChannel.h
@@ -134,12 +134,14 @@ private:
     nsCString                       mInnerJarEntry;
 
     nsRefPtr<nsInputStreamPump>     mSynthesizedResponsePump;
     int64_t                         mSynthesizedStreamLength;
 
     // True if this channel should skip any interception checks.
     bool                            mForceNoIntercept;
 
+    // True if this channel should not download any remote files.
+    bool                            mBlockRemoteFiles;
     friend class mozilla::net::InterceptedJARChannel;
 };
 
 #endif // nsJARChannel_h__
--- a/modules/libjar/test/mochitest/mochitest.ini
+++ b/modules/libjar/test/mochitest/mochitest.ini
@@ -3,8 +3,9 @@ skip-if = buildapp == 'b2g'
 support-files =
   bug403331.zip
   bug403331.zip^headers^
   openredirect.sjs
 
 [test_bug403331.html]
 [test_bug1034143_mapped.html]
 run-if = os == 'linux'
+[test_bug1173171.html]
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/modules/libjar/test/mochitest/test_bug1173171.html
@@ -0,0 +1,66 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1173171
+-->
+<head>
+  <title>Test for Bug 1173171</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<iframe id="testFrame"></iframe>
+
+<pre id="test">
+<script class="testbody" type="application/javascript;version=1.7">
+
+/** Test for Bug 1173171 **/
+
+// __setPref(key, value)__.
+// Set a pref value asynchronously, returning a prmoise that resolves
+// when it succeeds.
+let pushPref = function (key, value) {
+  return new Promise(function(resolve, reject) {
+    SpecialPowers.pushPrefEnv({"set": [[key, value]]}, resolve);
+  });
+};
+
+// __xhr(method, url, responseType__.
+// A simple async XMLHttpRequest call.
+// Returns a promise with the response.
+let xhr = function (method, url, responseType) {
+  return new Promise(function (resolve, reject) {
+    let xhr = new XMLHttpRequest();
+    xhr.open(method, url, true);
+    xhr.onload = function () {
+      resolve(xhr.response);
+    };
+    xhr.responseType = responseType;
+    xhr.send();
+  });
+};
+
+let jarURL = "jar:http://mochi.test:8888/tests/modules/libjar/test/mochitest/bug403331.zip!/test.html";
+
+// Test behavior when blocking is deactivated and activated.
+add_task(function* () {
+  for (let shouldBlock of [false, true]) {
+    yield pushPref("network.jar.block-remote-files", shouldBlock);
+    try {
+      let response = yield xhr("GET", jarURL, "document");
+      didBlock = false;
+    } catch (e) {
+      didBlock = true;
+    }
+    ok(didBlock === shouldBlock,
+       "Remote jars should be blocked if and only if the 'network.jar.block-remote-files' pref is active.");
+  }
+});
+
+</script>
+</pre>
+
+</body>
+</html>
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1501,16 +1501,18 @@ pref("network.websocket.delay-failed-rec
 pref("dom.server-events.enabled", true);
 // Equal to the DEFAULT_RECONNECTION_TIME_VALUE value in nsEventSource.cpp
 pref("dom.server-events.default-reconnection-time", 5000); // in milliseconds
 
 // If false, remote JAR files that are served with a content type other than
 // application/java-archive or application/x-jar will not be opened
 // by the jar channel.
 pref("network.jar.open-unsafe-types", false);
+// If true, loading remote JAR files using the jar: protocol will be prevented.
+pref("network.jar.block-remote-files", false);
 
 // This preference, if true, causes all UTF-8 domain names to be normalized to
 // punycode.  The intention is to allow UTF-8 domain names as input, but never
 // generate them from punycode.
 pref("network.IDN_show_punycode", false);
 
 // If "network.IDN.use_whitelist" is set to true, TLDs with
 // "network.IDN.whitelist.tld" explicitly set to true are treated as