|
| 1 | +diff --git a/platform/core.network/src/org/netbeans/core/network/proxy/pac/impl/NbPacScriptEvaluator.java b/platform/core.network/src/org/netbeans/core/network/proxy/pac/impl/NbPacScriptEvaluator.java |
| 2 | +index 76bb6080c73c..a708698bd008 100644 |
| 3 | +--- a/platform/core.network/src/org/netbeans/core/network/proxy/pac/impl/NbPacScriptEvaluator.java |
| 4 | ++++ b/platform/core.network/src/org/netbeans/core/network/proxy/pac/impl/NbPacScriptEvaluator.java |
| 5 | +@@ -26,6 +26,7 @@ |
| 6 | + import java.util.LinkedList; |
| 7 | + import java.util.List; |
| 8 | + import java.util.StringTokenizer; |
| 9 | ++import java.util.concurrent.atomic.AtomicReference; |
| 10 | + import java.util.logging.Level; |
| 11 | + import java.util.logging.Logger; |
| 12 | + import java.util.regex.Matcher; |
| 13 | +@@ -46,6 +47,9 @@ |
| 14 | + import org.netbeans.core.network.proxy.pac.PacUtils; |
| 15 | + import org.openide.util.Lookup; |
| 16 | + import org.openide.util.NbBundle; |
| 17 | ++import org.openide.util.RequestProcessor; |
| 18 | ++import org.openide.util.RequestProcessor.Task; |
| 19 | ++import org.netbeans.core.ProxySettings; |
| 20 | + |
| 21 | + /** |
| 22 | + * NetBeans implementation of a PAC script evaluator. This implementation |
| 23 | +@@ -196,6 +200,8 @@ public class NbPacScriptEvaluator implements PacScriptEvaluator { |
| 24 | + private static final String PAC_SOCKS5_FFEXT = "SOCKS5"; // Mozilla Firefox extension. Not part of original Netscape spec. |
| 25 | + private static final String PAC_HTTP_FFEXT = "HTTP"; // Mozilla Firefox extension. Not part of original Netscape spec. |
| 26 | + private static final String PAC_HTTPS_FFEXT = "HTTPS"; // Mozilla Firefox extension. Not part of original Netscape spec. |
| 27 | ++ private static class RPSingleton { private static final RequestProcessor instance = new RequestProcessor(NbPacScriptEvaluator.class.getName(), Runtime.getRuntime().availableProcessors(), true, false); } |
| 28 | ++ private static RequestProcessor getRequestProcessor() { return RPSingleton.instance; } |
| 29 | + private final String pacScriptSource; |
| 30 | + |
| 31 | + |
| 32 | +@@ -213,7 +219,8 @@ public NbPacScriptEvaluator(String pacSourceCocde) throws PacParsingException { |
| 33 | + @Override |
| 34 | + public List<Proxy> findProxyForURL(URI uri) throws PacValidationException { |
| 35 | + |
| 36 | +- List<Proxy> jsResultAnalyzed; |
| 37 | ++ AtomicReference<List<Proxy>> resultHolder = new AtomicReference<>(null); |
| 38 | ++ List<Proxy> jsResultAnalyzed = null; |
| 39 | + |
| 40 | + // First try the cache |
| 41 | + if (resultCache != null) { |
| 42 | +@@ -222,38 +229,36 @@ public List<Proxy> findProxyForURL(URI uri) throws PacValidationException { |
| 43 | + return jsResultAnalyzed; |
| 44 | + } |
| 45 | + } |
| 46 | +- try { |
| 47 | +- Object jsResult; |
| 48 | +- synchronized (scriptEngine) { |
| 49 | +- jsResult = scriptEngine.findProxyForURL(PacUtils.toStrippedURLStr(uri), uri.getHost()); |
| 50 | +- } |
| 51 | +- jsResultAnalyzed = analyzeResult(uri, jsResult); |
| 52 | +- if (canUseURLCaching && (resultCache != null)) { |
| 53 | +- resultCache.put(uri, jsResultAnalyzed); // save the result in the cache |
| 54 | +- } |
| 55 | +- return jsResultAnalyzed; |
| 56 | +- } catch (NoSuchMethodException ex) { |
| 57 | +- // If this exception occur at this time it is really, really unexpected. |
| 58 | +- // We already gave the function a test spin in the constructor. |
| 59 | +- Exceptions.printStackTrace(ex); |
| 60 | +- return Collections.singletonList(Proxy.NO_PROXY); |
| 61 | +- } catch (ScriptException ex) { |
| 62 | +- LOGGER.log(Level.WARNING, "Error when executing PAC script function " + scriptEngine.getJsMainFunction().getJsFunctionName() + " : ", ex); |
| 63 | +- return Collections.singletonList(Proxy.NO_PROXY); |
| 64 | +- } catch (Exception ex) { // for runtime exceptions |
| 65 | +- if (ex.getCause() != null) { |
| 66 | +- if (ex.getCause() instanceof ClassNotFoundException) { |
| 67 | +- // Is someone trying to break out of the sandbox ? |
| 68 | +- LOGGER.log(Level.WARNING, "The downloaded PAC script is attempting to access Java class ''{0}'' which may be a sign of maliciousness. You should investigate this with your network administrator.", ex.getCause().getMessage()); |
| 69 | +- return Collections.singletonList(Proxy.NO_PROXY); |
| 70 | ++ |
| 71 | ++ int timeout = ProxySettings.getPacScriptTimeout(); |
| 72 | ++ |
| 73 | ++ if (timeout <= 0){ |
| 74 | ++ jsResultAnalyzed = executeProxyScript(uri); |
| 75 | ++ } else { |
| 76 | ++ Task task = getRequestProcessor().post(() -> { |
| 77 | ++ resultHolder.set(executeProxyScript(uri)); |
| 78 | ++ }); |
| 79 | ++ |
| 80 | ++ try{ |
| 81 | ++ if(!task.waitFinished(timeout)){ |
| 82 | ++ LOGGER.log(Level.WARNING, "Timeout when executing PAC script function: {0}", scriptEngine.getJsMainFunction().getJsFunctionName()); |
| 83 | ++ } |
| 84 | ++ } catch (InterruptedException ex) { |
| 85 | ++ LOGGER.log(Level.WARNING, "PAC script execution interrupted: {0}", ex); |
| 86 | ++ } finally { |
| 87 | ++ if (!task.isFinished()) { |
| 88 | ++ // interruptThread is set true for the RequestProcessor so cancel will interrupt without any setting |
| 89 | ++ task.cancel(); |
| 90 | + } |
| 91 | + } |
| 92 | +- // other unforseen errors |
| 93 | +- LOGGER.log(Level.WARNING, "Error when executing PAC script function " + scriptEngine.getJsMainFunction().getJsFunctionName() + " : ", ex); |
| 94 | +- return Collections.singletonList(Proxy.NO_PROXY); |
| 95 | ++ jsResultAnalyzed = resultHolder.get(); |
| 96 | ++ } |
| 97 | ++ if (canUseURLCaching && (resultCache != null) && (jsResultAnalyzed != null)) { |
| 98 | ++ resultCache.put(uri, jsResultAnalyzed); // save the result in the cache |
| 99 | + } |
| 100 | ++ return jsResultAnalyzed != null ? jsResultAnalyzed : Collections.singletonList(Proxy.NO_PROXY); |
| 101 | + } |
| 102 | +- |
| 103 | ++ |
| 104 | + @Override |
| 105 | + public boolean usesCaching() { |
| 106 | + return (canUseURLCaching && (resultCache != null)); |
| 107 | +@@ -275,6 +280,32 @@ public String getPacScriptSource() { |
| 108 | + return this.pacScriptSource; |
| 109 | + } |
| 110 | + |
| 111 | ++ private List<Proxy> executeProxyScript(URI uri) { |
| 112 | ++ try{ |
| 113 | ++ Object jsResult; |
| 114 | ++ synchronized (scriptEngine) { |
| 115 | ++ jsResult = scriptEngine.findProxyForURL(PacUtils.toStrippedURLStr(uri), uri.getHost()); |
| 116 | ++ } |
| 117 | ++ return analyzeResult(uri, jsResult); |
| 118 | ++ |
| 119 | ++ } catch (NoSuchMethodException ex) { |
| 120 | ++ // If this exception occur at this time it is really, really unexpected. |
| 121 | ++ // We already gave the function a test spin in the constructor. |
| 122 | ++ Exceptions.printStackTrace(ex); |
| 123 | ++ } catch (ScriptException ex) { |
| 124 | ++ LOGGER.log(Level.WARNING, "Error when executing PAC script function " + scriptEngine.getJsMainFunction().getJsFunctionName() + " : ", ex); |
| 125 | ++ } catch (Exception ex) { // for runtime exceptions |
| 126 | ++ if (ex.getCause() != null) { |
| 127 | ++ if (ex.getCause() instanceof ClassNotFoundException) { |
| 128 | ++ // Is someone trying to break out of the sandbox ? |
| 129 | ++ LOGGER.log(Level.WARNING, "The downloaded PAC script is attempting to access Java class ''{0}'' which may be a sign of maliciousness. You should investigate this with your network administrator.", ex.getCause().getMessage()); |
| 130 | ++ } |
| 131 | ++ } |
| 132 | ++ // other unforseen errors |
| 133 | ++ LOGGER.log(Level.WARNING, "Error when executing PAC script function " + scriptEngine.getJsMainFunction().getJsFunctionName() + " : ", ex); |
| 134 | ++ } |
| 135 | ++ return null; |
| 136 | ++ } |
| 137 | + |
| 138 | + |
| 139 | + private PacScriptEngine getScriptEngine(String pacSource) throws PacParsingException { |
| 140 | +diff --git a/platform/core.network/test/unit/data/pacFiles2/pac-test-timeout.js b/platform/core.network/test/unit/data/pacFiles2/pac-test-timeout.js |
| 141 | +new file mode 100644 |
| 142 | +index 000000000000..9e31d3cb76b1 |
| 143 | +--- /dev/null |
| 144 | ++++ b/platform/core.network/test/unit/data/pacFiles2/pac-test-timeout.js |
| 145 | +@@ -0,0 +1,30 @@ |
| 146 | ++/* |
| 147 | ++ * Licensed under the Apache License, Version 2.0 (the "License"); |
| 148 | ++ * you may not use this file except in compliance with the License. |
| 149 | ++ * You may obtain a copy of the License at |
| 150 | ++ * |
| 151 | ++ * http://www.apache.org/licenses/LICENSE-2.0 |
| 152 | ++ * |
| 153 | ++ * Unless required by applicable law or agreed to in writing, software |
| 154 | ++ * distributed under the License is distributed on an "AS IS" BASIS, |
| 155 | ++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 156 | ++ * See the License for the specific language governing permissions and |
| 157 | ++ * limitations under the License. |
| 158 | ++ */ |
| 159 | ++ |
| 160 | ++ |
| 161 | ++ |
| 162 | ++ |
| 163 | ++// |
| 164 | ++// A PAC script which takes long time to execute and wastes cpu resources |
| 165 | ++// |
| 166 | ++ |
| 167 | ++function FindProxyForURL(url, host) |
| 168 | ++{ |
| 169 | ++ alert("pac-test-timeout.js"); |
| 170 | ++ const repeatedA = "A".repeat(999); |
| 171 | ++ while(true){ |
| 172 | ++ console.log(repeatedA); |
| 173 | ++ } |
| 174 | ++ return "DIRECT"; |
| 175 | ++} |
| 176 | +\ No newline at end of file |
| 177 | +diff --git a/platform/core.network/test/unit/src/org/netbeans/core/network/proxy/pac/PacEngineTest.java b/platform/core.network/test/unit/src/org/netbeans/core/network/proxy/pac/PacEngineTest.java |
| 178 | +index 178c9b162feb..90812bfa4612 100644 |
| 179 | +--- a/platform/core.network/test/unit/src/org/netbeans/core/network/proxy/pac/PacEngineTest.java |
| 180 | ++++ b/platform/core.network/test/unit/src/org/netbeans/core/network/proxy/pac/PacEngineTest.java |
| 181 | +@@ -27,9 +27,12 @@ |
| 182 | + import org.junit.Before; |
| 183 | + import org.junit.BeforeClass; |
| 184 | + import org.junit.Test; |
| 185 | ++import org.netbeans.core.ProxySettings; |
| 186 | ++import static org.netbeans.core.ProxySettings.PAC_SCRIPT_TIMEOUT; |
| 187 | + import org.netbeans.core.network.proxy.pac.impl.NbPacScriptEvaluatorFactory; |
| 188 | + import org.netbeans.junit.NbModuleSuite; |
| 189 | + import org.netbeans.junit.NbTestCase; |
| 190 | ++import org.openide.util.NbPreferences; |
| 191 | + |
| 192 | + /** |
| 193 | + * |
| 194 | +@@ -73,6 +76,9 @@ public static final junit.framework.Test suite() { |
| 195 | + @Test |
| 196 | + public void testEngine() throws PacParsingException, IOException, URISyntaxException, PacValidationException { |
| 197 | + System.out.println("toSemiColonListStr"); |
| 198 | ++ |
| 199 | ++ NbPreferences.forModule(ProxySettings.class) |
| 200 | ++ .putInt(PAC_SCRIPT_TIMEOUT, 2000); |
| 201 | + |
| 202 | + PacScriptEvaluatorFactory factory = new NbPacScriptEvaluatorFactory(); |
| 203 | + |
| 204 | +@@ -81,6 +87,7 @@ public void testEngine() throws PacParsingException, IOException, URISyntaxExcep |
| 205 | + testPacFile("pac-test3.js", factory, 1, false); |
| 206 | + testPacFileMalicious("pac-test-sandbox-breakout.js", factory); |
| 207 | + testPacFileMalicious("pac-test-getclass.js", factory); |
| 208 | ++ testPacFileMalicious("pac-test-timeout.js", factory); |
| 209 | + |
| 210 | + testPacFile2("pac-test4.js", factory); |
| 211 | + } |
| 212 | +diff --git a/platform/o.n.core/src/org/netbeans/core/ProxySettings.java b/platform/o.n.core/src/org/netbeans/core/ProxySettings.java |
| 213 | +index 2d29427cd3c2..19e48d9bad82 100644 |
| 214 | +--- a/platform/o.n.core/src/org/netbeans/core/ProxySettings.java |
| 215 | ++++ b/platform/o.n.core/src/org/netbeans/core/ProxySettings.java |
| 216 | +@@ -49,6 +49,8 @@ public class ProxySettings { |
| 217 | + public static final String USE_PROXY_ALL_PROTOCOLS = "useProxyAllProtocols"; // NOI18N |
| 218 | + public static final String DIRECT = "DIRECT"; // NOI18N |
| 219 | + public static final String PAC = "PAC"; // NOI18N |
| 220 | ++ public static final String PAC_SCRIPT_TIMEOUT = "pacScriptTimeout"; // NOI18N |
| 221 | ++ public static final int DEFAULT_TIMEOUT = 60000; |
| 222 | + |
| 223 | + public static final String SYSTEM_PROXY_HTTP_HOST = "systemProxyHttpHost"; // NOI18N |
| 224 | + public static final String SYSTEM_PROXY_HTTP_PORT = "systemProxyHttpPort"; // NOI18N |
| 225 | +@@ -141,6 +143,10 @@ public static int getProxyType () { |
| 226 | + return type; |
| 227 | + } |
| 228 | + |
| 229 | ++ public static int getPacScriptTimeout() { |
| 230 | ++ return NbPreferences.forModule(ProxySettings.class) |
| 231 | ++ .getInt(PAC_SCRIPT_TIMEOUT, DEFAULT_TIMEOUT); |
| 232 | ++ } |
| 233 | + |
| 234 | + public static String getSystemHttpHost() { |
| 235 | + return getPreferences().get(SYSTEM_PROXY_HTTP_HOST, ""); |
0 commit comments