Skip to content

Commit 4de8ce2

Browse files
authored
Merge pull request #8036 from subhash-arabhi/timecap-on-pac-script
Timecap on execution of PAC script
2 parents b2bdb81 + 35f11fb commit 4de8ce2

File tree

4 files changed

+102
-29
lines changed

4 files changed

+102
-29
lines changed

platform/core.network/src/org/netbeans/core/network/proxy/pac/impl/NbPacScriptEvaluator.java

Lines changed: 59 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.util.LinkedList;
2727
import java.util.List;
2828
import java.util.StringTokenizer;
29+
import java.util.concurrent.atomic.AtomicReference;
2930
import java.util.logging.Level;
3031
import java.util.logging.Logger;
3132
import java.util.regex.Matcher;
@@ -46,6 +47,9 @@
4647
import org.netbeans.core.network.proxy.pac.PacUtils;
4748
import org.openide.util.Lookup;
4849
import org.openide.util.NbBundle;
50+
import org.openide.util.RequestProcessor;
51+
import org.openide.util.RequestProcessor.Task;
52+
import org.netbeans.core.ProxySettings;
4953

5054
/**
5155
* NetBeans implementation of a PAC script evaluator. This implementation
@@ -196,6 +200,7 @@ public class NbPacScriptEvaluator implements PacScriptEvaluator {
196200
private static final String PAC_SOCKS5_FFEXT = "SOCKS5"; // Mozilla Firefox extension. Not part of original Netscape spec.
197201
private static final String PAC_HTTP_FFEXT = "HTTP"; // Mozilla Firefox extension. Not part of original Netscape spec.
198202
private static final String PAC_HTTPS_FFEXT = "HTTPS"; // Mozilla Firefox extension. Not part of original Netscape spec.
203+
private static final RequestProcessor RP = new RequestProcessor(NbPacScriptEvaluator.class.getName(), Runtime.getRuntime().availableProcessors(), true, false);
199204
private final String pacScriptSource;
200205

201206

@@ -213,7 +218,7 @@ public NbPacScriptEvaluator(String pacSourceCocde) throws PacParsingException {
213218
@Override
214219
public List<Proxy> findProxyForURL(URI uri) throws PacValidationException {
215220

216-
List<Proxy> jsResultAnalyzed;
221+
List<Proxy> jsResultAnalyzed = null;
217222

218223
// First try the cache
219224
if (resultCache != null) {
@@ -222,38 +227,37 @@ public List<Proxy> findProxyForURL(URI uri) throws PacValidationException {
222227
return jsResultAnalyzed;
223228
}
224229
}
225-
try {
226-
Object jsResult;
227-
synchronized (scriptEngine) {
228-
jsResult = scriptEngine.findProxyForURL(PacUtils.toStrippedURLStr(uri), uri.getHost());
229-
}
230-
jsResultAnalyzed = analyzeResult(uri, jsResult);
231-
if (canUseURLCaching && (resultCache != null)) {
232-
resultCache.put(uri, jsResultAnalyzed); // save the result in the cache
233-
}
234-
return jsResultAnalyzed;
235-
} catch (NoSuchMethodException ex) {
236-
// If this exception occur at this time it is really, really unexpected.
237-
// We already gave the function a test spin in the constructor.
238-
Exceptions.printStackTrace(ex);
239-
return Collections.singletonList(Proxy.NO_PROXY);
240-
} catch (ScriptException ex) {
241-
LOGGER.log(Level.WARNING, "Error when executing PAC script function " + scriptEngine.getJsMainFunction().getJsFunctionName() + " : ", ex);
242-
return Collections.singletonList(Proxy.NO_PROXY);
243-
} catch (Exception ex) { // for runtime exceptions
244-
if (ex.getCause() != null) {
245-
if (ex.getCause() instanceof ClassNotFoundException) {
246-
// Is someone trying to break out of the sandbox ?
247-
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());
248-
return Collections.singletonList(Proxy.NO_PROXY);
230+
231+
int timeout = ProxySettings.getPacScriptTimeout();
232+
233+
if (timeout <= 0){
234+
jsResultAnalyzed = executeProxyScript(uri);
235+
} else {
236+
AtomicReference<List<Proxy>> resultHolder = new AtomicReference<>(null);
237+
Task task = RP.post(() -> {
238+
resultHolder.set(executeProxyScript(uri));
239+
});
240+
241+
try{
242+
if(!task.waitFinished(timeout)){
243+
LOGGER.log(Level.WARNING, "Timeout when executing PAC script function: {0}", scriptEngine.getJsMainFunction().getJsFunctionName());
244+
}
245+
} catch (InterruptedException ex) {
246+
LOGGER.log(Level.WARNING, "PAC script execution interrupted: {0}", ex);
247+
} finally {
248+
if (!task.isFinished()) {
249+
// interruptThread is set true for the RequestProcessor so cancel will interrupt without any setting
250+
task.cancel();
249251
}
250252
}
251-
// other unforseen errors
252-
LOGGER.log(Level.WARNING, "Error when executing PAC script function " + scriptEngine.getJsMainFunction().getJsFunctionName() + " : ", ex);
253-
return Collections.singletonList(Proxy.NO_PROXY);
253+
jsResultAnalyzed = resultHolder.get();
254254
}
255+
if (canUseURLCaching && (resultCache != null) && (jsResultAnalyzed != null)) {
256+
resultCache.put(uri, jsResultAnalyzed); // save the result in the cache
257+
}
258+
return jsResultAnalyzed != null ? jsResultAnalyzed : Collections.singletonList(Proxy.NO_PROXY);
255259
}
256-
260+
257261
@Override
258262
public boolean usesCaching() {
259263
return (canUseURLCaching && (resultCache != null));
@@ -275,6 +279,32 @@ public String getPacScriptSource() {
275279
return this.pacScriptSource;
276280
}
277281

282+
private List<Proxy> executeProxyScript(URI uri) {
283+
try{
284+
Object jsResult;
285+
synchronized (scriptEngine) {
286+
jsResult = scriptEngine.findProxyForURL(PacUtils.toStrippedURLStr(uri), uri.getHost());
287+
}
288+
return analyzeResult(uri, jsResult);
289+
290+
} catch (NoSuchMethodException ex) {
291+
// If this exception occur at this time it is really, really unexpected.
292+
// We already gave the function a test spin in the constructor.
293+
Exceptions.printStackTrace(ex);
294+
} catch (ScriptException ex) {
295+
LOGGER.log(Level.WARNING, "Error when executing PAC script function " + scriptEngine.getJsMainFunction().getJsFunctionName() + " : ", ex);
296+
} catch (Exception ex) { // for runtime exceptions
297+
if (ex.getCause() != null) {
298+
if (ex.getCause() instanceof ClassNotFoundException) {
299+
// Is someone trying to break out of the sandbox ?
300+
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());
301+
}
302+
}
303+
// other unforseen errors
304+
LOGGER.log(Level.WARNING, "Error when executing PAC script function " + scriptEngine.getJsMainFunction().getJsFunctionName() + " : ", ex);
305+
}
306+
return null;
307+
}
278308

279309

280310
private PacScriptEngine getScriptEngine(String pacSource) throws PacParsingException {
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* You may obtain a copy of the License at
5+
*
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*/
14+
15+
16+
17+
18+
//
19+
// A PAC script which takes long time to execute and wastes cpu resources
20+
//
21+
22+
function FindProxyForURL(url, host)
23+
{
24+
alert("pac-test-timeout.js");
25+
const repeatedA = "A".repeat(999);
26+
while(true){
27+
console.log(repeatedA);
28+
}
29+
return "DIRECT";
30+
}

platform/core.network/test/unit/src/org/netbeans/core/network/proxy/pac/PacEngineTest.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,12 @@
2727
import org.junit.Before;
2828
import org.junit.BeforeClass;
2929
import org.junit.Test;
30+
import org.netbeans.core.ProxySettings;
31+
import static org.netbeans.core.ProxySettings.PAC_SCRIPT_TIMEOUT;
3032
import org.netbeans.core.network.proxy.pac.impl.NbPacScriptEvaluatorFactory;
3133
import org.netbeans.junit.NbModuleSuite;
3234
import org.netbeans.junit.NbTestCase;
35+
import org.openide.util.NbPreferences;
3336

3437
/**
3538
*
@@ -73,6 +76,9 @@ public static final junit.framework.Test suite() {
7376
@Test
7477
public void testEngine() throws PacParsingException, IOException, URISyntaxException, PacValidationException {
7578
System.out.println("toSemiColonListStr");
79+
80+
NbPreferences.forModule(ProxySettings.class)
81+
.putInt(PAC_SCRIPT_TIMEOUT, 2000);
7682

7783
PacScriptEvaluatorFactory factory = new NbPacScriptEvaluatorFactory();
7884

@@ -81,6 +87,7 @@ public void testEngine() throws PacParsingException, IOException, URISyntaxExcep
8187
testPacFile("pac-test3.js", factory, 1, false);
8288
testPacFileMalicious("pac-test-sandbox-breakout.js", factory);
8389
testPacFileMalicious("pac-test-getclass.js", factory);
90+
testPacFileMalicious("pac-test-timeout.js", factory);
8491

8592
testPacFile2("pac-test4.js", factory);
8693
}

platform/o.n.core/src/org/netbeans/core/ProxySettings.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ public class ProxySettings {
4949
public static final String USE_PROXY_ALL_PROTOCOLS = "useProxyAllProtocols"; // NOI18N
5050
public static final String DIRECT = "DIRECT"; // NOI18N
5151
public static final String PAC = "PAC"; // NOI18N
52+
public static final String PAC_SCRIPT_TIMEOUT = "pacScriptTimeout"; // NOI18N
53+
public static final int DEFAULT_TIMEOUT = 10000;
5254

5355
public static final String SYSTEM_PROXY_HTTP_HOST = "systemProxyHttpHost"; // NOI18N
5456
public static final String SYSTEM_PROXY_HTTP_PORT = "systemProxyHttpPort"; // NOI18N
@@ -141,6 +143,10 @@ public static int getProxyType () {
141143
return type;
142144
}
143145

146+
public static int getPacScriptTimeout() {
147+
return getPreferences ()
148+
.getInt(PAC_SCRIPT_TIMEOUT, DEFAULT_TIMEOUT);
149+
}
144150

145151
public static String getSystemHttpHost() {
146152
return getPreferences().get(SYSTEM_PROXY_HTTP_HOST, "");

0 commit comments

Comments
 (0)