Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Timecap on execution of PAC script #8036

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import java.util.LinkedList;
import java.util.List;
import java.util.StringTokenizer;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
Expand All @@ -46,6 +47,9 @@
import org.netbeans.core.network.proxy.pac.PacUtils;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.util.RequestProcessor;
import org.openide.util.RequestProcessor.Task;
import org.netbeans.core.ProxySettings;

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


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

List<Proxy> jsResultAnalyzed;
List<Proxy> jsResultAnalyzed = null;

// First try the cache
if (resultCache != null) {
Expand All @@ -222,38 +227,37 @@ public List<Proxy> findProxyForURL(URI uri) throws PacValidationException {
return jsResultAnalyzed;
}
}
try {
Object jsResult;
synchronized (scriptEngine) {
jsResult = scriptEngine.findProxyForURL(PacUtils.toStrippedURLStr(uri), uri.getHost());
}
jsResultAnalyzed = analyzeResult(uri, jsResult);
if (canUseURLCaching && (resultCache != null)) {
resultCache.put(uri, jsResultAnalyzed); // save the result in the cache
}
return jsResultAnalyzed;
} catch (NoSuchMethodException ex) {
// If this exception occur at this time it is really, really unexpected.
// We already gave the function a test spin in the constructor.
Exceptions.printStackTrace(ex);
return Collections.singletonList(Proxy.NO_PROXY);
} catch (ScriptException ex) {
LOGGER.log(Level.WARNING, "Error when executing PAC script function " + scriptEngine.getJsMainFunction().getJsFunctionName() + " : ", ex);
return Collections.singletonList(Proxy.NO_PROXY);
} catch (Exception ex) { // for runtime exceptions
if (ex.getCause() != null) {
if (ex.getCause() instanceof ClassNotFoundException) {
// Is someone trying to break out of the sandbox ?
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());
return Collections.singletonList(Proxy.NO_PROXY);

int timeout = ProxySettings.getPacScriptTimeout();

if (timeout <= 0){
jsResultAnalyzed = executeProxyScript(uri);
} else {
AtomicReference<List<Proxy>> resultHolder = new AtomicReference<>(null);
Task task = RP.post(() -> {
resultHolder.set(executeProxyScript(uri));
});

try{
if(!task.waitFinished(timeout)){
LOGGER.log(Level.WARNING, "Timeout when executing PAC script function: {0}", scriptEngine.getJsMainFunction().getJsFunctionName());
}
} catch (InterruptedException ex) {
LOGGER.log(Level.WARNING, "PAC script execution interrupted: {0}", ex);
} finally {
if (!task.isFinished()) {
// interruptThread is set true for the RequestProcessor so cancel will interrupt without any setting
task.cancel();
}
}
// other unforseen errors
LOGGER.log(Level.WARNING, "Error when executing PAC script function " + scriptEngine.getJsMainFunction().getJsFunctionName() + " : ", ex);
return Collections.singletonList(Proxy.NO_PROXY);
jsResultAnalyzed = resultHolder.get();
}
if (canUseURLCaching && (resultCache != null) && (jsResultAnalyzed != null)) {
resultCache.put(uri, jsResultAnalyzed); // save the result in the cache
}
return jsResultAnalyzed != null ? jsResultAnalyzed : Collections.singletonList(Proxy.NO_PROXY);
}

@Override
public boolean usesCaching() {
return (canUseURLCaching && (resultCache != null));
Expand All @@ -275,6 +279,32 @@ public String getPacScriptSource() {
return this.pacScriptSource;
}

private List<Proxy> executeProxyScript(URI uri) {
try{
Object jsResult;
synchronized (scriptEngine) {
jsResult = scriptEngine.findProxyForURL(PacUtils.toStrippedURLStr(uri), uri.getHost());
}
return analyzeResult(uri, jsResult);

} catch (NoSuchMethodException ex) {
// If this exception occur at this time it is really, really unexpected.
// We already gave the function a test spin in the constructor.
Exceptions.printStackTrace(ex);
} catch (ScriptException ex) {
LOGGER.log(Level.WARNING, "Error when executing PAC script function " + scriptEngine.getJsMainFunction().getJsFunctionName() + " : ", ex);
} catch (Exception ex) { // for runtime exceptions
if (ex.getCause() != null) {
if (ex.getCause() instanceof ClassNotFoundException) {
// Is someone trying to break out of the sandbox ?
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());
}
}
// other unforseen errors
LOGGER.log(Level.WARNING, "Error when executing PAC script function " + scriptEngine.getJsMainFunction().getJsFunctionName() + " : ", ex);
}
return null;
}


private PacScriptEngine getScriptEngine(String pacSource) throws PacParsingException {
Expand Down
30 changes: 30 additions & 0 deletions platform/core.network/test/unit/data/pacFiles2/pac-test-timeout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/




//
// A PAC script which takes long time to execute and wastes cpu resources
//

function FindProxyForURL(url, host)
{
alert("pac-test-timeout.js");
const repeatedA = "A".repeat(999);
while(true){
console.log(repeatedA);
}
return "DIRECT";
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,12 @@
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.netbeans.core.ProxySettings;
import static org.netbeans.core.ProxySettings.PAC_SCRIPT_TIMEOUT;
import org.netbeans.core.network.proxy.pac.impl.NbPacScriptEvaluatorFactory;
import org.netbeans.junit.NbModuleSuite;
import org.netbeans.junit.NbTestCase;
import org.openide.util.NbPreferences;

/**
*
Expand Down Expand Up @@ -73,6 +76,9 @@ public static final junit.framework.Test suite() {
@Test
public void testEngine() throws PacParsingException, IOException, URISyntaxException, PacValidationException {
System.out.println("toSemiColonListStr");

NbPreferences.forModule(ProxySettings.class)
.putInt(PAC_SCRIPT_TIMEOUT, 2000);

PacScriptEvaluatorFactory factory = new NbPacScriptEvaluatorFactory();

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

testPacFile2("pac-test4.js", factory);
}
Expand Down
6 changes: 6 additions & 0 deletions platform/o.n.core/src/org/netbeans/core/ProxySettings.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ public class ProxySettings {
public static final String USE_PROXY_ALL_PROTOCOLS = "useProxyAllProtocols"; // NOI18N
public static final String DIRECT = "DIRECT"; // NOI18N
public static final String PAC = "PAC"; // NOI18N
public static final String PAC_SCRIPT_TIMEOUT = "pacScriptTimeout"; // NOI18N
public static final int DEFAULT_TIMEOUT = 10000;

public static final String SYSTEM_PROXY_HTTP_HOST = "systemProxyHttpHost"; // NOI18N
public static final String SYSTEM_PROXY_HTTP_PORT = "systemProxyHttpPort"; // NOI18N
Expand Down Expand Up @@ -141,6 +143,10 @@ public static int getProxyType () {
return type;
}

public static int getPacScriptTimeout() {
return getPreferences ()
.getInt(PAC_SCRIPT_TIMEOUT, DEFAULT_TIMEOUT);
}

public static String getSystemHttpHost() {
return getPreferences().get(SYSTEM_PROXY_HTTP_HOST, "");
Expand Down