diff --git a/.github/workflows/fuzzer.yaml b/.github/workflows/fuzzer.yaml new file mode 100644 index 00000000..1a074f50 --- /dev/null +++ b/.github/workflows/fuzzer.yaml @@ -0,0 +1,152 @@ +name: Fuzzing Test + +on: + pull_request + +jobs: + + extract_version: + runs-on: ubuntu-latest + name: Extract release version + outputs: + supported-features: ${{ steps.version.outputs.value }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 'latest' + - name: Get package version + id: version + run: echo "value=$(node -p -e "require('./px_metadata.json').version")" >> "$GITHUB_OUTPUT" + + + Fuzzing: + name: "Fuzzing Test" + env: + MOCK_COLLECTOR_IMAGE_TAG: 1.3.6 + FUZZER_TAG: 1.0.3 + SAMPLE_SITE_IMAGE_TAG: 1.0.0 + ENFORCER_TAG: ${{ needs.extract_version.outputs.version }} + + strategy: + matrix: + mode: [ "url", "first_party", "headers", "cookies", "user_agent" ] + + runs-on: ubuntu-latest + timeout-minutes: 60 + needs: + - extract_version + + steps: + + - name: Checkout Repo + uses: actions/checkout@v4 + + - name: Set up Docker + uses: docker/setup-buildx-action@v3 + + - name: Build local cluster + run: ./ci_files/build_cluster.sh + + - name: Build Enforcer Docker image + run: | + docker build . -t localhost:5001/java-enforcer-sample-site:$SAMPLE_SITE_IMAGE_TAG && \ + docker push localhost:5001/java-enforcer-sample-site:$SAMPLE_SITE_IMAGE_TAG + + - uses: azure/setup-helm@v3 + with: + version: '3.14.2' + + - name: Clone helm charts repo - mock-collector + uses: actions/checkout@v4 + with: + repository: PerimeterX/connect-helm-charts + token: ${{ secrets.CONNECT_PULL_TOKEN }} + ref: mock-collector-0.1.1 + path: ./deploy_charts/mock-collector + + - name: Clone helm charts repo - fuzzer + uses: actions/checkout@v4 + with: + repository: PerimeterX/connect-helm-charts + token: ${{ secrets.CONNECT_PULL_TOKEN }} + ref: fuzzer-0.2.0 + path: ./deploy_charts/fuzzer + + - name: Clone helm charts repo - sample-site + uses: actions/checkout@v4 + with: + repository: PerimeterX/connect-helm-charts + token: ${{ secrets.CONNECT_PULL_TOKEN }} + ref: sample-site-0.5.0 + path: ./deploy_charts/sample-site + + - name: Set up Google Cloud SDK + id: 'auth' + uses: 'google-github-actions/auth@v2' + with: + credentials_json: '${{ secrets.GCR_SA_KEY }}' + + - name: Configure Docker credentials + run: | + gcloud auth configure-docker gcr.io + + - name: pull mock collector image + run: | + docker pull gcr.io/px-docker-repo/connecteam/mock-collector:$MOCK_COLLECTOR_IMAGE_TAG && \ + docker tag gcr.io/px-docker-repo/connecteam/mock-collector:$MOCK_COLLECTOR_IMAGE_TAG localhost:5001/mock-collector:$MOCK_COLLECTOR_IMAGE_TAG && \ + docker push localhost:5001/mock-collector:$MOCK_COLLECTOR_IMAGE_TAG + + - name: deploy mock collector + run: | + helm install mock-collector ./deploy_charts/mock-collector/charts/mock-collector \ + --set image.repository=localhost:5001/mock-collector \ + --set image.tag=$MOCK_COLLECTOR_IMAGE_TAG \ + --set imagePullPolicy=Always --wait + + - name: set secrets in enforcer config + run: | + cat ./ci_files/enforcer-config.json |\ + jq '.px_app_id="${{ secrets.PX_APP_ID }}"' |\ + jq '.px_cookie_secret="${{ secrets.TEST_COOKIE_SECRET }}"' |\ + jq '.px_auth_token="${{ secrets.PX_AUTH_TOKEN }}"' > /tmp/enforcer-config.json + + - name: log enforcer config + run: cat /tmp/enforcer-config.json + + - name: deploy java enforcer + run: | + helm install java-enforcer ./deploy_charts/sample-site/charts/sample-site \ + -f ./ci_files/enforcer-values.yaml \ + --set image.name=localhost:5001/java-enforcer-sample-site \ + --set image.tag=$SAMPLE_SITE_IMAGE_TAG \ + --set-file enforcerConfig.content=/tmp/enforcer-config.json \ + --wait + + - name: pull fuzzer image + run: | + docker pull gcr.io/px-docker-repo/connecteam/connect-enforcer-fuzzer:$FUZZER_TAG && \ + docker tag gcr.io/px-docker-repo/connecteam/connect-enforcer-fuzzer:$FUZZER_TAG localhost:5001/connect-enforcer-fuzzer:$FUZZER_TAG && \ + docker push localhost:5001/connect-enforcer-fuzzer:$FUZZER_TAG + + - name: run fuzzer + run: | + helm install fuzzer ./deploy_charts/fuzzer/charts/fuzzer \ + --set image.repository=localhost:5001/connect-enforcer-fuzzer \ + --set image.tag=$FUZZER_TAG \ + --set appId=$PX_APP_ID \ + --set mode=$FUZZ_MODE \ + --set siteURL=$SITE_URL \ + --wait \ + --timeout 60m0s \ + --wait-for-jobs + env: + FUZZ_MODE: ${{ matrix.mode }} + PX_APP_ID: ${{ secrets.PX_APP_ID }} + SITE_URL: "http://java-enforcer-sample-site:3000" + + - name: get tests results + if: ${{ always() }} + run: kubectl logs job/fuzzer-enforcer-fuzzer diff --git a/CHANGELOG.md b/CHANGELOG.md index c5ace89a..8c719b8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +## [v6.13.0](https://github.com/PerimeterX/perimeterx-java-sdk/compare/6.13.0...HEAD) (2024-04-27) +- Added vid Validation for _pxvid extraction +- Added Enforcer Fuzzer as part of the CI process +- Updated log4j artifact version +- Removed `/examples` directory + ## [v6.12.0](https://github.com/PerimeterX/perimeterx-java-sdk/compare/6.12.0...HEAD) (2024-02-25) - Added Dockerfile for web application example. - Bugfix - Sensitive headers are now case-insensitive. diff --git a/README.md b/README.md index 6e65477a..b71c5e81 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ # [PerimeterX](http://www.perimeterx.com) Java SDK -> Latest stable version: [v6.12.0](https://search.maven.org/#artifactdetails%7Ccom.perimeterx%7Cperimeterx-sdk%7C6.12.0%7Cjar) +> Latest stable version: [v6.13.0](https://search.maven.org/#artifactdetails%7Ccom.perimeterx%7Cperimeterx-sdk%7C6.13.0%7Cjar) ## Table of Contents diff --git a/examples/pom.xml b/examples/pom.xml deleted file mode 100644 index 0ba42d4e..00000000 --- a/examples/pom.xml +++ /dev/null @@ -1,87 +0,0 @@ - - - - org.springframework.boot - spring-boot-starter-parent - 2.2.7.RELEASE - - 4.0.0 - jar - examples - - - - - org.springframework.boot - spring-boot-starter-web - 2.2.7.RELEASE - - - - com.perimeterx - perimeterx-sdk - 6.9.5 - - - - commons-io - commons-io - 2.8.0 - - - - com.fasterxml.jackson.core - jackson-databind - 2.11.4 - - - - org.apache.tomcat.embed - tomcat-embed-core - 10.0.5 - - - - junit - junit - 4.13.2 - - - - org.codehaus.plexus - plexus-utils - 3.3.0 - - - - org.springframework - spring-web - 5.3.6 - - - - org.apache.httpcomponents - httpclient - 4.5.13 - - - - org.yaml - snakeyaml - 1.28 - - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - - diff --git a/examples/src/main/java/Application.java b/examples/src/main/java/Application.java deleted file mode 100644 index 070cd160..00000000 --- a/examples/src/main/java/Application.java +++ /dev/null @@ -1,13 +0,0 @@ -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.ComponentScan; - -@SpringBootApplication -@ComponentScan(basePackages = {"controllers", "filters"}) -public class Application { - - public static void main(String[] args) { - SpringApplication.run(Application.class, args); - } - -} diff --git a/examples/src/main/java/controllers/ExampleController.java b/examples/src/main/java/controllers/ExampleController.java deleted file mode 100644 index 8fd2a4ea..00000000 --- a/examples/src/main/java/controllers/ExampleController.java +++ /dev/null @@ -1,14 +0,0 @@ -package controllers; - -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -public class ExampleController { - - @RequestMapping("/") - public String example() { - return "Hello world"; - } - -} diff --git a/examples/src/main/java/filters/PXFilter.java b/examples/src/main/java/filters/PXFilter.java deleted file mode 100644 index 205d4979..00000000 --- a/examples/src/main/java/filters/PXFilter.java +++ /dev/null @@ -1,69 +0,0 @@ -package filters; - -import com.perimeterx.api.PXConfiguration; -import com.perimeterx.api.PerimeterX; -import com.perimeterx.models.configuration.ModuleMode; -import com.perimeterx.models.exceptions.PXException; -import px.CustomBlockHandler; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpServletResponseWrapper; -import java.io.IOException; - -/** - * Filter implementation based on PerimeterX SDK - */ -public class PXFilter implements Filter { - - private PerimeterX enforcer; - - @Override - public void init(FilterConfig filterConfig) throws ServletException { - // Creating filter configuration - PXConfiguration pxConf = new PXConfiguration.Builder() - .appId("") // Your PerimeterX application ID - .cookieKey("") // Should copy from RiskCookie section in https://console.perimeterx.com/botDefender/admin?page=policiesmgmt - .captchaEnabled(false) // This will trigger captcha validation flow when blocking - .moduleMode(ModuleMode.BLOCKING) - .authToken("") // PX Server request auth token to be copied from Token section in https://console.perimeterx.com/botDefender/admin?page=applicationsmgmt - .build(); - try { - this.enforcer = new PerimeterX(pxConf); - // This will set the blocking handler from the default one to the our custom block handler - // note that when we enable captcha logic we must use a blocking handler that display the appropriate html page with captcha - // for instance CaptchaBlockHandler that is included in the SDK - this.enforcer.setBlockHandler(new CustomBlockHandler()); - } catch (Exception e) { - // Could not init enforcer - } - } - - @Override - public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { - - // The request wrapper enables to read the request multiple times. - request = new RequestWrapper((HttpServletRequest) request); - - // This will apply PerimeterX SDK logic on each request going through this filter - HttpServletResponse httpRes = (HttpServletResponse) servletResponse; - - try { - PXContext ctx = enforcer.pxVerify(httpReq, new HttpServletResponseWrapper(httpRes)); - if (ctx.isVerified()) { - filterChain.doFilter(servletRequest, servletResponse); - } - - // This enables to read the response, it should happen after the response was already sent to the client - response = new ResponseWrapper((HttpServletResponse) response); - pxFilter.pxPostVerify((ResponseWrapper) response, context); - } catch (PXException e) { - // ignoring error for now and passing the request - filterChain.doFilter(servletRequest, servletResponse); - } - } - - @Override - public void destroy() { - } -} diff --git a/examples/src/main/java/px/CustomBlockHandler.java b/examples/src/main/java/px/CustomBlockHandler.java deleted file mode 100644 index 687603af..00000000 --- a/examples/src/main/java/px/CustomBlockHandler.java +++ /dev/null @@ -1,28 +0,0 @@ -package px; - -import com.perimeterx.api.blockhandler.BlockHandler; -import com.perimeterx.models.PXContext; -import com.perimeterx.models.exceptions.PXException; - -import javax.servlet.http.HttpServletResponseWrapper; -import java.io.IOException; - -/** - * This will implement the BlockHandler interface which can later be set in PerimeterX object initializtion - */ -public class CustomBlockHandler implements BlockHandler { - - /** - * Simple block handler which set status code to be 403 add one header with score and write to page "You are blocked" - */ - @Override - public void handleBlocking(PXContext context, HttpServletResponseWrapper responseWrapper) throws PXException { - responseWrapper.setHeader("PX-Blocking-Score", String.valueOf(context.getScore())); - try { - responseWrapper.setStatus(403); - responseWrapper.getWriter().print("You are blocked"); - } catch (IOException e) { - e.printStackTrace(); - } - } -} diff --git a/pom.xml b/pom.xml index 583bdef2..2e174612 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ PerimeterX JAVA SDK com.perimeterx perimeterx-sdk - 6.12.0 + 6.13.0 jar PerimeterX Java SDK diff --git a/px_metadata.json b/px_metadata.json index 1172ab20..78e2a0f7 100644 --- a/px_metadata.json +++ b/px_metadata.json @@ -1,5 +1,5 @@ { - "version": "6.12.0", + "version": "6.13.0", "supported_features": [ "advanced_blocking_response", "bypass_monitor_header", diff --git a/src/main/java/com/perimeterx/models/PXContext.java b/src/main/java/com/perimeterx/models/PXContext.java index ece56cff..4073cb55 100644 --- a/src/main/java/com/perimeterx/models/PXContext.java +++ b/src/main/java/com/perimeterx/models/PXContext.java @@ -404,8 +404,12 @@ private void setVidAndPxhd(Cookie[] cookies) { if (cookies != null) { for (Cookie cookie : cookies) { if (cookie.getName().equals("_pxvid") || cookie.getName().equals("pxvid")) { - this.vid = cookie.getValue(); - this.vidSource = VidSource.VID_COOKIE; + if (isValidVid(cookie.getValue())) { + this.vid = cookie.getValue(); + this.vidSource = VidSource.VID_COOKIE; + } else { + logger.debug("setVidAndPxhd - invalid VID value was extracted"); + } } if (cookie.getName().equals("_pxhd")) { try { @@ -548,4 +552,10 @@ public boolean isBreachedAccount() { public boolean isContainCredentialsIntelligence() { return this.getLoginData() != null && this.getLoginData().getLoginCredentials() != null; } + + private boolean isValidVid(String vidCookieValue) { + Pattern vidPattern = Pattern.compile("^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$", Pattern.CASE_INSENSITIVE); + Matcher matcher = vidPattern.matcher(vidCookieValue); + return matcher.matches(); + } } diff --git a/web/pom.xml b/web/pom.xml index df937f8c..46665da7 100644 --- a/web/pom.xml +++ b/web/pom.xml @@ -38,10 +38,11 @@ slf4j-log4j12 1.7.25 + - log4j - log4j - 1.2.17 + org.apache.logging.log4j + log4j-core + 2.23.1 @@ -64,7 +65,7 @@ 8 8 - 6.12.0 + 6.13.0