Skip to content

Commit

Permalink
Merge pull request #340 from PerimeterX/release/v6.9.4
Browse files Browse the repository at this point in the history
Release/v6.9.4
  • Loading branch information
etrpx authored Nov 21, 2023
2 parents 611dfa3 + 73a9e5d commit 0b088f8
Show file tree
Hide file tree
Showing 12 changed files with 132 additions and 61 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Change Log

## [v6.9.4](https://github.com/PerimeterX/perimeterx-java-sdk/compare/6.9.4...HEAD) (2023-11-21)
- Fixed first party connection timeout issue.
- Updated the captcha template with timeout mechanism addressing scenarios where delays occurred in retrieving the captcha.

## [v6.9.3](https://github.com/PerimeterX/perimeterx-java-sdk/compare/6.9.3...HEAD) (2023-11-16)
- Fixed risk request schema.
- Fixed cookie validation.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

# [PerimeterX](http://www.perimeterx.com) Java SDK

> Latest stable version: [v6.9.3](https://search.maven.org/#artifactdetails%7Ccom.perimeterx%7Cperimeterx-sdk%7C6.9.3%7Cjar)
> Latest stable version: [v6.9.4](https://search.maven.org/#artifactdetails%7Ccom.perimeterx%7Cperimeterx-sdk%7C6.9.4%7Cjar)
## Table of Contents

Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<name>PerimeterX JAVA SDK</name>
<groupId>com.perimeterx</groupId>
<artifactId>perimeterx-sdk</artifactId>
<version>6.9.3</version>
<version>6.9.4</version>

<packaging>jar</packaging>
<description>PerimeterX Java SDK</description>
Expand Down
8 changes: 5 additions & 3 deletions px_metadata.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "6.9.3",
"version": "6.9.4",
"supported_features": [
"advanced_blocking_response",
"bypass_monitor_header",
Expand Down Expand Up @@ -47,6 +47,8 @@
"test_risk_cookie_valid_cookie_with_user_agent_bigger_than_max_length",
"test_block_page_captcha_response_contains_http_reason_phrase",
"test_cookie_v3_cookie_decryption_failed_iterations_above_max_allowed_value",
"test_cookie_v3_cookie_validation_failed_big_cookie"
"test_cookie_v3_cookie_validation_failed_big_cookie",
"test_block_page_captcha_response_mobile",
"test_block_page_captcha_response_web"
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.perimeterx.models.configuration.PXConfiguration;
import com.perimeterx.models.exceptions.PXException;
import com.perimeterx.utils.Constants;
import com.perimeterx.utils.PXResourcesUtil;
import org.apache.commons.io.IOUtils;

import java.io.IOException;
Expand All @@ -16,15 +17,15 @@
import java.util.HashMap;
import java.util.Map;

import static com.perimeterx.utils.Constants.*;

/**
* This class is a helper class, in order to get a template the factory receives a name of a template and returns
* the template compiled
* <p>
* Created by nitzangoldfeder on 02/03/2017.
*/
public abstract class TemplateFactory {
private final static String URL_HTTPS_PREFIX = "https://";

public static String getTemplate(String template, Map<String, String> props) throws PXException {
try {
MustacheFactory mf = new DefaultMustacheFactory();
Expand All @@ -49,17 +50,17 @@ public static Map<String, String> getProps(PXContext pxContext, PXConfiguration
props.put("jsRef", pxConfig.getJsRef());

String captchaSrcParams = getCaptchaSrcParams(pxContext);
String blockScript = getCaptchaUrl(Constants.CAPTCHA_HOST, pxConfig.getAppId(), captchaSrcParams);
String altBlockScript = getCaptchaUrl(Constants.ALT_CAPTCHA_HOST, pxConfig.getAppId(), captchaSrcParams);
final String altBlockScript = PXResourcesUtil.getPxCaptchaURL(pxConfig, captchaSrcParams, true);
String blockScript = PXResourcesUtil.getPxCaptchaURL(pxConfig, captchaSrcParams, false);


String jsClientSrc = URL_HTTPS_PREFIX + Constants.CLIENT_HOST + "/" + pxConfig.getAppId() + "/main.min.js";
String jsClientSrc = URL_HTTPS_PREFIX + Constants.CLIENT_HOST + SLASH + pxConfig.getAppId() + SENSOR_FIRST_PARTY_PATH;
String hostUrl = pxContext.getCollectorURL();
if (pxConfig.isFirstPartyEnabled() && !pxContext.isMobileToken()) {
String prefix = pxConfig.getAppId().substring(2);
blockScript = "/" + prefix + Constants.FIRST_PARTY_CAPTCHA_PATH + "?" + captchaSrcParams;
jsClientSrc = "/" + prefix + Constants.FIRST_PARTY_VENDOR_PATH;
hostUrl = "/" + prefix + Constants.FIRST_PARTY_XHR_PATH;
blockScript = SLASH + prefix + Constants.FIRST_PARTY_CAPTCHA_PATH + QUESTION_MARK + captchaSrcParams;
jsClientSrc = SLASH + prefix + Constants.FIRST_PARTY_VENDOR_PATH;
hostUrl = SLASH + prefix + Constants.FIRST_PARTY_XHR_PATH;
}
props.put("hostUrl", hostUrl);
props.put("blockScript", blockScript);
Expand Down Expand Up @@ -87,8 +88,4 @@ private static String getCaptchaSrcParams(PXContext pxContext) {
String urlVid = pxContext.getVid() != null ? pxContext.getVid() : "";
return "a=" + pxContext.getBlockAction().getCode() + "&u=" + pxContext.getUuid() + "&v=" + urlVid + "&m=" + (pxContext.isMobileToken() ? "1" : "0");
}

private static String getCaptchaUrl(String host, String appId, String params) {
return URL_HTTPS_PREFIX + host + "/" + appId + "/captcha.js?" + params;
}
}
46 changes: 20 additions & 26 deletions src/main/java/com/perimeterx/api/proxy/DefaultReverseProxy.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import com.perimeterx.http.IPXOutgoingRequest;
import com.perimeterx.models.configuration.PXConfiguration;
import com.perimeterx.models.proxy.PredefinedResponse;
import com.perimeterx.utils.Constants;
import com.perimeterx.utils.PXLogger;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;

Expand All @@ -16,6 +15,10 @@
import java.io.IOException;
import java.net.URISyntaxException;

import static com.perimeterx.utils.Constants.CONTENT_TYPE_APPLICATION_JSON;
import static com.perimeterx.utils.Constants.XHR_PATH;
import static com.perimeterx.utils.PXResourcesUtil.*;

/**
* Created by nitzangoldfeder on 14/05/2018.
*/
Expand All @@ -28,24 +31,19 @@ public class DefaultReverseProxy implements ReverseProxy {
0x00, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, 0x21, (byte) 0xf9, 0x04,
0x01, 0x0a, 0x00, 0x01, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x4c, 0x01, 0x00, 0x3b};
private final String CONTENT_TYPE_JAVASCRIPT = "application/javascript";
private final String CONTENT_TYPE_APPLICATION_JSON = "application/json";
private final String CONTENT_TYPE_IMAGE_GIF = "image/gif";

private final String XHR_PATH = "xhr";
private final String CLIENT_FP_PATH = "init.js";
private final String CLIENT_TP_PATH = "main.min.js";
private final String CAPTACHA_PATH = "captcha";

private IPProvider ipProvider;
private String clientPath;
private String clientReversePrefix;
private String xhrReversePrefix;
private String captchaReversePrefix;
private String collectorUrl;
private final String clientReversePrefix;
private final String xhrReversePrefix;
private final String captchaReversePrefix;
private IPXHttpClient proxyClient;
private PredefinedResponseHelper predefinedResponseHelper;

private PXConfiguration pxConfiguration;
private final PXConfiguration pxConfiguration;

public DefaultReverseProxy(PXConfiguration pxConfiguration, IPProvider ipProvider) {
this.predefinedResponseHelper = new DefaultPredefinedResponseHandler();
Expand All @@ -54,8 +52,6 @@ public DefaultReverseProxy(PXConfiguration pxConfiguration, IPProvider ipProvide
this.clientReversePrefix = String.format("/%s/%s", reverseAppId, CLIENT_FP_PATH);
this.xhrReversePrefix = String.format("/%s/%s", reverseAppId, XHR_PATH);
this.captchaReversePrefix = "/" + reverseAppId + "/" + CAPTACHA_PATH;
this.clientPath = String.format("/%s/%s", pxConfiguration.getAppId(), CLIENT_TP_PATH);
this.collectorUrl = pxConfiguration.getCollectorUrl();
this.ipProvider = ipProvider;

PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
Expand All @@ -76,11 +72,9 @@ public boolean reversePxClient(HttpServletRequest req, HttpServletResponse res)
return true;
}

String url = "https://" + pxConfiguration.getClientHost();

RemoteServer remoteServer = new RemoteServer(url, clientPath, req, res, ipProvider, proxyClient, null, null, pxConfiguration);
final RemoteServer remoteServer = new RemoteServer(getPxSensorURL(pxConfiguration), req, res, ipProvider, proxyClient, null, null, pxConfiguration);
IPXOutgoingRequest proxyRequest = remoteServer.prepareProxyRequest();
remoteServer.handleResponse(proxyRequest, false);
remoteServer.handleResponse(proxyRequest);
return true;
}

Expand All @@ -106,13 +100,13 @@ public boolean reversePxXhr(HttpServletRequest req, HttpServletResponse res) thr
return true;
}

final String originalUrl = req.getRequestURI().substring(xhrReversePrefix.length());
final RemoteServer remoteServer = new RemoteServer(collectorUrl, originalUrl, req, res, ipProvider, proxyClient, predefinedResponse, predefinedResponseHelper, pxConfiguration);
final String url = getPxXhrUrl(pxConfiguration, req.getRequestURI());
final RemoteServer remoteServer = new RemoteServer(url, req, res, ipProvider, proxyClient, predefinedResponse, predefinedResponseHelper, pxConfiguration);
IPXOutgoingRequest proxyRequest = null;

try {
proxyRequest = remoteServer.prepareProxyRequest();
remoteServer.handleResponse(proxyRequest, true);
remoteServer.handleResponse(proxyRequest);
} catch (Exception e) {
logger.error("reversePxXhr - failed to handle xhr request, error :: ", e.getMessage());
safelyCloseInputStream(proxyRequest);
Expand All @@ -135,20 +129,20 @@ public boolean reverseCaptcha(HttpServletRequest req, HttpServletResponseWrapper
if (!req.getRequestURI().contains(captchaReversePrefix)) {
return false;
}
final PredefinedResponse predefinedResponse = new PredefinedResponse(CONTENT_TYPE_JAVASCRIPT, DEFAULT_JAVASCRIPT_VALUE);

if (!pxConfiguration.isFirstPartyEnabled()) {
logger.debug("First party is disabled, rendering default response");
PredefinedResponse predefinedResponse = new PredefinedResponse(CONTENT_TYPE_JAVASCRIPT, DEFAULT_JAVASCRIPT_VALUE);
predefinedResponseHelper.handlePredefinedResponse(res, predefinedResponse);
return false;
}
String query = req.getQueryString();
String originalRequest = pxConfiguration.getAppId() + "/captcha.js?" + query;
String url = "https://" + Constants.CAPTCHA_HOST + "/" + originalRequest;
logger.debug("Forwarding request from " + captchaReversePrefix + "/" + originalRequest + "to xhr at " + url);

RemoteServer remoteServer = new RemoteServer("", url, req, res, ipProvider, proxyClient, null, predefinedResponseHelper, pxConfiguration);
final String url = getPxCaptchaURL(pxConfiguration, req.getQueryString(), false);
logger.debug("Forwarding request from " + req.getRequestURL() + "to xhr at " + url);

final RemoteServer remoteServer = new RemoteServer(url, req, res, ipProvider, proxyClient, predefinedResponse, predefinedResponseHelper, pxConfiguration);
IPXOutgoingRequest proxyRequest = remoteServer.prepareProxyRequest();
remoteServer.handleResponse(proxyRequest, true);
remoteServer.handleResponse(proxyRequest);
return true;

}
Expand Down
18 changes: 12 additions & 6 deletions src/main/java/com/perimeterx/api/proxy/RemoteServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import java.util.Formatter;
import java.util.Objects;

import static com.perimeterx.utils.Constants.FIRST_PARTY_HEADER_NAME;
import static com.perimeterx.utils.Constants.FIRST_PARTY_HEADER_VALUE;
import static com.perimeterx.utils.PXIOUtils.copy;

/**
Expand Down Expand Up @@ -66,12 +68,12 @@ public class RemoteServer {
}
}

public RemoteServer(String serverUrl, String uri, HttpServletRequest req, HttpServletResponse res,
public RemoteServer(String serverUrl, HttpServletRequest req, HttpServletResponse res,
IPProvider ipProvider, IPXHttpClient httpClient, PredefinedResponse predefinedResponse,
PredefinedResponseHelper predefinedResponseHelper, PXConfiguration pxConfiguration) throws URISyntaxException {
this.req = req;
this.res = res;
this.targetUri = serverUrl.concat(uri);
this.targetUri = serverUrl;
this.proxyClient = httpClient;
this.targetUriObj = new URI(targetUri);
this.targetHost = URIUtils.extractHost(targetUriObj);
Expand Down Expand Up @@ -106,15 +108,15 @@ public IPXOutgoingRequest prepareProxyRequest() throws IOException {
return requestBuilder.build();
}

public IPXIncomingResponse handleResponse(IPXOutgoingRequest proxyRequest, boolean allowPredefinedHandler) {
public IPXIncomingResponse handleResponse(IPXOutgoingRequest proxyRequest) {
IPXIncomingResponse proxyResponse = null;
try {
// Execute the request
proxyResponse = doExecute(proxyRequest);
int statusCode = proxyResponse.status().getStatusCode();

// In failure we can check if we enable predefined request or proxy the original response
if (allowPredefinedHandler && statusCode >= HttpStatus.SC_BAD_REQUEST) {
if (this.isAllowedPredefinedResponse() && statusCode >= HttpStatus.SC_BAD_REQUEST) {
predefinedResponseHelper.handlePredefinedResponse(res, predefinedResponse);
return proxyResponse;
}
Expand All @@ -137,7 +139,7 @@ public IPXIncomingResponse handleResponse(IPXOutgoingRequest proxyRequest, boole
}

} catch (Exception e) {
if (allowPredefinedHandler) {
if (this.isAllowedPredefinedResponse()) {
predefinedResponseHelper.handlePredefinedResponse(res, predefinedResponse);
}
}
Expand Down Expand Up @@ -267,7 +269,7 @@ protected void copyRequestHeaders(HttpServletRequest servletRequest, PXOutgoingR
*/
protected void handlePXHeaders(PXOutgoingRequestImplBuilder proxyRequest) {
proxyRequest.header(new PXHttpHeader("X-PX-ENFORCER-TRUE-IP", this.ipProvider.getRequestIP(this.req)));
proxyRequest.header(new PXHttpHeader("X-PX-FIRST-PARTY", "1"));
proxyRequest.header(new PXHttpHeader(FIRST_PARTY_HEADER_NAME, FIRST_PARTY_HEADER_VALUE));
}

private void handleXForwardedForHeader(HttpServletRequest servletRequest, PXOutgoingRequestImplBuilder proxyRequest) {
Expand Down Expand Up @@ -417,4 +419,8 @@ private static CharSequence encodeUriQuery(CharSequence in, boolean encodePercen
protected IPXIncomingResponse doExecute(IPXOutgoingRequest proxyRequest) throws IOException {
return proxyClient.send(proxyRequest);
}

private boolean isAllowedPredefinedResponse() {
return this.predefinedResponse != null && this.predefinedResponseHelper != null;
}
}
14 changes: 11 additions & 3 deletions src/main/java/com/perimeterx/http/PXApacheHttpClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
import com.perimeterx.models.configuration.PXConfiguration;
import com.perimeterx.utils.PXCommonUtils;
import com.perimeterx.utils.PXLogger;
import org.apache.http.client.methods.*;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
Expand All @@ -24,6 +27,8 @@
import java.net.URISyntaxException;
import java.util.concurrent.TimeUnit;

import static com.perimeterx.utils.PXResourcesUtil.isValidPxThirdPartyRequest;


public class PXApacheHttpClient implements IPXHttpClient {
private static final PXLogger logger = PXLogger.getLogger(PXApacheHttpClient.class);
Expand Down Expand Up @@ -155,7 +160,11 @@ private HttpRequestBase createRequest(IPXOutgoingRequest request) {
for (PXHttpHeader header : request.getHeaders()) {
req.addHeader(header.getName(), header.getValue());
}
req.setConfig(PXCommonUtils.getRequestConfig(pxConfiguration));

if (!isValidPxThirdPartyRequest(req)) {
req.setConfig(PXCommonUtils.getRequestConfig(pxConfiguration));
}

return req;
}

Expand All @@ -179,5 +188,4 @@ public String getMethod() {
};
}
}

}
9 changes: 9 additions & 0 deletions src/main/java/com/perimeterx/utils/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,13 @@ public final class Constants {
public static final String DEFAULT_COMPROMISED_CREDENTIALS_HEADER_NAME = "px-compromised-credentials";

public static final String DEFAULT_TELEMETRY_REQUEST_HEADER_NAME = "x-px-enforcer-telemetry";

public final static String URL_HTTPS_PREFIX = "https://";
public final static String CAPTCHA_FIRST_PARTY_FILE_PATH = "/captcha.js";
public final static String SENSOR_FIRST_PARTY_PATH = "/main.min.js";
public final static char SLASH = '/';
public final static char QUESTION_MARK = '?';
public final static String XHR_PATH = "xhr";
public final static String FIRST_PARTY_HEADER_NAME = "X-PX-FIRST-PARTY";
public final static String FIRST_PARTY_HEADER_VALUE = "1";
}
43 changes: 43 additions & 0 deletions src/main/java/com/perimeterx/utils/PXResourcesUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.perimeterx.utils;

import com.perimeterx.models.configuration.PXConfiguration;
import org.apache.http.client.methods.HttpRequestBase;

import static com.perimeterx.utils.Constants.*;
import static org.apache.commons.lang3.StringUtils.isBlank;

public class PXResourcesUtil {
public static boolean isValidPxThirdPartyRequest(HttpRequestBase req) {
if (req != null && req.getHeaders(FIRST_PARTY_HEADER_NAME).length > 0) {
return FIRST_PARTY_HEADER_VALUE
.equals(req.getFirstHeader(FIRST_PARTY_HEADER_NAME).getValue());
}
return false;
}

public static String getPxCaptchaURL(PXConfiguration config, String params, boolean alternativeCaptcha) {
final String host = alternativeCaptcha ? ALT_CAPTCHA_HOST : CAPTCHA_HOST;
String url = URL_HTTPS_PREFIX + host + SLASH + config.getAppId() + CAPTCHA_FIRST_PARTY_FILE_PATH;

if (!isBlank(params)) {
url += QUESTION_MARK + params;
}
return url;
}

public static String getPxSensorURL(PXConfiguration config) {
final String path = String.format("/%s%s", config.getAppId(), SENSOR_FIRST_PARTY_PATH);
return URL_HTTPS_PREFIX + config.getClientHost() + path;
}

public static String getPxXhrUrl(PXConfiguration config, String requestURI) {
if (isBlank(requestURI)) {
return null;
}
final String firstPartyAppIdForm = config.getAppId().substring(2);
final String xhrPrefix = String.format("/%s/%s", firstPartyAppIdForm, XHR_PATH);
final String path = requestURI.substring(xhrPrefix.length());

return config.getCollectorUrl() + path;
}
}
Loading

0 comments on commit 0b088f8

Please sign in to comment.