This file is part of the Open Web Application Security Project (OWASP) Benchmark Project For
+ * details, please see https://owasp.org/www-project-benchmark/.
+ *
+ *
The OWASP Benchmark is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, version 2.
+ *
+ *
The OWASP Benchmark is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * @author David Anderson
+ * @created 2024
+ */
+package org.owasp.benchmarkutils.entities;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.validation.constraints.NotNull;
+import javax.xml.bind.Marshaller;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorValue;
+
+@XmlDiscriminatorValue("CliArg")
+// @XmlType(name = "CliArgExecutableTestCaseInput")
+public class CliArgExecutableTestCaseInput extends ExecutableTestCaseInput {
+
+ List args;
+
+ void beforeMarshal(Marshaller marshaller) {
+ // System.out.println("Before marshal");
+ if (args != null && args.isEmpty()) args = null;
+ }
+
+ void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
+ // System.out.println("After unmarshal");
+ if (args == null) args = new ArrayList();
+ }
+
+ @XmlElementWrapper(name = "args")
+ @XmlElement(name = "arg", required = true)
+ @NotNull
+ public List getArgs() {
+ return args;
+ }
+
+ public void setArgs(List args) {
+ // Copy the given list so setSafe() does not affect other CliArgExecutableTestCaseInput
+ // objects.
+ this.args = new ArrayList<>(args);
+ }
+
+ public void addArg(RequestVariable arg) {
+ if (this.args == null) {
+ this.args = new ArrayList<>();
+ }
+ this.args.add(arg);
+ }
+
+ public CliRequest buildAttackRequest() {
+ // ArrayList executeArgs = new ArrayList<>();
+ // // FIXME: This will break if the command string has arguments that contain spaces.
+ // executeArgs.addAll(Arrays.asList(getCommand().split(" ")));
+ // executeArgs.addAll(getArgs());
+ ArrayList argsCopy = new ArrayList<>();
+ for (RequestVariable arg : args) {
+ RequestVariable argCopy = new RequestVariable(arg);
+ argCopy.setSafe(false);
+ argsCopy.add(argCopy);
+ }
+ return new CliRequest(getCommand(), argsCopy, null);
+ }
+
+ public CliRequest buildSafeRequest() {
+ ArrayList argsCopy = new ArrayList<>();
+ for (RequestVariable arg : args) {
+ RequestVariable argCopy = new RequestVariable(arg);
+ argCopy.setSafe(true);
+ argsCopy.add(argCopy);
+ }
+ return new CliRequest(getCommand(), argsCopy, null);
+ }
+
+ public void setSafe(boolean isSafe) {
+ // this.isSafe = isSafe;
+ for (RequestVariable arg : getArgs()) {
+ // setSafe() considers whether attack and safe values exist for this parameter before
+ // setting isSafe true or false. So you don't have to check that here.
+ arg.setSafe(isSafe);
+ }
+ }
+
+ // @Override
+ // public String toString() {
+ // return this.getClass().getSimpleName() + " [args=" + getArgs() + "]";
+ // }
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName()
+ + "["
+ + "command="
+ + getCommand()
+ + ", args="
+ + getArgs()
+ + "]";
+ }
+
+ // public void execute() {
+ // List executeArgs = Arrays.asList(getCommand());
+ //
+ // // crawlArgs.extend([arg1])
+ // // child = pexpect.spawn("python", cwd=TEST_SUITE_DIR, args=crawlArgs)
+ // // child.logfile = sys.stdout
+ // // child.expect(pexpect.EOF)
+ // // child.close()
+ // // print("Return code: %d" % child.exitstatus)
+ //
+ // executeArgs.add(getPayload());
+ // ProcessBuilder builder = new ProcessBuilder(executeArgs);
+ // final Process process = builder.start();
+ // int exitValue = process.waitFor();
+ // System.out.printf("Program terminated with return code: %s%n", exitValue);
+ // }
+
+}
diff --git a/library/src/main/java/org/owasp/benchmarkutils/entities/CliFileExecutableTestCaseInput.java b/library/src/main/java/org/owasp/benchmarkutils/entities/CliFileExecutableTestCaseInput.java
new file mode 100644
index 00000000..9009aed8
--- /dev/null
+++ b/library/src/main/java/org/owasp/benchmarkutils/entities/CliFileExecutableTestCaseInput.java
@@ -0,0 +1,87 @@
+/**
+ * OWASP Benchmark Project
+ *
+ *
This file is part of the Open Web Application Security Project (OWASP) Benchmark Project For
+ * details, please see https://owasp.org/www-project-benchmark/.
+ *
+ *
The OWASP Benchmark is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, version 2.
+ *
+ *
The OWASP Benchmark is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * @author David Anderson
+ * @created 2024
+ */
+package org.owasp.benchmarkutils.entities;
+
+import java.util.List;
+import javax.validation.constraints.NotNull;
+import javax.xml.bind.annotation.XmlElement;
+import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorValue;
+
+@XmlDiscriminatorValue("CliFile")
+public class CliFileExecutableTestCaseInput extends ExecutableTestCaseInput {
+
+ List fileArgs;
+
+ @XmlElement(name = "fileArg", required = true)
+ @NotNull
+ public List getFileArgs() {
+ return fileArgs;
+ }
+
+ public CliRequest buildAttackRequest() {
+ // ArrayList executeArgs = new ArrayList<>();
+ // // FIXME: This will break if the command string has arguments that contain spaces.
+ // executeArgs.addAll(Arrays.asList(getCommand().split(" ")));
+ // executeArgs.addAll(getArgs());
+
+ setSafe(false);
+ return new CliRequest(getCommand(), getFileArgs(), null);
+ }
+
+ public CliRequest buildSafeRequest() {
+ setSafe(true);
+ return new CliRequest(getCommand(), getFileArgs(), null);
+ }
+
+ public void setSafe(boolean isSafe) {
+ // this.isSafe = isSafe;
+ for (RequestVariable arg : getFileArgs()) {
+ // setSafe() considers whether attack and safe values exist for this parameter before
+ // setting isSafe true or false. So you don't have to check that here.
+ arg.setSafe(isSafe);
+ }
+ }
+
+ // public void execute() {
+ // List executeArgs = Arrays.asList(getCommand());
+ //
+ // File argsFile = new File(TEST_SUITE_DIR, "args_file.txt");
+ //
+ // // args_file = 'args_file.txt'
+ // // with open(TEST_SUITE_DIR + args_file, 'w') as f:
+ // // f.write(arg1)
+ // // crawlArgs.extend([args_file])
+ // // child = pexpect.spawn("python", cwd=TEST_SUITE_DIR, args=crawlArgs)
+ // // child.logfile = sys.stdout
+ // // child.expect(pexpect.EOF)
+ // // child.close()
+ // // print("Return code: %d" % child.exitstatus)
+ //
+ // executeArgs.add(getPayload());
+ // executeArgs.add("-f");
+ // executeArgs.add(argsFile.getPath());
+ // try (PrintWriter writer = new PrintWriter(new FileWriter(argsFile))) {
+ // writer.print(getPayload());
+ // }
+ //
+ // ProcessBuilder builder = new ProcessBuilder(executeArgs);
+ // final Process process = builder.start();
+ // int exitValue = process.waitFor();
+ // System.out.printf("Program terminated with return code: %s%n", exitValue);
+ // }
+}
diff --git a/library/src/main/java/org/owasp/benchmarkutils/entities/CliRequest.java b/library/src/main/java/org/owasp/benchmarkutils/entities/CliRequest.java
new file mode 100644
index 00000000..61b59352
--- /dev/null
+++ b/library/src/main/java/org/owasp/benchmarkutils/entities/CliRequest.java
@@ -0,0 +1,99 @@
+/**
+ * OWASP Benchmark Project
+ *
+ *
This file is part of the Open Web Application Security Project (OWASP) Benchmark Project For
+ * details, please see https://owasp.org/www-project-benchmark/.
+ *
+ *
The OWASP Benchmark is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, version 2.
+ *
+ *
The OWASP Benchmark is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * @author David Anderson
+ * @created 2024
+ */
+package org.owasp.benchmarkutils.entities;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class CliRequest {
+ private String command;
+
+ private List args;
+
+ private RequestVariable stdinData;
+
+ public CliRequest() {}
+
+ public CliRequest(String command, List args, RequestVariable stdinData) {
+ super();
+ this.command = command;
+ if (args == null) {
+ this.args = new ArrayList();
+ } else {
+ this.args = new ArrayList(args);
+ }
+ this.stdinData = stdinData;
+ }
+
+ // public CliRequest(String command, RequestVariable arg, RequestVariable stdinData) {
+ // super();
+ // this.command = command;
+ // // Make a copy of the given args list so that when setSafe() changes elements, the
+ // changes
+ // // do not affect other CliRequest objects.
+ // this.args = new ArrayList(Arrays.asList(arg));
+ // this.stdinData = stdinData;
+ // }
+
+ public String getCommand() {
+ return command;
+ }
+
+ public void setCommand(String command) {
+ this.command = command;
+ }
+
+ public List getArgs() {
+ return args;
+ }
+
+ public void setArgs(List args) {
+ if (args == null) {
+ this.args = new ArrayList();
+ } else {
+ this.args = new ArrayList(args);
+ }
+ }
+
+ // public List getExecuteArgs() {
+ // List executeArgs = Arrays.asList(getCommand().split(" "));
+ // executeArgs.addAll(getArgs());
+ // return executeArgs;
+ // }
+
+ public RequestVariable getStdinData() {
+ return stdinData;
+ }
+
+ public void setStdinData(RequestVariable stdinData) {
+ this.stdinData = stdinData;
+ }
+
+ public String toString() {
+ ArrayList executeArgs = new ArrayList<>(Arrays.asList(command.split(" ")));
+ for (RequestVariable arg : args) {
+ executeArgs.add(arg.getValue());
+ }
+ String s = String.join(" ", executeArgs);
+ if (getStdinData() != null) {
+ s += " stdin: " + getStdinData().getValue();
+ }
+ return s;
+ }
+}
diff --git a/library/src/main/java/org/owasp/benchmarkutils/entities/CliResponseInfo.java b/library/src/main/java/org/owasp/benchmarkutils/entities/CliResponseInfo.java
new file mode 100644
index 00000000..4fc4cdcb
--- /dev/null
+++ b/library/src/main/java/org/owasp/benchmarkutils/entities/CliResponseInfo.java
@@ -0,0 +1,43 @@
+/**
+ * OWASP Benchmark Project
+ *
+ *
This file is part of the Open Web Application Security Project (OWASP) Benchmark Project For
+ * details, please see https://owasp.org/www-project-benchmark/.
+ *
+ *
The OWASP Benchmark is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, version 2.
+ *
+ *
The OWASP Benchmark is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * @author David Anderson
+ * @created 2024
+ */
+package org.owasp.benchmarkutils.entities;
+
+import javax.xml.bind.annotation.XmlElement;
+
+public class CliResponseInfo extends ResponseInfo {
+
+ private CliRequest request;
+
+ public CliResponseInfo() {
+ // Default is this is a normal, non-attack response
+ super();
+ }
+
+ public CliResponseInfo(boolean attackRequest) {
+ super(attackRequest);
+ }
+
+ @XmlElement(required = true)
+ public CliRequest getRequest() {
+ return request;
+ }
+
+ public void setRequest(CliRequest request) {
+ this.request = request;
+ }
+}
diff --git a/library/src/main/java/org/owasp/benchmarkutils/entities/CliTestCase.java b/library/src/main/java/org/owasp/benchmarkutils/entities/CliTestCase.java
new file mode 100644
index 00000000..b4a099c7
--- /dev/null
+++ b/library/src/main/java/org/owasp/benchmarkutils/entities/CliTestCase.java
@@ -0,0 +1,70 @@
+/**
+ * OWASP Benchmark Project
+ *
+ *
This file is part of the Open Web Application Security Project (OWASP) Benchmark Project For
+ * details, please see https://owasp.org/www-project-benchmark/.
+ *
+ *
The OWASP Benchmark is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, version 2.
+ *
+ *
The OWASP Benchmark is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * @author David Anderson
+ * @created 2024
+ */
+package org.owasp.benchmarkutils.entities;
+
+import javax.xml.bind.annotation.XmlElement;
+
+public class CliTestCase extends TestCase {
+
+ private ExecutableTestCaseInput testCaseInput;
+
+ @Override
+ @XmlElement(name = "input", required = true)
+ public ExecutableTestCaseInput getTestCaseInput() {
+ return testCaseInput;
+ }
+
+ // public void execute() {
+ //
+ // // FIXME: What would the executable testcase's attackRequest look like?
+ // HttpUriRequest attackRequest = getTestCaseInput().buildAttackRequest();
+ // HttpUriRequest safeRequest = getTestCaseInput().buildSafeRequest();
+ //
+ // // Send the next test case request with its attack payload
+ // ResponseInfo attackPayloadResponseInfo = sendRequest(httpclient, attackRequest);
+ // responseInfoList.add(attackPayloadResponseInfo);
+ //
+ // // Log the response
+ // log(attackPayloadResponseInfo);
+ //
+ // ResponseInfo safePayloadResponseInfo = null;
+ // if (!isUnverifiable()) {
+ // // Send the next test case request with its safe payload
+ // safePayloadResponseInfo = sendRequest(httpclient, safeRequest);
+ // responseInfoList.add(safePayloadResponseInfo);
+ //
+ // // Log the response
+ // log(safePayloadResponseInfo);
+ // }
+ //
+ // TestCaseVerificationResults result =
+ // new TestCaseVerificationResults(
+ // attackRequest,
+ // safeRequest,
+ // this,
+ // attackPayloadResponseInfo,
+ // safePayloadResponseInfo);
+ //
+ // // Verify the response
+ // if (RegressionTesting.isTestingEnabled) {
+ // handleResponse(result);
+ // }
+ //
+ // return result;
+ // }
+}
diff --git a/library/src/main/java/org/owasp/benchmarkutils/entities/ContentFormatEnum.java b/library/src/main/java/org/owasp/benchmarkutils/entities/ContentFormatEnum.java
new file mode 100644
index 00000000..eb886075
--- /dev/null
+++ b/library/src/main/java/org/owasp/benchmarkutils/entities/ContentFormatEnum.java
@@ -0,0 +1,27 @@
+/**
+ * OWASP Benchmark Project
+ *
+ *
This file is part of the Open Web Application Security Project (OWASP) Benchmark Project For
+ * details, please see https://owasp.org/www-project-benchmark/.
+ *
+ *
The OWASP Benchmark is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, version 2.
+ *
+ *
The OWASP Benchmark is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * @author David Anderson
+ * @created 2024
+ */
+package org.owasp.benchmarkutils.entities;
+
+import javax.xml.bind.annotation.*;
+
+@XmlType(name = "ContentFormat")
+@XmlEnum
+public enum ContentFormatEnum {
+ JSON,
+ XML;
+}
diff --git a/library/src/main/java/org/owasp/benchmarkutils/entities/ExecutableTestCaseInput.java b/library/src/main/java/org/owasp/benchmarkutils/entities/ExecutableTestCaseInput.java
new file mode 100644
index 00000000..1a18ff6a
--- /dev/null
+++ b/library/src/main/java/org/owasp/benchmarkutils/entities/ExecutableTestCaseInput.java
@@ -0,0 +1,103 @@
+/**
+ * OWASP Benchmark Project
+ *
+ *
This file is part of the Open Web Application Security Project (OWASP) Benchmark Project For
+ * details, please see https://owasp.org/www-project-benchmark/.
+ *
+ *
The OWASP Benchmark is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, version 2.
+ *
+ *
The OWASP Benchmark is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * @author David Anderson
+ * @created 2024
+ */
+package org.owasp.benchmarkutils.entities;
+
+import javax.validation.constraints.NotNull;
+import javax.xml.bind.annotation.XmlElement;
+
+public abstract class ExecutableTestCaseInput extends TestCaseInput {
+
+ private String command;
+
+ // private String payload;
+
+ @XmlElement(name = "command", required = true)
+ @NotNull
+ public String getCommand() {
+ return command;
+ }
+
+ public void setCommand(String command) {
+ this.command = command;
+ }
+
+ public abstract CliRequest buildAttackRequest();
+
+ public abstract CliRequest buildSafeRequest();
+
+ // public String getPayload() {
+ // return payload;
+ // }
+ //
+ // public void setPayload(String payload) {
+ // this.payload = payload;
+ // }
+
+ // public void execute() {
+ // // Execute the appropriate command string
+ // List executeArgs = Arrays.asList(command);
+ // // if (isSingleApplication) {
+ // // executeArgs = Arrays.asList("benchmark-python.py", "-t", this.getName());
+ // // } else {
+ // // executeArgs = Arrays.asList("testcode/" + "JulietPyTest" + this.getName() +
+ // ".py");
+ // // }
+ //
+ // if (payloadType == PayloadType.CLI_ARG) {
+ // // crawlArgs.extend([arg1])
+ // // child = pexpect.spawn("python", cwd=TEST_SUITE_DIR, args=crawlArgs)
+ // // child.logfile = sys.stdout
+ // // child.expect(pexpect.EOF)
+ // // child.close()
+ // // print("Return code: %d" % child.exitstatus)
+ //
+ // executeArgs.add(payload);
+ // ProcessBuilder builder = new ProcessBuilder(executeArgs);
+ // final Process process = builder.start();
+ // int exitValue = process.waitFor();
+ // System.out.printf("Program terminated with return code: %s%n", exitValue);
+ //
+ // } else if (payloadType == PayloadType.CLI_FILE) {
+ // // args_file = 'args_file.txt'
+ // // with open(TEST_SUITE_DIR + args_file, 'w') as f:
+ // // f.write(arg1)
+ // // crawlArgs.extend([args_file])
+ // // child = pexpect.spawn("python", cwd=TEST_SUITE_DIR, args=crawlArgs)
+ // // child.logfile = sys.stdout
+ // // child.expect(pexpect.EOF)
+ // // child.close()
+ // // print("Return code: %d" % child.exitstatus)
+ // } else if (payloadType == PayloadType.CLI_STDIN) {
+ // // child = pexpect.spawn("python", cwd=TEST_SUITE_DIR, args=crawlArgs)
+ // // #child.interact()
+ // // child.sendline(arg1)
+ // // child.logfile = sys.stdout
+ // // child.expect(pexpect.EOF)
+ // // child.close()
+ // // print("Return code: %d" % child.exitstatus)
+ // } else {
+ // // TODO: Throw an exception?
+ // System.out.printf("ERROR: Unrecognized payload type: %s%n", payloadType);
+ // }
+ // }
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName() + "[" + "command=" + command + "]";
+ }
+}
diff --git a/library/src/main/java/org/owasp/benchmarkutils/entities/FileCopyConfig.java b/library/src/main/java/org/owasp/benchmarkutils/entities/FileCopyConfig.java
new file mode 100644
index 00000000..2327b926
--- /dev/null
+++ b/library/src/main/java/org/owasp/benchmarkutils/entities/FileCopyConfig.java
@@ -0,0 +1,63 @@
+/**
+ * OWASP Benchmark Project
+ *
+ *
This file is part of the Open Web Application Security Project (OWASP) Benchmark Project For
+ * details, please see https://owasp.org/www-project-benchmark/.
+ *
+ *
The OWASP Benchmark is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, version 2.
+ *
+ *
The OWASP Benchmark is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * @author David Anderson
+ * @created 2024
+ */
+package org.owasp.benchmarkutils.entities;
+
+import com.google.common.io.Files;
+import java.io.File;
+import java.io.IOException;
+import javax.validation.constraints.NotNull;
+import javax.xml.bind.annotation.XmlAttribute;
+
+public class FileCopyConfig extends TestCaseSetup {
+
+ private String sourceFile;
+
+ private String destinationFile;
+
+ public void setup() throws TestCaseSetupException {
+ try {
+ Files.copy(new File(sourceFile), new File(destinationFile));
+ } catch (IOException e) {
+ throw new TestCaseSetupException("Could not setup HttpClientConfig for test case", e);
+ }
+ }
+
+ public void close() throws TestCaseSetupException {
+ // Do nothing
+ }
+
+ @XmlAttribute(name = "source", required = true)
+ @NotNull
+ public String getSourceFile() {
+ return sourceFile;
+ }
+
+ public void setSourceFile(String sourceFile) {
+ this.sourceFile = sourceFile;
+ }
+
+ @XmlAttribute(name = "destination", required = true)
+ @NotNull
+ public String getDestinationFile() {
+ return destinationFile;
+ }
+
+ public void setDestinationFile(String destinationFile) {
+ this.destinationFile = destinationFile;
+ }
+}
diff --git a/library/src/main/java/org/owasp/benchmarkutils/entities/HttpClientConfig.java b/library/src/main/java/org/owasp/benchmarkutils/entities/HttpClientConfig.java
new file mode 100644
index 00000000..e9c0cb58
--- /dev/null
+++ b/library/src/main/java/org/owasp/benchmarkutils/entities/HttpClientConfig.java
@@ -0,0 +1,86 @@
+/**
+ * OWASP Benchmark Project
+ *
+ *
This file is part of the Open Web Application Security Project (OWASP) Benchmark Project For
+ * details, please see https://owasp.org/www-project-benchmark/.
+ *
+ *
The OWASP Benchmark is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, version 2.
+ *
+ *
The OWASP Benchmark is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * @author David Anderson
+ * @created 2024
+ */
+package org.owasp.benchmarkutils.entities;
+
+import java.io.IOException;
+import java.security.KeyManagementException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLContext;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.HttpClients;
+import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
+import org.apache.hc.client5.http.io.HttpClientConnectionManager;
+import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
+import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
+import org.apache.hc.client5.http.ssl.TrustAllStrategy;
+import org.apache.hc.core5.ssl.SSLContextBuilder;
+
+public class HttpClientConfig extends TestCaseSetup {
+
+ private static CloseableHttpClient httpclient;
+
+ // This method taken directly from:
+ // https://memorynotfound.com/ignore-certificate-errors-apache-httpclient/
+ private CloseableHttpClient createAcceptSelfSignedCertificateClient()
+ throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException {
+
+ // use the TrustSelfSignedStrategy to allow Self Signed Certificates
+ SSLContext sslContext =
+ SSLContextBuilder.create()
+ .loadTrustMaterial(null, TrustAllStrategy.INSTANCE)
+ .build();
+
+ // we can optionally disable hostname verification.
+ // if you don't want to further weaken the security, you don't have to include this.
+ HostnameVerifier allowAllHosts = new NoopHostnameVerifier();
+
+ // create an SSL Socket Factory to use the SSLContext with the trust self signed certificate
+ // strategy and allow all hosts verifier.
+ SSLConnectionSocketFactory connectionFactory =
+ new SSLConnectionSocketFactory(sslContext, allowAllHosts);
+ HttpClientConnectionManager connectionManager =
+ PoolingHttpClientConnectionManagerBuilder.create()
+ .setSSLSocketFactory(connectionFactory)
+ .build();
+
+ // finally create the HttpClient using HttpClient factory methods and assign the SSL Socket
+ // Factory
+ return HttpClients.custom().setConnectionManager(connectionManager).build();
+ }
+
+ public void setup() throws TestCaseSetupException {
+ if (httpclient == null) {
+ try {
+ httpclient = createAcceptSelfSignedCertificateClient();
+ } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) {
+ throw new TestCaseSetupException(
+ "Could not setup HttpClientConfig for test case", e);
+ }
+ }
+ }
+
+ public void close() throws TestCaseSetupException {
+ try {
+ httpclient.close();
+ } catch (IOException e) {
+ throw new TestCaseSetupException("Could not close HttpClientConfig for test case", e);
+ }
+ }
+}
diff --git a/library/src/main/java/org/owasp/benchmarkutils/entities/HttpGetTestCaseInput.java b/library/src/main/java/org/owasp/benchmarkutils/entities/HttpGetTestCaseInput.java
new file mode 100644
index 00000000..cdc744a3
--- /dev/null
+++ b/library/src/main/java/org/owasp/benchmarkutils/entities/HttpGetTestCaseInput.java
@@ -0,0 +1,53 @@
+/**
+ * OWASP Benchmark Project
+ *
+ *
This file is part of the Open Web Application Security Project (OWASP) Benchmark Project For
+ * details, please see https://owasp.org/www-project-benchmark/.
+ *
+ *
The OWASP Benchmark is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, version 2.
+ *
+ *
The OWASP Benchmark is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * @author David Anderson
+ * @created 2024
+ */
+package org.owasp.benchmarkutils.entities;
+
+import org.apache.hc.client5.http.classic.methods.HttpGet;
+import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase;
+import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorValue;
+
+@XmlDiscriminatorValue("HttpGet")
+// @XmlType(name = "HttpGetTestCaseInput")
+public class HttpGetTestCaseInput extends HttpTestCaseInput {
+ void buildQueryString() {
+ setQueryString("");
+ boolean first = true;
+ for (RequestVariable field : getGetParameters()) {
+ if (first) {
+ setQueryString("?");
+ first = false;
+ } else {
+ setQueryString(getQueryString() + "&");
+ }
+ String name = field.getName();
+ String value = field.getValue();
+ // System.out.println(query);
+ setQueryString(getQueryString() + (name + "=" + urlEncode(value)));
+ }
+ }
+
+ void buildBodyParameters(HttpUriRequestBase request) {
+ // No request body
+ }
+
+ @Override
+ HttpUriRequestBase createRequestInstance(String url) {
+ HttpGet httpGet = new HttpGet(url);
+ return httpGet;
+ }
+}
diff --git a/library/src/main/java/org/owasp/benchmarkutils/entities/HttpPostTestCaseInput.java b/library/src/main/java/org/owasp/benchmarkutils/entities/HttpPostTestCaseInput.java
new file mode 100644
index 00000000..75ca8724
--- /dev/null
+++ b/library/src/main/java/org/owasp/benchmarkutils/entities/HttpPostTestCaseInput.java
@@ -0,0 +1,72 @@
+/**
+ * OWASP Benchmark Project
+ *
+ *
This file is part of the Open Web Application Security Project (OWASP) Benchmark Project For
+ * details, please see https://owasp.org/www-project-benchmark/.
+ *
+ *
The OWASP Benchmark is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, version 2.
+ *
+ *
The OWASP Benchmark is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * @author David Anderson
+ * @created 2024
+ */
+package org.owasp.benchmarkutils.entities;
+
+import org.apache.hc.client5.http.classic.methods.HttpPost;
+import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase;
+import org.apache.hc.core5.http.io.entity.StringEntity;
+import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
+import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorValue;
+
+@XmlDiscriminatorValue("HttpPost")
+// @XmlType(name = "HttpPostTestCaseInput")
+public class HttpPostTestCaseInput extends HttpTestCaseInput {
+ @Override
+ void buildQueryString() {
+ setQueryString("");
+ boolean first = true;
+ for (RequestVariable field : getGetParameters()) {
+ if (first) {
+ setQueryString("?");
+ first = false;
+ } else {
+ setQueryString(getQueryString() + "&");
+ }
+ String name = field.getName();
+ String value = field.getValue();
+ // System.out.println(query);
+ setQueryString(getQueryString() + (name + "=" + urlEncode(value)));
+ }
+ }
+
+ @Override
+ void buildBodyParameters(HttpUriRequestBase request) {
+ boolean first = true;
+ String params = "{";
+ for (RequestVariable field : getFormParameters()) {
+ String name = field.getName();
+ String value = field.getValue();
+ // System.out.println(name+"="+value);
+ if (first) {
+ first = false;
+ } else {
+ params = params + ",";
+ }
+ params = params + String.format("\"%s\":\"%s\"", name, value.replace("\"", "\\\""));
+ }
+ params += "}";
+ StringEntity paramsEnt = new StringEntity(params);
+ ((BasicClassicHttpRequest) request).setEntity(paramsEnt);
+ }
+
+ @Override
+ HttpUriRequestBase createRequestInstance(String url) {
+ HttpPost httpPost = new HttpPost(url);
+ return httpPost;
+ }
+}
diff --git a/library/src/main/java/org/owasp/benchmarkutils/entities/HttpResponseInfo.java b/library/src/main/java/org/owasp/benchmarkutils/entities/HttpResponseInfo.java
new file mode 100644
index 00000000..ad00d190
--- /dev/null
+++ b/library/src/main/java/org/owasp/benchmarkutils/entities/HttpResponseInfo.java
@@ -0,0 +1,65 @@
+/**
+ * OWASP Benchmark Project
+ *
+ *
This file is part of the Open Web Application Security Project (OWASP) Benchmark Project For
+ * details, please see https://owasp.org/www-project-benchmark/.
+ *
+ *
The OWASP Benchmark is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, version 2.
+ *
+ *
The OWASP Benchmark is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * @author David Anderson
+ * @created 2024
+ */
+package org.owasp.benchmarkutils.entities;
+
+import javax.xml.bind.annotation.XmlElement;
+
+public class HttpResponseInfo extends ResponseInfo {
+
+ // private HttpUriRequest requestBase;
+
+ private String method;
+
+ private String uri;
+
+ public HttpResponseInfo() {
+ // Default is this is a normal, non-attack response
+ super();
+ }
+
+ public HttpResponseInfo(boolean attackRequest) {
+ super(attackRequest);
+ }
+
+ // @XmlElement(required = true)
+ // public HttpUriRequest getRequestBase() {
+ // return requestBase;
+ // }
+ //
+ // public void setRequestBase(HttpUriRequest request) {
+ // this.requestBase = request;
+ // }
+
+ @XmlElement(required = true)
+ public String getMethod() {
+ return method;
+ }
+
+ public void setMethod(String method) {
+ this.method = method;
+ }
+
+ @XmlElement(required = true)
+ public String getUri() {
+ return uri;
+ }
+
+ public void setUri(String uri) {
+ this.uri = uri;
+ }
+}
diff --git a/library/src/main/java/org/owasp/benchmarkutils/entities/HttpTestCase.java b/library/src/main/java/org/owasp/benchmarkutils/entities/HttpTestCase.java
new file mode 100644
index 00000000..2ce98cd0
--- /dev/null
+++ b/library/src/main/java/org/owasp/benchmarkutils/entities/HttpTestCase.java
@@ -0,0 +1,66 @@
+/**
+ * OWASP Benchmark Project
+ *
+ *
This file is part of the Open Web Application Security Project (OWASP) Benchmark Project For
+ * details, please see https://owasp.org/www-project-benchmark/.
+ *
+ *
The OWASP Benchmark is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, version 2.
+ *
+ *
The OWASP Benchmark is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * @author David Anderson
+ * @created 2024
+ */
+package org.owasp.benchmarkutils.entities;
+
+public class HttpTestCase extends TestCase {
+
+ // @Override
+ // @XmlElement(name = "input", required = true)
+ // public HttpTestCaseInput getTestCaseInput() {
+ // return testCaseInput;
+ // }
+
+ // public void execute() {
+ //
+ // // FIXME: What would the executable testcase's attackRequest look like?
+ // HttpUriRequest attackRequest = getTestCaseInput().buildAttackRequest();
+ // HttpUriRequest safeRequest = getTestCaseInput().buildSafeRequest();
+ //
+ // // Send the next test case request with its attack payload
+ // ResponseInfo attackPayloadResponseInfo = sendRequest(httpclient, attackRequest);
+ // responseInfoList.add(attackPayloadResponseInfo);
+ //
+ // // Log the response
+ // log(attackPayloadResponseInfo);
+ //
+ // ResponseInfo safePayloadResponseInfo = null;
+ // if (!isUnverifiable()) {
+ // // Send the next test case request with its safe payload
+ // safePayloadResponseInfo = sendRequest(httpclient, safeRequest);
+ // responseInfoList.add(safePayloadResponseInfo);
+ //
+ // // Log the response
+ // log(safePayloadResponseInfo);
+ // }
+ //
+ // TestCaseVerificationResults result =
+ // new TestCaseVerificationResults(
+ // attackRequest,
+ // safeRequest,
+ // this,
+ // attackPayloadResponseInfo,
+ // safePayloadResponseInfo);
+ //
+ // // Verify the response
+ // if (RegressionTesting.isTestingEnabled) {
+ // handleResponse(result);
+ // }
+ //
+ // return result;
+ // }
+}
diff --git a/library/src/main/java/org/owasp/benchmarkutils/entities/HttpTestCaseInput.java b/library/src/main/java/org/owasp/benchmarkutils/entities/HttpTestCaseInput.java
new file mode 100644
index 00000000..51a932be
--- /dev/null
+++ b/library/src/main/java/org/owasp/benchmarkutils/entities/HttpTestCaseInput.java
@@ -0,0 +1,397 @@
+/**
+ * OWASP Benchmark Project
+ *
+ *
This file is part of the Open Web Application Security Project (OWASP) Benchmark Project For
+ * details, please see https://owasp.org/www-project-benchmark/.
+ *
+ *
The OWASP Benchmark is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, version 2.
+ *
+ *
The OWASP Benchmark is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * @author David Anderson
+ * @created 2024
+ */
+package org.owasp.benchmarkutils.entities;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URLEncoder;
+import java.security.KeyManagementException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.List;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLContext;
+import javax.validation.constraints.NotNull;
+import javax.xml.bind.Marshaller;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+import org.apache.commons.lang.time.StopWatch;
+import org.apache.hc.client5.http.classic.methods.HttpPost;
+import org.apache.hc.client5.http.classic.methods.HttpUriRequest;
+import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
+import org.apache.hc.client5.http.impl.classic.HttpClients;
+import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
+import org.apache.hc.client5.http.io.HttpClientConnectionManager;
+import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
+import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
+import org.apache.hc.client5.http.ssl.TrustAllStrategy;
+import org.apache.hc.core5.http.HttpEntity;
+import org.apache.hc.core5.http.io.entity.EntityUtils;
+import org.apache.hc.core5.ssl.SSLContextBuilder;
+
+public abstract class HttpTestCaseInput extends TestCaseInput {
+
+ private String url;
+
+ protected ContentFormatEnum contentFormat;
+
+ private String queryString;
+
+ private List formParameters;
+
+ private List getParameters;
+
+ private List cookies;
+
+ private List headers;
+
+ // private static CloseableHttpClient httpClient;
+
+ void beforeMarshal(Marshaller marshaller) {
+ // System.out.println("Before marshal");
+ if (formParameters != null && formParameters.isEmpty()) formParameters = null;
+ if (getParameters != null && getParameters.isEmpty()) getParameters = null;
+ if (cookies != null && cookies.isEmpty()) cookies = null;
+ if (headers != null && headers.isEmpty()) headers = null;
+ }
+
+ void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
+ // System.out.println("After unmarshal");
+ if (formParameters == null) formParameters = new ArrayList();
+ if (getParameters == null) getParameters = new ArrayList();
+ if (cookies == null) cookies = new ArrayList();
+ if (headers == null) headers = new ArrayList();
+ }
+
+ @XmlElement(name = "url", required = true)
+ @NotNull
+ public String getUrl() {
+ return url;
+ }
+
+ public String getQueryString() {
+ return queryString;
+ }
+
+ @XmlElementWrapper(name = "formParams", required = false)
+ @XmlElement(name = "formParam", required = false)
+ @NotNull
+ public List getFormParameters() {
+ return formParameters;
+ }
+
+ @XmlElementWrapper(name = "getParams", required = false)
+ @XmlElement(name = "getParam", required = false)
+ @NotNull
+ public List getGetParameters() {
+ return getParameters;
+ }
+
+ @XmlElementWrapper(name = "cookies", required = false)
+ @XmlElement(name = "cookie", required = false)
+ @NotNull
+ public List getCookies() {
+ return cookies;
+ }
+
+ @XmlElementWrapper(name = "headers", required = false)
+ @XmlElement(name = "header", required = false)
+ @NotNull
+ public List getHeaders() {
+ return headers;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ public void setQueryString(String queryString) {
+ this.queryString = queryString;
+ }
+
+ public void setFormParameters(List formParameters) {
+ this.formParameters = formParameters;
+ }
+
+ public void setGetParameters(List getParameters) {
+ this.getParameters = getParameters;
+ }
+
+ public void setCookies(List cookies) {
+ this.cookies = cookies;
+ }
+
+ public void setHeaders(List headers) {
+ this.headers = headers;
+ }
+
+ public void addFormParameter(RequestVariable formParameter) {
+ if (this.formParameters == null) {
+ this.formParameters = new ArrayList<>();
+ }
+ this.formParameters.add(formParameter);
+ }
+
+ public void addGetParameter(RequestVariable getParameter) {
+ if (this.getParameters == null) {
+ this.getParameters = new ArrayList<>();
+ }
+ this.getParameters.add(getParameter);
+ }
+
+ public void addCookie(RequestVariable cookie) {
+ if (this.cookies == null) {
+ this.cookies = new ArrayList<>();
+ }
+ this.cookies.add(cookie);
+ }
+
+ public void addHeader(RequestVariable header) {
+ if (this.headers == null) {
+ this.headers = new ArrayList<>();
+ }
+ this.headers.add(header);
+ }
+
+ @XmlElement
+ public ContentFormatEnum getContentFormat() {
+ return contentFormat;
+ }
+
+ public void setContentFormat(ContentFormatEnum contentFormat) {
+ this.contentFormat = contentFormat;
+ }
+
+ /** Defines what parameters in the body will be sent. */
+ abstract void buildBodyParameters(HttpUriRequestBase request);
+
+ /** Defines what cookies will be sent. */
+ void buildCookies(HttpUriRequestBase request) {
+ for (RequestVariable cookie : getCookies()) {
+ String name = cookie.getName();
+ String value = cookie.getValue();
+ // Note: URL encoding of a space becomes a +, which is OK for Java, but
+ // not other languages. So after URLEncoding, replace all + with %20, which is the
+ // standard URL encoding for a space char.
+ request.addHeader("Cookie", name + "=" + urlEncode(value).replace("+", "%20"));
+ }
+ }
+
+ /** Defines what headers will be sent. */
+ void buildHeaders(HttpUriRequestBase request) {
+ for (RequestVariable header : getHeaders()) {
+ String name = header.getName();
+ String value = header.getValue();
+ // System.out.println("Header:" + name + "=" + value);
+ request.addHeader(name, value);
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ String urlEncode(String input) {
+ return URLEncoder.encode(input);
+ }
+
+ /** Defines how to construct URL query string. */
+ abstract void buildQueryString();
+
+ // public void execute() {
+ // // TODO: Not thread-safe
+ // // TODO: We never close this resource, which is poor form
+ // // TODO: What about other setup tasks, like starting a DB server or app server?
+ // if (httpclient == null) {
+ // httpclient = createAcceptSelfSignedCertificateClient();
+ // }
+ //
+ // HttpUriRequestBase request = buildAttackRequest();
+ //
+ // // Send the next test case request
+ // sendRequest(httpclient, request);
+ // }
+
+ /**
+ * Issue the requested request, measure the time required to execute, then output both to stdout
+ * and the global variable timeString the URL tested, the time required to execute and the
+ * response code.
+ *
+ * @param httpclient - The HTTP client to use to make the request
+ * @param request - The HTTP request to issue
+ */
+ static ResponseInfo sendRequest(CloseableHttpClient httpclient, HttpUriRequest request) {
+ // The default is this is a normal, non-attack request, so send false as isAttack value
+ return sendRequest(httpclient, request, false);
+ }
+
+ /**
+ * Issue the requested request, measure the time required to execute, then output both to stdout
+ * and the global variable timeString the URL tested, the time required to execute and the
+ * response code.
+ *
+ * @param httpclient - The HTTP client to use to make the request
+ * @param request - The HTTP request to issue
+ * @param attackRequest - Is the request an attack, or not
+ */
+ static ResponseInfo sendRequest(
+ CloseableHttpClient httpclient, HttpUriRequest request, boolean attackRequest) {
+ HttpResponseInfo responseInfo = new HttpResponseInfo(attackRequest);
+ // responseInfo.setRequestBase(request);
+ responseInfo.setMethod(request.getMethod());
+ URI uri = null;
+ try {
+ uri = request.getUri();
+ } catch (URISyntaxException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ responseInfo.setUri(uri.toString());
+
+ CloseableHttpResponse response = null;
+
+ boolean isPost = request instanceof HttpPost;
+ System.out.println((isPost ? "POST " : "GET ") + uri);
+
+ StopWatch watch = new StopWatch();
+
+ watch.start();
+ try {
+ response = httpclient.execute(request);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ watch.stop();
+
+ try {
+ HttpEntity entity = response.getEntity();
+ int statusCode = response.getCode();
+ responseInfo.setStatusCode(statusCode);
+ int seconds = (int) watch.getTime() / 1000;
+ responseInfo.setTimeInSeconds(seconds);
+ System.out.printf("--> (%d : %d sec)%n", statusCode, seconds);
+
+ try {
+ responseInfo.setResponseString(EntityUtils.toString(entity));
+ EntityUtils.consume(entity);
+ } catch (IOException | org.apache.hc.core5.http.ParseException e) {
+ e.printStackTrace();
+ }
+ } finally {
+ if (response != null)
+ try {
+ response.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ return responseInfo;
+ }
+
+ /**
+ * TODO: Make this class a POJO TestCase and pass it as an arg to another class TestCaseRequest
+ * that can build an actual HttpUriRequest.
+ *
+ * @return
+ */
+ public HttpUriRequestBase buildRequest() {
+ buildQueryString();
+ HttpUriRequestBase request = createRequestInstance(getUrl() + getQueryString());
+ buildHeaders(request);
+ buildCookies(request);
+ buildBodyParameters(request);
+ return request;
+ }
+
+ abstract HttpUriRequestBase createRequestInstance(String url);
+
+ public HttpUriRequestBase buildAttackRequest() {
+ setSafe(false);
+ return buildRequest();
+ }
+
+ public HttpUriRequestBase buildSafeRequest() {
+ setSafe(true);
+ return buildRequest();
+ }
+
+ public void setSafe(boolean isSafe) {
+ // this.isSafe = isSafe;
+ for (RequestVariable header : getHeaders()) {
+ // setSafe() considers whether attack and safe values exist for this parameter before
+ // setting isSafe true or false. So you don't have to check that here.
+ header.setSafe(isSafe);
+ }
+ for (RequestVariable cookie : getCookies()) {
+ cookie.setSafe(isSafe);
+ }
+ for (RequestVariable getParam : getGetParameters()) {
+ getParam.setSafe(isSafe);
+ }
+ for (RequestVariable formParam : getFormParameters()) {
+ formParam.setSafe(isSafe);
+ }
+ }
+
+ // This method taken directly from:
+ // https://memorynotfound.com/ignore-certificate-errors-apache-httpclient/
+ private CloseableHttpClient createAcceptSelfSignedCertificateClient()
+ throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException {
+
+ // use the TrustSelfSignedStrategy to allow Self Signed Certificates
+ SSLContext sslContext =
+ SSLContextBuilder.create()
+ .loadTrustMaterial(null, TrustAllStrategy.INSTANCE)
+ .build();
+
+ // we can optionally disable hostname verification.
+ // if you don't want to further weaken the security, you don't have to include this.
+ HostnameVerifier allowAllHosts = new NoopHostnameVerifier();
+
+ // create an SSL Socket Factory to use the SSLContext with the trust self signed certificate
+ // strategy and allow all hosts verifier.
+ SSLConnectionSocketFactory connectionFactory =
+ new SSLConnectionSocketFactory(sslContext, allowAllHosts);
+ HttpClientConnectionManager connectionManager =
+ PoolingHttpClientConnectionManagerBuilder.create()
+ .setSSLSocketFactory(connectionFactory)
+ .build();
+
+ // finally create the HttpClient using HttpClient factory methods and assign the SSL Socket
+ // Factory
+ return HttpClients.custom().setConnectionManager(connectionManager).build();
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName()
+ + " [url="
+ + url
+ + ", formParameters="
+ + formParameters
+ + ", getParameters="
+ + getParameters
+ + ", cookies="
+ + cookies
+ + ", headers="
+ + headers
+ + "]";
+ }
+}
diff --git a/plugin/src/main/java/org/owasp/benchmarkutils/helpers/JerseyTestCase.java b/library/src/main/java/org/owasp/benchmarkutils/entities/JerseyTestCase.java
similarity index 95%
rename from plugin/src/main/java/org/owasp/benchmarkutils/helpers/JerseyTestCase.java
rename to library/src/main/java/org/owasp/benchmarkutils/entities/JerseyTestCase.java
index bd50f3bc..359b6d00 100644
--- a/plugin/src/main/java/org/owasp/benchmarkutils/helpers/JerseyTestCase.java
+++ b/library/src/main/java/org/owasp/benchmarkutils/entities/JerseyTestCase.java
@@ -15,7 +15,7 @@
* @author David Anderson
* @created 2021
*/
-package org.owasp.benchmarkutils.helpers;
+package org.owasp.benchmarkutils.entities;
import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorValue;
diff --git a/plugin/src/main/java/org/owasp/benchmarkutils/tools/JerseyTestCaseRequest.java b/library/src/main/java/org/owasp/benchmarkutils/entities/JerseyTestCaseInput.java
similarity index 83%
rename from plugin/src/main/java/org/owasp/benchmarkutils/tools/JerseyTestCaseRequest.java
rename to library/src/main/java/org/owasp/benchmarkutils/entities/JerseyTestCaseInput.java
index 1ed2df56..03df0e95 100644
--- a/plugin/src/main/java/org/owasp/benchmarkutils/tools/JerseyTestCaseRequest.java
+++ b/library/src/main/java/org/owasp/benchmarkutils/entities/JerseyTestCaseInput.java
@@ -10,35 +10,25 @@
*
*
The OWASP Benchmark is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details
+ * PURPOSE. See the GNU General Public License for more details.
*
- * @author Juan Gama
- * @created 2017
+ * @author David Anderson
+ * @created 2024
*/
-package org.owasp.benchmarkutils.tools;
+package org.owasp.benchmarkutils.entities;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase;
import org.apache.hc.core5.http.io.entity.StringEntity;
import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorValue;
-import org.owasp.benchmarkutils.helpers.RequestVariable;
-@XmlDiscriminatorValue("JERSEYWS")
-public class JerseyTestCaseRequest extends AbstractTestCaseRequest {
-
- public JerseyTestCaseRequest() {}
+@XmlDiscriminatorValue("Jersey")
+// @XmlType(name = "HttpPostTestCaseInput")
+public class JerseyTestCaseInput extends HttpTestCaseInput {
@Override
void buildQueryString() {
- setQuery("");
- }
-
- @Override
- HttpUriRequestBase createRequestInstance(String URL) {
- // Apparently all Jersey Requests are POSTS. Never any query string params per buildQuery()
- // above.
- HttpPost httpPost = new HttpPost(URL);
- return httpPost;
+ setQueryString("");
}
@Override
@@ -65,7 +55,7 @@ void buildCookies(HttpUriRequestBase request) {
@Override
void buildBodyParameters(HttpUriRequestBase request) {
String params = "";
- for (RequestVariable field : getFormParams()) {
+ for (RequestVariable field : getFormParameters()) {
String name = field.getName();
String value = field.getValue();
params += "<" + name + ">" + escapeXML(value) + "" + name + ">";
@@ -84,4 +74,12 @@ private static String escapeXML(String value) {
return value;
}
+
+ @Override
+ HttpUriRequestBase createRequestInstance(String url) {
+ // Apparently all Jersey Requests are POSTS. Never any query string params per buildQuery()
+ // above.
+ HttpPost httpPost = new HttpPost(url);
+ return httpPost;
+ }
}
diff --git a/plugin/src/main/java/org/owasp/benchmarkutils/helpers/RequestVariable.java b/library/src/main/java/org/owasp/benchmarkutils/entities/RequestVariable.java
similarity index 83%
rename from plugin/src/main/java/org/owasp/benchmarkutils/helpers/RequestVariable.java
rename to library/src/main/java/org/owasp/benchmarkutils/entities/RequestVariable.java
index 17bd2d78..ee1526b8 100644
--- a/plugin/src/main/java/org/owasp/benchmarkutils/helpers/RequestVariable.java
+++ b/library/src/main/java/org/owasp/benchmarkutils/entities/RequestVariable.java
@@ -13,13 +13,14 @@
* PURPOSE. See the GNU General Public License for more details.
*
* @author David Anderson
- * @created 2021
+ * @created 2024
*/
-package org.owasp.benchmarkutils.helpers;
+package org.owasp.benchmarkutils.entities;
import javax.validation.constraints.NotNull;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlTransient;
import org.apache.hc.core5.http.NameValuePair;
import org.apache.hc.core5.http.message.BasicNameValuePair;
@@ -61,6 +62,19 @@ public RequestVariable(
isSafe = name.equals(safeName) && value.equals(safeValue);
}
+ public RequestVariable(RequestVariable otherRequestVariable) {
+ super();
+ this.name = otherRequestVariable.getName();
+ this.value = otherRequestVariable.getValue();
+ this.attackName = otherRequestVariable.getAttackName();
+ this.attackValue = otherRequestVariable.getAttackValue();
+ this.safeName = otherRequestVariable.getSafeName();
+ this.safeValue = otherRequestVariable.getSafeValue();
+ if (name == null) throw new NullPointerException("name parameter cannot be null");
+ if (value == null) throw new NullPointerException("value parameter cannot be null");
+ isSafe = name.equals(safeName) && value.equals(safeValue);
+ }
+
@XmlAttribute
@NotNull
public String getName() {
@@ -121,6 +135,7 @@ public NameValuePair getNameValuePair() {
return new BasicNameValuePair(name, value);
}
+ @XmlTransient
public boolean isSafe() {
return isSafe;
}
diff --git a/plugin/src/main/java/org/owasp/benchmarkutils/tools/ResponseInfo.java b/library/src/main/java/org/owasp/benchmarkutils/entities/ResponseInfo.java
similarity index 55%
rename from plugin/src/main/java/org/owasp/benchmarkutils/tools/ResponseInfo.java
rename to library/src/main/java/org/owasp/benchmarkutils/entities/ResponseInfo.java
index b4e0b4a3..14f1ae1f 100644
--- a/plugin/src/main/java/org/owasp/benchmarkutils/tools/ResponseInfo.java
+++ b/library/src/main/java/org/owasp/benchmarkutils/entities/ResponseInfo.java
@@ -12,33 +12,51 @@
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
- * @author Dave Wichers
- * @created 2021
+ * @author David Anderson
+ * @created 2024
*/
-package org.owasp.benchmarkutils.tools;
+package org.owasp.benchmarkutils.entities;
-import org.apache.hc.client5.http.classic.methods.HttpUriRequest;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlSeeAlso;
+import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorNode;
-class ResponseInfo {
+@XmlRootElement(name = "ResponseInfo")
+@XmlSeeAlso({CliResponseInfo.class, HttpResponseInfo.class})
+@XmlDiscriminatorNode("@type")
+public abstract class ResponseInfo {
+
+ // True if response to an attack request. False if response to normal request
+ private boolean isAttackResponse = false; // Default
private String responseString;
- private int seconds;
private int statusCode;
- private HttpUriRequest requestBase;
+ private int seconds;
- public String getResponseString() {
- return responseString;
+ public ResponseInfo() {
+ // Default is this is a normal, non-attack response
}
- public void setResponseString(String responseString) {
- this.responseString = responseString;
+ public ResponseInfo(boolean attackRequest) {
+ this.isAttackResponse = attackRequest;
}
- public int getTimeInSeconds() {
- return seconds;
+ public void setIsAttackResponse(boolean isAttackResponse) {
+ this.isAttackResponse = isAttackResponse;
}
- public void setTimeInSeconds(int seconds) {
- this.seconds = seconds;
+ @XmlAttribute(required = true)
+ public boolean getIsAttackResponse() {
+ return isAttackResponse;
+ }
+
+ public void setResponseString(String responseString) {
+ this.responseString = responseString;
+ }
+
+ @XmlAttribute(required = true)
+ public String getResponseString() {
+ return responseString;
}
public int getStatusCode() {
@@ -49,11 +67,11 @@ public void setStatusCode(int statusCode) {
this.statusCode = statusCode;
}
- public HttpUriRequest getRequestBase() {
- return requestBase;
+ public int getTimeInSeconds() {
+ return seconds;
}
- public void setRequestBase(HttpUriRequest request) {
- this.requestBase = request;
+ public void setTimeInSeconds(int seconds) {
+ this.seconds = seconds;
}
}
diff --git a/plugin/src/main/java/org/owasp/benchmarkutils/helpers/ServletTestCase.java b/library/src/main/java/org/owasp/benchmarkutils/entities/ServletTestCase.java
similarity index 95%
rename from plugin/src/main/java/org/owasp/benchmarkutils/helpers/ServletTestCase.java
rename to library/src/main/java/org/owasp/benchmarkutils/entities/ServletTestCase.java
index defa0567..676e78f5 100644
--- a/plugin/src/main/java/org/owasp/benchmarkutils/helpers/ServletTestCase.java
+++ b/library/src/main/java/org/owasp/benchmarkutils/entities/ServletTestCase.java
@@ -15,7 +15,7 @@
* @author David Anderson
* @created 2021
*/
-package org.owasp.benchmarkutils.helpers;
+package org.owasp.benchmarkutils.entities;
import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorValue;
diff --git a/plugin/src/main/java/org/owasp/benchmarkutils/tools/ServletTestCaseRequest.java b/library/src/main/java/org/owasp/benchmarkutils/entities/ServletTestCaseInput.java
similarity index 64%
rename from plugin/src/main/java/org/owasp/benchmarkutils/tools/ServletTestCaseRequest.java
rename to library/src/main/java/org/owasp/benchmarkutils/entities/ServletTestCaseInput.java
index b4e7889f..9db50238 100644
--- a/plugin/src/main/java/org/owasp/benchmarkutils/tools/ServletTestCaseRequest.java
+++ b/library/src/main/java/org/owasp/benchmarkutils/entities/ServletTestCaseInput.java
@@ -10,14 +10,13 @@
*
*
The OWASP Benchmark is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details
+ * PURPOSE. See the GNU General Public License for more details.
*
- * @author Juan Gama
- * @created 2017
+ * @author David Anderson
+ * @created 2024
*/
-package org.owasp.benchmarkutils.tools;
+package org.owasp.benchmarkutils.entities;
-import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import org.apache.hc.client5.http.classic.methods.HttpGet;
@@ -26,45 +25,25 @@
import org.apache.hc.client5.http.entity.UrlEncodedFormEntity;
import org.apache.hc.core5.http.NameValuePair;
import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorValue;
-import org.owasp.benchmarkutils.helpers.RequestVariable;
-/*
- * This class is used by the crawlers to test the target Benchmark style web application. It tests Servlet style
- * web applications that use traditional GET parameters in URLs, POST body parameters, header name/values, cookies,
- * etc. Nothing fancy, specific to particular frameworks, like parameters embedded in the URL path, etc.
- */
-
-@XmlDiscriminatorValue("SERVLET")
-public class ServletTestCaseRequest extends AbstractTestCaseRequest {
+@XmlDiscriminatorValue("Servlet")
+public class ServletTestCaseInput extends HttpTestCaseInput {
- public ServletTestCaseRequest() {}
-
- @SuppressWarnings("deprecation")
@Override
void buildQueryString() {
- setQuery("");
+ setQueryString("");
boolean first = true;
- for (RequestVariable field : getGetParams()) {
+ for (RequestVariable field : getGetParameters()) {
if (first) {
- setQuery("?");
+ setQueryString("?");
first = false;
} else {
- setQuery(getQuery() + "&");
+ setQueryString(getQueryString() + "&");
}
String name = field.getName();
String value = field.getValue();
// System.out.println(query);
- setQuery(getQuery() + (name + "=" + URLEncoder.encode(value)));
- }
- }
-
- @Override
- HttpUriRequestBase createRequestInstance(String URL) {
- // If there are query parameters, this must be a GET, otherwise a POST.
- if (getQuery().length() == 0) {
- return new HttpPost(URL);
- } else {
- return new HttpGet(URL);
+ setQueryString(getQueryString() + (name + "=" + urlEncode(value)));
}
}
@@ -72,7 +51,8 @@ HttpUriRequestBase createRequestInstance(String URL) {
void buildHeaders(HttpUriRequestBase request) {
// AJAX does: text/plain;charset=UTF-8, while HTML Form: application/x-www-form-urlencoded
// request.addHeader("Content-Type", ";charset=UTF-8"); --This BREAKS BenchmarkCrawling
- request.addHeader("Content-Type", "application/x-www-form-urlencoded"); // Works for both though
+ request.addHeader(
+ "Content-Type", "application/x-www-form-urlencoded"); // Works for both though
for (RequestVariable header : getHeaders()) {
String name = header.getName();
@@ -82,7 +62,6 @@ void buildHeaders(HttpUriRequestBase request) {
}
}
- @SuppressWarnings("deprecation")
@Override
void buildCookies(HttpUriRequestBase request) {
for (RequestVariable cookie : getCookies()) {
@@ -91,14 +70,14 @@ void buildCookies(HttpUriRequestBase request) {
// Note: URL encoding of a space becomes a +, which is OK for Java, but
// not other languages. So after URLEncoding, replace all + with %20, which is the
// standard URL encoding for a space char.
- request.addHeader("Cookie", name + "=" + URLEncoder.encode(value).replace("+", "%20"));
+ request.addHeader("Cookie", name + "=" + urlEncode(value).replace("+", "%20"));
}
}
@Override
void buildBodyParameters(HttpUriRequestBase request) {
List fields = new ArrayList<>();
- for (RequestVariable formParam : getFormParams()) {
+ for (RequestVariable formParam : getFormParameters()) {
fields.add(formParam.getNameValuePair());
}
@@ -107,4 +86,15 @@ void buildBodyParameters(HttpUriRequestBase request) {
request.setEntity(new UrlEncodedFormEntity(fields));
}
}
+
+ @Override
+ HttpUriRequestBase createRequestInstance(String url) {
+ HttpUriRequestBase httpUriRequestBase;
+ if (getQueryString().length() == 0) {
+ httpUriRequestBase = new HttpPost(url);
+ } else {
+ httpUriRequestBase = new HttpGet(url);
+ }
+ return httpUriRequestBase;
+ }
}
diff --git a/plugin/src/main/java/org/owasp/benchmarkutils/helpers/SpringTestCase.java b/library/src/main/java/org/owasp/benchmarkutils/entities/SpringTestCase.java
similarity index 95%
rename from plugin/src/main/java/org/owasp/benchmarkutils/helpers/SpringTestCase.java
rename to library/src/main/java/org/owasp/benchmarkutils/entities/SpringTestCase.java
index 147cd025..81e39865 100644
--- a/plugin/src/main/java/org/owasp/benchmarkutils/helpers/SpringTestCase.java
+++ b/library/src/main/java/org/owasp/benchmarkutils/entities/SpringTestCase.java
@@ -15,7 +15,7 @@
* @author David Anderson
* @created 2021
*/
-package org.owasp.benchmarkutils.helpers;
+package org.owasp.benchmarkutils.entities;
import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorValue;
diff --git a/plugin/src/main/java/org/owasp/benchmarkutils/tools/SpringTestCaseRequest.java b/library/src/main/java/org/owasp/benchmarkutils/entities/SpringTestCaseInput.java
similarity index 83%
rename from plugin/src/main/java/org/owasp/benchmarkutils/tools/SpringTestCaseRequest.java
rename to library/src/main/java/org/owasp/benchmarkutils/entities/SpringTestCaseInput.java
index a583c5d4..6bb6d215 100644
--- a/plugin/src/main/java/org/owasp/benchmarkutils/tools/SpringTestCaseRequest.java
+++ b/library/src/main/java/org/owasp/benchmarkutils/entities/SpringTestCaseInput.java
@@ -10,35 +10,25 @@
*
*
The OWASP Benchmark is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details
+ * PURPOSE. See the GNU General Public License for more details.
*
- * @author Juan Gama
- * @created 2017
+ * @author David Anderson
+ * @created 2024
*/
-package org.owasp.benchmarkutils.tools;
+package org.owasp.benchmarkutils.entities;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase;
import org.apache.hc.core5.http.io.entity.StringEntity;
import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorValue;
-import org.owasp.benchmarkutils.helpers.RequestVariable;
-@XmlDiscriminatorValue("SPRINGWS")
-public class SpringTestCaseRequest extends AbstractTestCaseRequest {
-
- public SpringTestCaseRequest() {}
+@XmlDiscriminatorValue("Spring")
+// @XmlType(name = "HttpPostTestCaseInput")
+public class SpringTestCaseInput extends HttpTestCaseInput {
@Override
void buildQueryString() {
- setQuery("");
- }
-
- @Override
- HttpUriRequestBase createRequestInstance(String URL) {
- // Apparently all Spring Requests are POSTS. Never any query string params per buildQuery()
- // above.
- HttpPost httpPost = new HttpPost(URL);
- return httpPost;
+ setQueryString("");
}
@Override
@@ -68,7 +58,7 @@ void buildCookies(HttpUriRequestBase request) {
void buildBodyParameters(HttpUriRequestBase request) {
boolean first = true;
String params = "{";
- for (RequestVariable field : getFormParams()) {
+ for (RequestVariable field : getFormParameters()) {
String name = field.getName();
String value = field.getValue();
// System.out.println(name+"="+value);
@@ -83,4 +73,12 @@ void buildBodyParameters(HttpUriRequestBase request) {
StringEntity paramsEnt = new StringEntity(params);
request.setEntity(paramsEnt);
}
+
+ @Override
+ HttpUriRequestBase createRequestInstance(String url) {
+ // Apparently all Spring Requests are POSTS. Never any query string params per buildQuery()
+ // above.
+ HttpPost httpPost = new HttpPost(url);
+ return httpPost;
+ }
}
diff --git a/library/src/main/java/org/owasp/benchmarkutils/entities/Sqlite3Config.java b/library/src/main/java/org/owasp/benchmarkutils/entities/Sqlite3Config.java
new file mode 100644
index 00000000..e8413bec
--- /dev/null
+++ b/library/src/main/java/org/owasp/benchmarkutils/entities/Sqlite3Config.java
@@ -0,0 +1,93 @@
+/**
+ * OWASP Benchmark Project
+ *
+ *
This file is part of the Open Web Application Security Project (OWASP) Benchmark Project For
+ * details, please see https://owasp.org/www-project-benchmark/.
+ *
+ *
The OWASP Benchmark is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, version 2.
+ *
+ *
The OWASP Benchmark is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * @author David Anderson
+ * @created 2024
+ */
+package org.owasp.benchmarkutils.entities;
+
+import java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.List;
+import javax.validation.constraints.NotNull;
+import javax.xml.bind.annotation.XmlAttribute;
+
+public class Sqlite3Config extends TestCaseSetup {
+
+ private String initializationScriptFile;
+
+ private String scriptFile;
+
+ public void executeScripts(String scriptFile) throws FileNotFoundException, IOException {
+ // db parameters
+ String url = "jdbc:sqlite:./benchmark.db";
+
+ // create a connection to the database
+ try (Connection conn = DriverManager.getConnection(url)) {
+
+ System.out.println("Connection to SQLite has been established.");
+
+ Statement statement = conn.createStatement();
+
+ List lines = new ArrayList();
+ try (BufferedReader reader = new BufferedReader(new FileReader(scriptFile))) {
+ String line;
+ while ((line = reader.readLine()) != null) {
+ lines.add(line);
+ }
+ }
+
+ for (String line : lines) {
+ statement.execute(line);
+ }
+
+ } catch (SQLException e) {
+ System.out.println(e.getMessage());
+ }
+ }
+
+ public void initialize() throws FileNotFoundException, IOException {
+ executeScripts(this.initializationScriptFile);
+ }
+
+ public void setup() throws TestCaseSetupException {
+ try {
+ initialize();
+ executeScripts(getScriptFile());
+ } catch (IOException e) {
+ throw new TestCaseSetupException("Could not setup Sqlite3Config for test case", e);
+ }
+ }
+
+ public void close() throws TestCaseSetupException {
+ // Do nothing
+ }
+
+ @XmlAttribute(name = "script")
+ @NotNull
+ public String getScriptFile() {
+ return scriptFile;
+ }
+
+ public void setScriptFile(String scriptFile) {
+ this.scriptFile = scriptFile;
+ }
+}
diff --git a/library/src/main/java/org/owasp/benchmarkutils/entities/StdinExecutableTestCaseInput.java b/library/src/main/java/org/owasp/benchmarkutils/entities/StdinExecutableTestCaseInput.java
new file mode 100644
index 00000000..e7e21e7d
--- /dev/null
+++ b/library/src/main/java/org/owasp/benchmarkutils/entities/StdinExecutableTestCaseInput.java
@@ -0,0 +1,92 @@
+/**
+ * OWASP Benchmark Project
+ *
+ *
This file is part of the Open Web Application Security Project (OWASP) Benchmark Project For
+ * details, please see https://owasp.org/www-project-benchmark/.
+ *
+ *
The OWASP Benchmark is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, version 2.
+ *
+ *
The OWASP Benchmark is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * @author David Anderson
+ * @created 2024
+ */
+package org.owasp.benchmarkutils.entities;
+
+import javax.validation.constraints.NotNull;
+import javax.xml.bind.annotation.XmlElement;
+import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorValue;
+
+@XmlDiscriminatorValue("Stdin")
+public class StdinExecutableTestCaseInput extends ExecutableTestCaseInput {
+
+ RequestVariable stdinData;
+
+ @XmlElement(name = "stdinData", required = true)
+ @NotNull
+ public RequestVariable getStdinData() {
+ return stdinData;
+ }
+
+ public void setStdinData(RequestVariable stdinData) {
+ this.stdinData = stdinData;
+ }
+
+ public CliRequest buildAttackRequest() {
+ // ArrayList executeArgs = new ArrayList<>();
+ // // FIXME: This will break if the command string has arguments that contain spaces.
+ // executeArgs.addAll(Arrays.asList(getCommand().split(" ")));
+ // executeArgs.addAll(getArgs());
+
+ stdinData.setSafe(false);
+ return new CliRequest(getCommand(), null, getStdinData());
+ }
+
+ public CliRequest buildSafeRequest() {
+ setSafe(true);
+ return new CliRequest(getCommand(), null, getStdinData());
+ }
+
+ public void setSafe(boolean isSafe) {
+ // // this.isSafe = isSafe;
+ // for (RequestVariable arg : getArgs()) {
+ // // setSafe() considers whether attack and safe values exist for this parameter
+ // before
+ // // setting isSafe true or false. So you don't have to check that here.
+ // arg.setSafe(isSafe);
+ // }
+ }
+
+ // public void execute() {
+ // List executeArgs = Arrays.asList(getCommand());
+ //
+ // File argsFile = new File(TEST_SUITE_DIR, "args_file.txt");
+ //
+ // // child = pexpect.spawn("python", cwd=TEST_SUITE_DIR, args=crawlArgs)
+ // // #child.interact()
+ // // child.sendline(arg1)
+ // // child.logfile = sys.stdout
+ // // child.expect(pexpect.EOF)
+ // // child.close()
+ // // print("Return code: %d" % child.exitstatus)
+ //
+ // ProcessBuilder builder = new ProcessBuilder(executeArgs);
+ // final Process process = builder.start();
+ // OutputStream stdin = process.getOutputStream();
+ // InputStream stdout = process.getInputStream();
+ //
+ // BufferedReader reader = new BufferedReader(new InputStreamReader(stdout));
+ // BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(stdin));
+ //
+ // writer.write(getPayload());
+ // writer.flush();
+ // writer.close();
+ //
+ // int exitValue = process.waitFor();
+ // System.out.printf("Program terminated with return code: %s%n", exitValue);
+ // }
+}
diff --git a/library/src/main/java/org/owasp/benchmarkutils/entities/TcpSocketExecutableTestCaseInput.java b/library/src/main/java/org/owasp/benchmarkutils/entities/TcpSocketExecutableTestCaseInput.java
new file mode 100644
index 00000000..afc537f6
--- /dev/null
+++ b/library/src/main/java/org/owasp/benchmarkutils/entities/TcpSocketExecutableTestCaseInput.java
@@ -0,0 +1,60 @@
+/**
+ * OWASP Benchmark Project
+ *
+ *
This file is part of the Open Web Application Security Project (OWASP) Benchmark Project For
+ * details, please see https://owasp.org/www-project-benchmark/.
+ *
+ *
The OWASP Benchmark is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, version 2.
+ *
+ *
The OWASP Benchmark is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * @author David Anderson
+ * @created 2024
+ */
+package org.owasp.benchmarkutils.entities;
+
+import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorValue;
+
+@XmlDiscriminatorValue("TcpSocket")
+public class TcpSocketExecutableTestCaseInput extends ExecutableTestCaseInput {
+
+ public RequestVariable getTcpSocketData() {
+ // FIXME
+ return null;
+ }
+
+ public CliRequest buildAttackRequest() {
+ // ArrayList executeArgs = new ArrayList<>();
+ // // FIXME: This will break if the command string has arguments that contain spaces.
+ // executeArgs.addAll(Arrays.asList(getCommand().split(" ")));
+ // executeArgs.addAll(getArgs());
+
+ setSafe(false);
+ return new CliRequest(getCommand(), null, getTcpSocketData());
+ }
+
+ public CliRequest buildSafeRequest() {
+ setSafe(true);
+ return new CliRequest(getCommand(), null, getTcpSocketData());
+ }
+
+ public void setSafe(boolean isSafe) {
+ // // this.isSafe = isSafe;
+ // for (RequestVariable arg : getArgs()) {
+ // // setSafe() considers whether attack and safe values exist for this parameter
+ // before
+ // // setting isSafe true or false. So you don't have to check that here.
+ // arg.setSafe(isSafe);
+ // }
+ }
+
+ // public void execute() {
+ // // FIXME: Not yet implemented
+ //
+ // // System.out.printf("Program terminated with return code: %s%n", exitValue);
+ // }
+}
diff --git a/library/src/main/java/org/owasp/benchmarkutils/entities/TestCase.java b/library/src/main/java/org/owasp/benchmarkutils/entities/TestCase.java
new file mode 100644
index 00000000..0ab03316
--- /dev/null
+++ b/library/src/main/java/org/owasp/benchmarkutils/entities/TestCase.java
@@ -0,0 +1,337 @@
+/**
+ * OWASP Benchmark Project
+ *
+ *
This file is part of the Open Web Application Security Project (OWASP) Benchmark Project For
+ * details, please see https://owasp.org/www-project-benchmark/.
+ *
+ *
The OWASP Benchmark is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, version 2.
+ *
+ *
The OWASP Benchmark is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * @author David Anderson
+ * @created 2024
+ */
+package org.owasp.benchmarkutils.entities;
+
+import java.util.Comparator;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.validation.constraints.NotNull;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElements;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+import org.eclipse.persistence.oxm.annotations.XmlPath;
+import org.eclipse.persistence.oxm.annotations.XmlPaths;
+import org.owasp.benchmarkutils.helpers.Category;
+import org.owasp.benchmarkutils.helpers.CategoryAdapter;
+
+@XmlRootElement(name = "TestCase")
+public class TestCase {
+
+ private Category category;
+ private String name; // Full name of the test case
+ private int number; // The number of this test case
+
+ private boolean isVulnerability;
+
+ private String sourceFile;
+ private String sourceUIType;
+ private String dataflowFile = "none";
+ private String sinkFile;
+
+ private String templateFile;
+ private String UITemplateFile;
+
+ private String notAutoverifiableReason; // Any value, e.g. "none" sets it notAutoverifiable
+
+ private String verificationResult = "notYetKnown";
+
+ private TestCaseInput testCaseInput;
+ private String attackSuccessString;
+
+ @XmlElements({
+ @XmlElement(type = HttpClientConfig.class),
+ @XmlElement(type = FileCopyConfig.class),
+ @XmlElement(type = Sqlite3Config.class)
+ })
+ @XmlPaths({
+ @XmlPath("fee[@type='HttpClientConfig']"),
+ @XmlPath("fee[@type='FileCopyConfig']"),
+ @XmlPath("fee[@type='Sqlite3Config']")
+ })
+ private TestCaseSetup testCaseSetup;
+
+ // FIXME: These fields are not in the crawler config file, but they need to be captured when
+ // running the verification crawler because we retrieve request details from them to write to
+ // failedTestCases.txt.
+ // private TestExecutor safeTestExecutor;
+ // private TestExecutor attackTestExecutor;
+
+ static final Pattern lastIntPattern = Pattern.compile("[^0-9]+([0-9]+)$");
+
+ public TestCase() {
+ super();
+ }
+
+ @XmlAttribute(name = "AttackSuccessIndicator")
+ public String getAttackSuccessString() {
+ return this.attackSuccessString;
+ }
+
+ @XmlAttribute(name = "Category", required = true)
+ @XmlJavaTypeAdapter(CategoryAdapter.class)
+ @NotNull
+ public Category getCategory() {
+ return category;
+ }
+
+ @XmlAttribute(name = "DataflowFile", required = true)
+ @NotNull
+ public String getDataflowFile() {
+ return dataflowFile;
+ }
+
+ @XmlAttribute(name = "Name", required = true)
+ @NotNull
+ public String getName() {
+ return name;
+ }
+
+ public int getNumber() {
+ return number;
+ }
+
+ @XmlAttribute(name = "NotAutoverifiable")
+ public String getNotAutoverifiableReason() {
+ return notAutoverifiableReason;
+ }
+
+ @XmlAttribute(name = "SinkFile", required = true)
+ @NotNull
+ public String getSinkFile() {
+ return sinkFile;
+ }
+
+ @XmlAttribute(name = "SourceFile", required = true)
+ @NotNull
+ public String getSourceFile() {
+ return sourceFile;
+ }
+
+ @XmlAttribute(name = "SourceUIType", required = true)
+ @NotNull
+ public String getSourceUIType() {
+ return sourceUIType;
+ }
+
+ @XmlAttribute(name = "TemplateFile", required = true)
+ @NotNull
+ public String getTemplateFile() {
+ return templateFile;
+ }
+
+ @XmlAttribute(name = "UITemplateFile", required = true)
+ @NotNull
+ public String getUITemplateFile() {
+ return UITemplateFile;
+ }
+
+ @XmlElement(name = "Input", required = true)
+ public TestCaseInput getTestCaseInput() {
+ return testCaseInput;
+ }
+
+ public TestCaseSetup getTestCaseSetup() {
+ return testCaseSetup;
+ }
+
+ public boolean isUnverifiable() {
+ return getNotAutoverifiableReason() != null;
+ }
+
+ @XmlAttribute(name = "Vulnerability", required = true)
+ public boolean isVulnerability() {
+ return isVulnerability;
+ }
+
+ // public TestCaseRequest getAttackTestCaseRequest() {
+ // return attackTestCaseRequest;
+ // }
+ //
+ // public void setAttackTestCaseRequest(TestCaseRequest attackTestCaseRequest) {
+ // this.attackTestCaseRequest = attackTestCaseRequest;
+ // }
+ //
+ // public TestCaseRequest getSafeTestCaseRequest() {
+ // return safeTestCaseRequest;
+ // }
+ //
+ // public void setSafeTestCaseRequest(TestCaseRequest safeTestCaseRequest) {
+ // this.safeTestCaseRequest = safeTestCaseRequest;
+ // }
+
+ public void setAttackSuccessString(String attackSuccessString) {
+ this.attackSuccessString = attackSuccessString;
+ }
+
+ public void setCategory(Category category) {
+ this.category = category;
+ }
+
+ public void setDataflowFile(String dataflowFile) {
+ this.dataflowFile = dataflowFile;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+
+ // Auto extract the test case number from the name.
+ Matcher matcher = lastIntPattern.matcher(name);
+ if (matcher.find()) {
+ String someNumberStr = matcher.group(1);
+ this.number = Integer.parseInt(someNumberStr);
+ } else {
+ System.out.println(
+ "Warning: TestCaseRequest.setName() invoked with test case name: "
+ + name
+ + " that doesn't end with a test case number.");
+ }
+ }
+
+ public void setNotAutoverifiableReason(String notAutoverifiableReason) {
+ this.notAutoverifiableReason = notAutoverifiableReason;
+ }
+
+ public void setSinkFile(String sinkFile) {
+ this.sinkFile = sinkFile;
+ }
+
+ public void setSourceFile(String sourceFile) {
+ this.sourceFile = sourceFile;
+ }
+
+ public void setSourceUIType(String sourceUIType) {
+ this.sourceUIType = sourceUIType;
+ }
+
+ public void setTemplateFile(String templateFile) {
+ this.templateFile = templateFile;
+ }
+
+ public void setTestCaseInput(TestCaseInput testCaseInput) {
+ this.testCaseInput = testCaseInput;
+ }
+
+ public void setUITemplateFile(String uITemplateFile) {
+ UITemplateFile = uITemplateFile;
+ }
+
+ public void setVulnerability(boolean isVulnerability) {
+ this.isVulnerability = isVulnerability;
+ }
+
+ public void setVerificationResult(String result) {
+ this.verificationResult = result;
+ }
+
+ public void setTestCaseSetup(TestCaseSetup testCaseSetup) {
+ this.testCaseSetup = testCaseSetup;
+ }
+
+ // public void execute() {
+ //
+ // this.getTestCaseInput().execute(this.getName());
+ // }
+ //
+ // // FIXME: Maybe not a good idea to move this here
+ // public void executeAndVerify() {
+ // TestCaseInput testCaseInput = getTestCaseInput();
+ //
+ // if (testCaseInput instanceof HttpTestCaseInput) {
+ // HttpTestCaseInput httpTestCaseInput = (HttpTestCaseInput) testCaseInput;
+ // HttpUriRequest attackRequest = httpTestCaseInput.buildAttackRequest();
+ // HttpUriRequest safeRequest = httpTestCaseInput.buildSafeRequest();
+ //
+ // // Send the next test case request with its attack payload
+ // ResponseInfo attackPayloadResponseInfo = sendRequest(httpclient, attackRequest);
+ // responseInfoList.add(attackPayloadResponseInfo);
+ //
+ // // Log the response
+ // log(attackPayloadResponseInfo);
+ //
+ // ResponseInfo safePayloadResponseInfo = null;
+ // if (!isUnverifiable()) {
+ // // Send the next test case request with its safe payload
+ // safePayloadResponseInfo = sendRequest(httpclient, safeRequest);
+ // responseInfoList.add(safePayloadResponseInfo);
+ //
+ // // Log the response
+ // log(safePayloadResponseInfo);
+ // }
+ //
+ // TestCaseVerificationResults result =
+ // new TestCaseVerificationResults(
+ // attackRequest,
+ // safeRequest,
+ // this,
+ // attackPayloadResponseInfo,
+ // safePayloadResponseInfo);
+ // results.add(result);
+ // }
+ //
+ // // Verify the response
+ // if (RegressionTesting.isTestingEnabled) {
+ // handleResponse(result);
+ // }
+ // }
+
+ @XmlElement(name = "VerificationResult", required = true)
+ public String getVerificationResult() {
+ return this.verificationResult;
+ }
+
+ @Override
+ public String toString() {
+ return "TestCase ["
+ + "category="
+ + category
+ + ", dataflowFile="
+ + dataflowFile
+ + ", name="
+ + name
+ + ", notAutoverifiableReason="
+ + notAutoverifiableReason
+ + ", sinkFile="
+ + sinkFile
+ + ", sourceFile="
+ + sourceFile
+ + ", sourceUIType="
+ + sourceUIType
+ + ", templateFile="
+ + templateFile
+ + ", UITemplateFile="
+ + UITemplateFile
+ + ", isVulnerability="
+ + isVulnerability
+ + ", "
+ + testCaseInput
+ + "]";
+ }
+
+ public static Comparator getNameComparator() {
+ return new Comparator() {
+
+ @Override
+ public int compare(TestCase o1, TestCase o2) {
+ if (!o1.name.equalsIgnoreCase(o2.name)) return o1.name.compareTo(o2.name);
+ return 0;
+ }
+ };
+ }
+}
diff --git a/library/src/main/java/org/owasp/benchmarkutils/entities/TestCaseInput.java b/library/src/main/java/org/owasp/benchmarkutils/entities/TestCaseInput.java
new file mode 100644
index 00000000..d8200166
--- /dev/null
+++ b/library/src/main/java/org/owasp/benchmarkutils/entities/TestCaseInput.java
@@ -0,0 +1,77 @@
+/**
+ * OWASP Benchmark Project
+ *
+ *
This file is part of the Open Web Application Security Project (OWASP) Benchmark Project For
+ * details, please see https://owasp.org/www-project-benchmark/.
+ *
+ *
The OWASP Benchmark is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, version 2.
+ *
+ *
The OWASP Benchmark is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * @author David Anderson
+ * @created 2024
+ */
+package org.owasp.benchmarkutils.entities;
+
+import javax.xml.bind.annotation.XmlSeeAlso;
+import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorNode;
+
+@XmlSeeAlso({
+ CliArgExecutableTestCaseInput.class,
+ CliFileExecutableTestCaseInput.class,
+ ExecutableTestCaseInput.class,
+ HttpTestCaseInput.class,
+ JerseyTestCaseInput.class,
+ ServletTestCaseInput.class,
+ SpringTestCaseInput.class,
+ StdinExecutableTestCaseInput.class,
+ TcpSocketExecutableTestCaseInput.class
+})
+@XmlDiscriminatorNode("@type")
+public abstract class TestCaseInput {
+
+ public enum TestCaseInputType {
+ HttpGet,
+ HttpPost,
+ CliArg
+ }
+
+ private String testCaseName;
+
+ // private TestCaseInputType type;
+
+ abstract void setSafe(boolean isSafe);
+
+ public String getTestCaseName() {
+ return testCaseName;
+ }
+
+ public void setTestCaseName(String testCaseName) {
+ this.testCaseName = testCaseName;
+ }
+
+ // @XmlAttribute(name = "inputType", required = true)
+ // @XmlReadOnly
+ // @NotNull
+ // public TestCaseInputType getType() {
+ // return type;
+ // }
+ //
+ // public void setType(TestCaseInputType type) {
+ // this.type = type;
+ // }
+
+ // abstract HttpUriRequestBase buildAttackRequest();
+ //
+ // abstract HttpUriRequestBase buildSafeRequest();
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName();
+ // return this.getClass().getSimpleName() + " [" + "type=" + type + "]";
+ }
+}
diff --git a/library/src/main/java/org/owasp/benchmarkutils/entities/TestCaseResult.java b/library/src/main/java/org/owasp/benchmarkutils/entities/TestCaseResult.java
new file mode 100644
index 00000000..0f0bffaf
--- /dev/null
+++ b/library/src/main/java/org/owasp/benchmarkutils/entities/TestCaseResult.java
@@ -0,0 +1,49 @@
+/**
+ * OWASP Benchmark Project
+ *
+ *
This file is part of the Open Web Application Security Project (OWASP) Benchmark Project For
+ * details, please see https://owasp.org/www-project-benchmark/.
+ *
+ *
The OWASP Benchmark is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, version 2.
+ *
+ *
The OWASP Benchmark is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * @author David Anderson
+ * @created 2024
+ */
+package org.owasp.benchmarkutils.entities;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement(name = "testcaseresult")
+public class TestCaseResult {
+
+ private String output;
+
+ private boolean isPassed;
+
+ public TestCaseResult(boolean isPassed, String output) {
+ this.isPassed = isPassed;
+ this.output = output;
+ }
+
+ public String getOutput() {
+ return output;
+ }
+
+ public void setOutput(String output) {
+ this.output = output;
+ }
+
+ public boolean isPassed() {
+ return isPassed;
+ }
+
+ public void setPassed(boolean isPassed) {
+ this.isPassed = isPassed;
+ }
+}
diff --git a/library/src/main/java/org/owasp/benchmarkutils/entities/TestCaseSetup.java b/library/src/main/java/org/owasp/benchmarkutils/entities/TestCaseSetup.java
new file mode 100644
index 00000000..59aa0377
--- /dev/null
+++ b/library/src/main/java/org/owasp/benchmarkutils/entities/TestCaseSetup.java
@@ -0,0 +1,28 @@
+/**
+ * OWASP Benchmark Project
+ *
+ *
This file is part of the Open Web Application Security Project (OWASP) Benchmark Project For
+ * details, please see https://owasp.org/www-project-benchmark/.
+ *
+ *
The OWASP Benchmark is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, version 2.
+ *
+ *
The OWASP Benchmark is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * @author David Anderson
+ * @created 2024
+ */
+package org.owasp.benchmarkutils.entities;
+
+import javax.xml.bind.annotation.XmlSeeAlso;
+
+@XmlSeeAlso({Sqlite3Config.class, FileCopyConfig.class, HttpClientConfig.class})
+public abstract class TestCaseSetup {
+
+ public abstract void setup() throws TestCaseSetupException;
+
+ public abstract void close() throws TestCaseSetupException;
+}
diff --git a/library/src/main/java/org/owasp/benchmarkutils/entities/TestCaseSetupException.java b/library/src/main/java/org/owasp/benchmarkutils/entities/TestCaseSetupException.java
new file mode 100644
index 00000000..b99ae3f3
--- /dev/null
+++ b/library/src/main/java/org/owasp/benchmarkutils/entities/TestCaseSetupException.java
@@ -0,0 +1,25 @@
+/**
+ * OWASP Benchmark Project
+ *
+ *
This file is part of the Open Web Application Security Project (OWASP) Benchmark Project For
+ * details, please see https://owasp.org/www-project-benchmark/.
+ *
+ *
The OWASP Benchmark is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, version 2.
+ *
+ *
The OWASP Benchmark is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * @author David Anderson
+ * @created 2024
+ */
+package org.owasp.benchmarkutils.entities;
+
+public class TestCaseSetupException extends Exception {
+
+ public TestCaseSetupException(String message, Exception e) {
+ super(message, e);
+ }
+}
diff --git a/library/src/main/java/org/owasp/benchmarkutils/entities/TestSuite.java b/library/src/main/java/org/owasp/benchmarkutils/entities/TestSuite.java
new file mode 100644
index 00000000..233d5daf
--- /dev/null
+++ b/library/src/main/java/org/owasp/benchmarkutils/entities/TestSuite.java
@@ -0,0 +1,97 @@
+/**
+ * OWASP Benchmark Project
+ *
+ *
This file is part of the Open Web Application Security Project (OWASP) Benchmark Project For
+ * details, please see https:/owasp.org/www-project-benchmark/.
+ *
+ *
The OWASP Benchmark is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, version 2.
+ *
+ *
The OWASP Benchmark is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * @author David Anderson
+ * @created 2021
+ */
+package org.owasp.benchmarkutils.entities;
+
+import java.util.List;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+
+@XmlRootElement(name = "TestSuite")
+public class TestSuite {
+ private List testCases;
+
+ private String name;
+
+ private String version;
+
+ CloseableHttpClient httpclient = null;
+
+ @XmlElement(name = "TestCase")
+ public List getTestCases() {
+ return testCases;
+ }
+
+ public void setTestCases(List testCases) {
+ this.testCases = testCases;
+ }
+
+ @XmlAttribute(required = true)
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @XmlAttribute(required = true)
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ // public void execute() {
+ //
+ // // TODO: Maybe create a TestCaseContext class to hold the httpclient, and setup tasks
+ // like
+ // // starting the DB server and app server. Pass this object as the argument to
+ // execute().
+ // // It is annoying that the httpclient field of the class will be null if there are no
+ // Http
+ // // test cases in the test suite. It would be better if we had a context class for
+ // each
+ // // TestCaseInput class.
+ // // The XML doc that defines the testcases could specify the config class by class
+ // name. The
+ // // testcase class would need to instantiate the named class accessible via a singleton
+ // map.
+ // // Then, each testcase can call a shared setup method and access shared state via its
+ // own
+ // // config field. I think this means that the DB server would be started in the middle
+ // of
+ // // parsing the XML doc, though.
+ //
+ // TestCaseContext context = TestCase.getContext(testCase);
+ // testCase.execute(context);
+ //
+ // // Execute all of the test cases
+ // for (TestCase testCase : this.getTestCases()) {
+ // testCase.execute();
+ // }
+ // }
+
+ @Override
+ public String toString() {
+ return "TestSuite [testCases=" + testCases + "]";
+ }
+}
diff --git a/library/src/main/java/org/owasp/benchmarkutils/entities/VerifyFixOutput.java b/library/src/main/java/org/owasp/benchmarkutils/entities/VerifyFixOutput.java
new file mode 100644
index 00000000..c23df234
--- /dev/null
+++ b/library/src/main/java/org/owasp/benchmarkutils/entities/VerifyFixOutput.java
@@ -0,0 +1,96 @@
+/**
+ * OWASP Benchmark Project
+ *
+ *
This file is part of the Open Web Application Security Project (OWASP) Benchmark Project For
+ * details, please see https://owasp.org/www-project-benchmark/.
+ *
+ *
The OWASP Benchmark is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, version 2.
+ *
+ *
The OWASP Benchmark is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * @author David Anderson
+ * @created 2024
+ */
+package org.owasp.benchmarkutils.entities;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement
+public class VerifyFixOutput {
+ private String testCaseName;
+ private ResponseInfo unfixedSafeResponseInfo;
+ private ResponseInfo unfixedAttackResponseInfo;
+ private ResponseInfo fixedSafeResponseInfo;
+ private ResponseInfo fixedAttackResponseInfo;
+ private boolean wasNotVerifiable;
+ private boolean wasExploited;
+ private boolean wasBroken;
+
+ public String getTestCaseName() {
+ return testCaseName;
+ }
+
+ public void setTestCaseName(String testCaseName) {
+ this.testCaseName = testCaseName;
+ }
+
+ public ResponseInfo getUnfixedSafeResponseInfo() {
+ return unfixedSafeResponseInfo;
+ }
+
+ public void setUnfixedSafeResponseInfo(ResponseInfo unfixedSafeResponseInfo) {
+ this.unfixedSafeResponseInfo = unfixedSafeResponseInfo;
+ }
+
+ public ResponseInfo getUnfixedAttackResponseInfo() {
+ return unfixedAttackResponseInfo;
+ }
+
+ public void setUnfixedAttackResponseInfo(ResponseInfo unfixedAttackResponseInfo) {
+ this.unfixedAttackResponseInfo = unfixedAttackResponseInfo;
+ }
+
+ public ResponseInfo getFixedSafeResponseInfo() {
+ return fixedSafeResponseInfo;
+ }
+
+ public void setFixedSafeResponseInfo(ResponseInfo fixedSafeResponseInfo) {
+ this.fixedSafeResponseInfo = fixedSafeResponseInfo;
+ }
+
+ public ResponseInfo getFixedAttackResponseInfo() {
+ return fixedAttackResponseInfo;
+ }
+
+ public void setFixedAttackResponseInfo(ResponseInfo fixedAttackResponseInfo) {
+ this.fixedAttackResponseInfo = fixedAttackResponseInfo;
+ }
+
+ public boolean isWasNotVerifiable() {
+ return wasNotVerifiable;
+ }
+
+ public void setWasNotVerifiable(boolean wasNotVerifiable) {
+ this.wasNotVerifiable = wasNotVerifiable;
+ }
+
+ public boolean isWasExploited() {
+ return wasExploited;
+ }
+
+ public void setWasExploited(boolean wasExploited) {
+ this.wasExploited = wasExploited;
+ }
+
+ public boolean isWasBroken() {
+ return wasBroken;
+ }
+
+ public void setWasBroken(boolean wasBroken) {
+ this.wasBroken = wasBroken;
+ }
+}
diff --git a/library/src/main/java/org/owasp/benchmarkutils/entities/VerifyFixesOutput.java b/library/src/main/java/org/owasp/benchmarkutils/entities/VerifyFixesOutput.java
new file mode 100644
index 00000000..6671ca72
--- /dev/null
+++ b/library/src/main/java/org/owasp/benchmarkutils/entities/VerifyFixesOutput.java
@@ -0,0 +1,35 @@
+/**
+ * OWASP Benchmark Project
+ *
+ *
This file is part of the Open Web Application Security Project (OWASP) Benchmark Project For
+ * details, please see https://owasp.org/www-project-benchmark/.
+ *
+ *
The OWASP Benchmark is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, version 2.
+ *
+ *
The OWASP Benchmark is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * @author David Anderson
+ * @created 2024
+ */
+package org.owasp.benchmarkutils.entities;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement
+public class VerifyFixesOutput {
+ private List list = new ArrayList<>();
+
+ public List getList() {
+ return this.list;
+ }
+
+ public void setList(List list) {
+ this.list = list;
+ }
+}
diff --git a/library/src/main/java/org/owasp/benchmarkutils/entities/jaxb.properties b/library/src/main/java/org/owasp/benchmarkutils/entities/jaxb.properties
new file mode 100644
index 00000000..b2979f90
--- /dev/null
+++ b/library/src/main/java/org/owasp/benchmarkutils/entities/jaxb.properties
@@ -0,0 +1 @@
+javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
diff --git a/library/src/main/java/org/owasp/benchmarkutils/helpers/Category.java b/library/src/main/java/org/owasp/benchmarkutils/helpers/Category.java
index c7b43204..21c5aa41 100644
--- a/library/src/main/java/org/owasp/benchmarkutils/helpers/Category.java
+++ b/library/src/main/java/org/owasp/benchmarkutils/helpers/Category.java
@@ -29,6 +29,11 @@ public class Category implements Comparable {
private final boolean isInjection;
private final String shortName; // PATH
+ /** Unused but necessary to make JAXB happy. */
+ public Category() {
+ this(null, null, -1, false, null);
+ }
+
/**
* Create a vuln category.
*
diff --git a/plugin/src/main/java/org/owasp/benchmarkutils/helpers/CategoryAdapter.java b/library/src/main/java/org/owasp/benchmarkutils/helpers/CategoryAdapter.java
similarity index 100%
rename from plugin/src/main/java/org/owasp/benchmarkutils/helpers/CategoryAdapter.java
rename to library/src/main/java/org/owasp/benchmarkutils/helpers/CategoryAdapter.java
diff --git a/plugin/pom.xml b/plugin/pom.xml
index 78b8e0fc..e5f4ebe1 100644
--- a/plugin/pom.xml
+++ b/plugin/pom.xml
@@ -20,6 +20,12 @@
1.3
+
+ com.contrastsecurity
+ java-sarif
+ 2.0
+
+
com.fasterxml.jackson.corejackson-annotations
@@ -50,6 +56,14 @@
33.3.1-jre
+
+
javax.xml.bindjaxb-api
@@ -112,17 +126,9 @@
provided
-
- org.eclipse.persistence
- org.eclipse.persistence.core
- ${version.eclipse.persistence}
-
-
-
org.eclipse.persistenceorg.eclipse.persistence.moxy
- ${version.eclipse.persistence}
@@ -143,27 +149,22 @@
2.3
-
-
-
- javax.validation
- validation-api
- 2.0.1.Final
-
-
xml-apisxml-apis
-
- 1.4.01
+
+
+ javax.json
+ javax.json-api
+ 1.1.4
+
+
+ org.glassfish
+ javax.json
+ 1.1.4
+
org.junit.jupiterjunit-jupiter-api
diff --git a/plugin/src/main/java/org/owasp/benchmarkutils/helpers/TestCase.java b/plugin/src/main/java/org/owasp/benchmarkutils/helpers/TestCase.java
deleted file mode 100644
index c4e98a8c..00000000
--- a/plugin/src/main/java/org/owasp/benchmarkutils/helpers/TestCase.java
+++ /dev/null
@@ -1,201 +0,0 @@
-/**
- * OWASP Benchmark Project
- *
- *
This file is part of the Open Web Application Security Project (OWASP) Benchmark Project For
- * details, please see https:/owasp.org/www-project-benchmark/.
- *
- *
The OWASP Benchmark is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Foundation, version 2.
- *
- *
This file is part of the Open Web Application Security Project (OWASP) Benchmark Project For
- * details, please see https:/owasp.org/www-project-benchmark/.
- *
- *
The OWASP Benchmark is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Foundation, version 2.
- *
- *
The OWASP Benchmark is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * @author David Anderson
- * @created 2021
- */
-package org.owasp.benchmarkutils.helpers;
-
-import java.util.List;
-import javax.xml.bind.annotation.XmlAttribute;
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlRootElement;
-import org.owasp.benchmarkutils.tools.AbstractTestCaseRequest;
-
-@XmlRootElement(name = "benchmarkSuite")
-public class TestSuite {
- private List testCases;
-
- private String name; // Name of the test suite, e.g., benchmark (Which is BenchmarkJava)
-
- private String version;
-
- @XmlElement(name = "benchmarkTest")
- public List getTestCases() {
- return testCases;
- }
-
- public void setTestCases(List testCases) {
- this.testCases = testCases;
- }
-
- @XmlAttribute(name = "testsuite", required = true)
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- @XmlAttribute(name = "version", required = true)
- public String getVersion() {
- return version;
- }
-
- public void setVersion(String version) {
- this.version = version;
- }
-
- /**
- * Dump out some basic details from the Crawler file to the command line to verify it was read
- * in properly. Used for debugging.
- */
- public void dumpBasicDetails() {
- System.out.println("Test suite name and version: " + name + " v" + version);
- System.out.println("Total test cases: " + this.getTestCases().size());
- }
-
- @Override
- public String toString() {
- return "TestSuite [testCases=" + testCases + "]";
- }
-}
diff --git a/plugin/src/main/java/org/owasp/benchmarkutils/helpers/Utils.java b/plugin/src/main/java/org/owasp/benchmarkutils/helpers/Utils.java
index 9c15a056..ccb1b0db 100644
--- a/plugin/src/main/java/org/owasp/benchmarkutils/helpers/Utils.java
+++ b/plugin/src/main/java/org/owasp/benchmarkutils/helpers/Utils.java
@@ -25,6 +25,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.io.StringWriter;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
@@ -44,12 +45,29 @@
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
+import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.transform.Source;
+import javax.xml.transform.sax.SAXSource;
import org.apache.commons.io.FileUtils;
import org.eclipse.persistence.jaxb.JAXBContextFactory;
+import org.eclipse.persistence.jaxb.MarshallerProperties;
+import org.eclipse.persistence.jaxb.UnmarshallerProperties;
+import org.eclipse.persistence.oxm.MediaType;
+import org.owasp.benchmarkutils.entities.ResponseInfo;
+import org.owasp.benchmarkutils.entities.TestSuite;
+import org.owasp.benchmarkutils.entities.VerifyFixOutput;
+import org.owasp.benchmarkutils.entities.VerifyFixesOutput;
+import org.owasp.benchmarkutils.tools.TestCaseRequestFileParseException;
+import org.owasp.benchmarkutils.tools.TestCaseVerificationResults;
+import org.owasp.benchmarkutils.tools.TestCaseVerificationResultsCollection;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
public class Utils {
@@ -59,7 +77,9 @@ public class Utils {
public static final String DATA_DIR = USERDIR + "data" + File.separator;
- public static final DocumentBuilderFactory safeDocBuilderFactory =
+ public static final String CRAWLER_CONFIG_FILE = "benchmark-attack-http.xml";
+
+ private static final DocumentBuilderFactory safeDocBuilderFactory =
DocumentBuilderFactory.newInstance();
static {
@@ -74,6 +94,10 @@ public class Utils {
}
}
+ public static DocumentBuilderFactory getSafeDocBuilderFactory() {
+ return safeDocBuilderFactory;
+ }
+
/**
* Find the specified file on the class path and return a File handle to it. Note: If the
* specified file is inside of a JAR on the classpath, this method will throw an error or return
@@ -95,7 +119,10 @@ public static File getFileFromClasspath(String filePath, ClassLoader classLoader
System.out.printf(
"getFileFromClasspath() url.toURI() is: %s and external form is: %s%n",
resourceURI, externalFormURI);
-
+ // String filePath = resourceURI.getPath();
+ // System.out.println("getFileFromClasspath() url.toURI().getPath()
+ // is: " + filePath);
+ // if (resourceURI != null) return new File(resourceURI);
if (externalFormURI != null) return new File(externalFormURI);
else {
System.out.printf(
@@ -174,16 +201,31 @@ public static List getLinesFromStream(InputStream fileStream, String sou
* @return A list of requests
* @throws JAXBException
* @throws FileNotFoundException
+ * @throws ParserConfigurationException
+ * @throws SAXException
+ * @throws TestCaseRequestFileParseException
*/
- public static TestSuite parseHttpFile(File file) throws JAXBException, FileNotFoundException {
-
- TestSuite testSuite = null;
+ public static TestSuite parseHttpFile(File file)
+ throws JAXBException, FileNotFoundException, SAXException,
+ ParserConfigurationException {
+
+ // Disable XXE
+ SAXParserFactory spf = SAXParserFactory.newInstance();
+ spf.setFeature("http://xml.org/sax/features/external-general-entities", false);
+ spf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
+ spf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
+
+ // Do unmarshall operation
+ Source xmlSource =
+ new SAXSource(
+ spf.newSAXParser().getXMLReader(), new InputSource(new FileReader(file)));
JAXBContext context = JAXBContextFactory.createContext(new Class[] {TestSuite.class}, null);
Unmarshaller unmarshaller = context.createUnmarshaller();
unmarshaller.setEventHandler(new javax.xml.bind.helpers.DefaultValidationEventHandler());
- testSuite = (TestSuite) unmarshaller.unmarshal(new FileReader(file));
+ JAXBElement testSuite =
+ (JAXBElement) unmarshaller.unmarshal(xmlSource);
- return testSuite;
+ return testSuite.getValue();
}
/**
@@ -369,4 +411,58 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
e.printStackTrace();
}
}
+
+ public static String objectToJson(Object object) throws JAXBException {
+ final Class[] marshallableClasses =
+ new Class[] {
+ ResponseInfo.class,
+ TestSuite.class,
+ TestCaseVerificationResults.class,
+ TestCaseVerificationResultsCollection.class,
+ VerifyFixOutput.class,
+ VerifyFixesOutput.class
+ };
+ JAXBContext jaxbContext =
+ org.eclipse.persistence.jaxb.JAXBContextFactory.createContext(
+ marshallableClasses, null);
+ Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
+
+ jaxbMarshaller.setProperty(MarshallerProperties.MEDIA_TYPE, MediaType.APPLICATION_JSON);
+ jaxbMarshaller.setProperty(MarshallerProperties.JSON_INCLUDE_ROOT, Boolean.TRUE);
+ jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
+
+ StringWriter writer = new StringWriter();
+ jaxbMarshaller.marshal(object, writer);
+
+ return writer.toString();
+ }
+
+ public static TestCaseVerificationResultsCollection jsonToTestCaseVerificationResultsList(
+ File file)
+ throws JAXBException, FileNotFoundException, SAXException,
+ ParserConfigurationException {
+
+ // Disable XXE
+ SAXParserFactory spf = SAXParserFactory.newInstance();
+ spf.setFeature("http://xml.org/sax/features/external-general-entities", false);
+ spf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
+ spf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
+
+ // Do unmarshall operation
+ // Source jsonSource =
+ // new SAXSource(
+ // spf.newSAXParser().getXMLReader(), new InputSource(new
+ // FileReader(file)));
+ JAXBContext context =
+ JAXBContextFactory.createContext(
+ new Class[] {TestCaseVerificationResultsCollection.class}, null);
+ Unmarshaller unmarshaller = context.createUnmarshaller();
+ unmarshaller.setProperty(UnmarshallerProperties.MEDIA_TYPE, MediaType.APPLICATION_JSON);
+ unmarshaller.setProperty(UnmarshallerProperties.JSON_INCLUDE_ROOT, Boolean.TRUE);
+ unmarshaller.setEventHandler(new javax.xml.bind.helpers.DefaultValidationEventHandler());
+ TestCaseVerificationResultsCollection resultsList =
+ (TestCaseVerificationResultsCollection) unmarshaller.unmarshal(file);
+
+ return resultsList;
+ }
}
diff --git a/plugin/src/main/java/org/owasp/benchmarkutils/score/TestCaseResult.java b/plugin/src/main/java/org/owasp/benchmarkutils/score/TestCaseResult.java
index 0244c0d9..a684a84e 100644
--- a/plugin/src/main/java/org/owasp/benchmarkutils/score/TestCaseResult.java
+++ b/plugin/src/main/java/org/owasp/benchmarkutils/score/TestCaseResult.java
@@ -17,7 +17,7 @@
*/
package org.owasp.benchmarkutils.score;
-import org.owasp.benchmarkutils.tools.AbstractTestCaseRequest;
+import org.owasp.benchmarkutils.entities.TestCase;
/* This class represents a single test case result. It documents the expected result (real),
* and the actual result (result).
@@ -49,7 +49,7 @@ public TestCaseResult() {
*
* @param request The request object used to access this test case.
*/
- public TestCaseResult(AbstractTestCaseRequest request) {
+ public TestCaseResult(TestCase request) {
this.testCaseName = request.getName();
this.number = request.getNumber();
this.truePositive = request.isVulnerability();
diff --git a/plugin/src/main/java/org/owasp/benchmarkutils/score/parsers/sarif/DatadogSastReader.java b/plugin/src/main/java/org/owasp/benchmarkutils/score/parsers/sarif/DatadogSastReader.java
index 3f4727d8..23e1d7f4 100644
--- a/plugin/src/main/java/org/owasp/benchmarkutils/score/parsers/sarif/DatadogSastReader.java
+++ b/plugin/src/main/java/org/owasp/benchmarkutils/score/parsers/sarif/DatadogSastReader.java
@@ -20,9 +20,9 @@
import org.owasp.benchmarkutils.score.ResultFile;
/**
- * This reader is made for the datadog-static-analyzer available on
- * ....
- * It uses the SARIF file produces by the tool.
+ * This reader is made for the datadog-static-analyzer available on .... It uses the SARIF file
+ * produces by the tool.
*/
public class DatadogSastReader extends SarifReader {
diff --git a/plugin/src/main/java/org/owasp/benchmarkutils/tools/AbstractTestCaseRequest.java b/plugin/src/main/java/org/owasp/benchmarkutils/tools/AbstractTestCaseRequest.java
deleted file mode 100644
index 7d368d40..00000000
--- a/plugin/src/main/java/org/owasp/benchmarkutils/tools/AbstractTestCaseRequest.java
+++ /dev/null
@@ -1,434 +0,0 @@
-/**
- * OWASP Benchmark Project
- *
- *
This file is part of the Open Web Application Security Project (OWASP) Benchmark Project For
- * details, please see https://owasp.org/www-project-benchmark/.
- *
- *
The OWASP Benchmark is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Foundation, version 2.
- *
- *
The OWASP Benchmark is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details
- *
- * @author Juan Gama
- * @created 2017
- */
-package org.owasp.benchmarkutils.tools;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import javax.validation.constraints.NotNull;
-import javax.xml.bind.annotation.XmlAttribute;
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlSeeAlso;
-import javax.xml.bind.annotation.XmlTransient;
-import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
-import org.apache.hc.client5.http.classic.methods.HttpUriRequest;
-import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase;
-import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorNode;
-import org.owasp.benchmarkutils.helpers.Category;
-import org.owasp.benchmarkutils.helpers.CategoryAdapter;
-import org.owasp.benchmarkutils.helpers.RequestVariable;
-
-@XmlSeeAlso({
- ServletTestCaseRequest.class,
- JerseyTestCaseRequest.class,
- SpringTestCaseRequest.class
-})
-@XmlDiscriminatorNode("@tcType")
-public abstract class AbstractTestCaseRequest {
-
- /*
- * The 1st three are Java.
- */
- public enum TestCaseType {
- JERSEYWS,
- SERVLET,
- SPRINGWS,
- NODEEXPRESS
- }
-
- public static Comparator getNameComparator() {
- return new Comparator() {
-
- @Override
- public int compare(AbstractTestCaseRequest o1, AbstractTestCaseRequest o2) {
- if (!o1.name.equalsIgnoreCase(o2.name)) return o1.name.compareTo(o2.name);
- return 0;
- }
- };
- }
-
- private Category category;
- private List cookies = new ArrayList();
- private String dataflowFile;
- private List formParams = new ArrayList();
- private String fullURL;
- private List getParams = new ArrayList();
- private List headers = new ArrayList();
- private String notAutoverifiableReason;
- private boolean isUnverifiable;
- private boolean isVulnerability;
- private String attackSuccessString;
-
- // Occasionally, its useful to verify that a string is MISSING from the response to indicate an
- // attack was successful
- private boolean attackSuccessStringPresent = true; // The default
-
- private String name; // TestCase name
- private int number = -1; // TestCase number, auto extracted from the name when its set
- private String query;
- private String sinkFile;
- private String sourceFile;
- private String sourceUIType;
- private TestCaseType tcType;
- private String templateFile;
- private String uiTemplateFile;
-
- public AbstractTestCaseRequest() {}
-
- /** Defines what parameters in the body will be sent. */
- abstract void buildBodyParameters(HttpUriRequestBase request);
-
- /** Defines what cookies will be sent. */
- abstract void buildCookies(HttpUriRequestBase request);
-
- /** Defines what headers will be sent. */
- abstract void buildHeaders(HttpUriRequestBase request);
-
- /** Defines how to construct URL query string. */
- abstract void buildQueryString();
-
- /**
- * TODO: Make this class a POJO TestCase and pass it as an arg to another class TestCaseRequest
- * that can build an actual HttpUriRequest.
- *
- * @return
- */
- public HttpUriRequest buildRequest() {
- buildQueryString();
- HttpUriRequestBase request = createRequestInstance(fullURL + query);
- buildHeaders(request);
- buildCookies(request);
- buildBodyParameters(request);
- return request;
- }
-
- public HttpUriRequest buildAttackRequest() {
- setSafe(false);
- return buildRequest();
- }
-
- public HttpUriRequest buildSafeRequest() {
- setSafe(true);
- return buildRequest();
- }
-
- /**
- * Method to create a POST, GET, DELETE, HEAD, OPTIONS, TRACE request object.
- *
- * @return an instance of a subclass of HttpUriRequestBase
- */
- abstract HttpUriRequestBase createRequestInstance(String URL);
-
- @XmlAttribute(name = "tcAttackSuccess")
- public String getAttackSuccessString() {
- return this.attackSuccessString;
- }
-
- @XmlAttribute(name = "tcAttackSuccessPresent")
- public boolean getAttackSuccessStringPresent() {
- return this.attackSuccessStringPresent;
- }
-
- @XmlAttribute(name = "tcCategory", required = true)
- @XmlJavaTypeAdapter(CategoryAdapter.class)
- @NotNull
- public Category getCategory() {
- return this.category;
- }
-
- @XmlElement(name = "cookie")
- @NotNull
- public List getCookies() {
- return this.cookies;
- }
-
- @XmlAttribute(name = "tcDataflowFile", required = true)
- @NotNull
- public String getDataflowFile() {
- return this.dataflowFile;
- }
-
- @XmlElement(name = "formparam")
- @NotNull
- public List getFormParams() {
- return this.formParams;
- }
-
- @XmlAttribute(name = "URL", required = true)
- @NotNull
- public String getFullURL() {
- return this.fullURL;
- }
-
- @XmlElement(name = "getparam")
- @NotNull
- public List getGetParams() {
- return this.getParams;
- }
-
- @XmlElement(name = "header")
- @NotNull
- public List getHeaders() {
- return this.headers;
- }
-
- @XmlAttribute(name = "tcName", required = true)
- @NotNull
- public String getName() {
- return this.name;
- }
-
- // This value is extracted from the test case name when it is set via setName(). Not sure it is
- // set when autoloaded from XML file.
- public int getNumber() {
- return this.number;
- }
-
- @XmlTransient
- public String getQuery() {
- return this.query;
- }
-
- @XmlAttribute(name = "tcSinkFile", required = true)
- @NotNull
- public String getSinkFile() {
- return this.sinkFile;
- }
-
- @XmlAttribute(name = "tcSourceFile", required = true)
- @NotNull
- public String getSourceFile() {
- return this.sourceFile;
- }
-
- @XmlAttribute(name = "tcSourceUIType", required = true)
- @NotNull
- public String getSourceUIType() {
- return this.sourceUIType;
- }
-
- @XmlAttribute(name = "tcTemplateFile", required = true)
- @NotNull
- public String getTemplateFile() {
- return this.templateFile;
- }
-
- // @XmlAttribute(name = "tcType", required = true)
- // @XmlReadOnly
- // @NotNull
- public TestCaseType getType() {
- return this.tcType;
- }
-
- @XmlAttribute(name = "tcUITemplateFile", required = true)
- @NotNull
- public String getUiTemplateFile() {
- return this.uiTemplateFile;
- }
-
- public boolean isUnverifiable() {
- return getNotAutoverifiableReason() != null;
- }
-
- @XmlAttribute(name = "tcNotAutoverifiable")
- public String getNotAutoverifiableReason() {
- return this.notAutoverifiableReason;
- }
-
- @XmlAttribute(name = "tcVulnerable", required = true)
- public boolean isVulnerability() {
- return this.isVulnerability;
- }
-
- public boolean isSafe() {
-
- boolean isSafe = true;
- // Bitwise AND is done on all parameters isSafe() values. If ANY of them are unsafe, isSafe
- // set to False.
- for (RequestVariable header : getHeaders()) {
- isSafe &= header.isSafe();
- }
-
- for (RequestVariable cookie : getCookies()) {
- isSafe &= cookie.isSafe();
- }
-
- for (RequestVariable getParam : getGetParams()) {
- isSafe &= getParam.isSafe();
- }
-
- for (RequestVariable formParam : getFormParams()) {
- isSafe &= formParam.isSafe();
- }
-
- return isSafe;
- }
-
- public String setAttackSuccessString(String attackSuccessString) {
- return this.attackSuccessString = attackSuccessString;
- }
-
- public boolean setAttackSuccessStringPresent(boolean attackSuccessStringPresent) {
- return this.attackSuccessStringPresent = attackSuccessStringPresent;
- }
-
- public void setCategory(Category category) {
- this.category = category;
- }
-
- public void setCookies(List cookies) {
- this.cookies = cookies;
- }
-
- public void setDataflowFile(String dataflowFile) {
- this.dataflowFile = dataflowFile;
- }
-
- public void setFormParams(List formParams) {
- this.formParams = formParams;
- }
-
- public void setFullURL(String fullURL) {
- this.fullURL = fullURL;
- }
-
- public void setGetParams(List getParams) {
- this.getParams = getParams;
- }
-
- public void setHeaders(List headers) {
- this.headers = headers;
- }
-
- static final Pattern lastIntPattern = Pattern.compile("[^0-9]+([0-9]+)$");
-
- public void setName(String name) {
- this.name = name;
- // Auto extract the test case number from the name.
- Matcher matcher = lastIntPattern.matcher(name);
- if (matcher.find()) {
- String someNumberStr = matcher.group(1);
- this.number = Integer.parseInt(someNumberStr);
- } else {
- System.out.println(
- "Warning: TestCaseRequest.setName() invoked with test case name: "
- + name
- + " that doesn't end with a test case number.");
- }
- }
-
- public void setQuery(String query) {
- this.query = query;
- }
-
- public void setSinkFile(String sinkFile) {
- this.sinkFile = sinkFile;
- }
-
- public void setSourceFile(String sourceFile) {
- this.sourceFile = sourceFile;
- }
-
- public void setSourceUIType(String sourceUIType) {
- this.sourceUIType = sourceUIType;
- }
-
- public void setTemplateFile(String templateFile) {
- this.templateFile = templateFile;
- }
-
- public void setType(TestCaseType type) {
- this.tcType = type;
- }
-
- public void setUiTemplateFile(String uiTemplateFile) {
- this.uiTemplateFile = uiTemplateFile;
- }
-
- public void setNotAutoverifiableReason(String notAutoverifiableReason) {
- this.notAutoverifiableReason = notAutoverifiableReason;
- }
-
- public void setVulnerability(boolean isVulnerability) {
- this.isVulnerability = isVulnerability;
- }
-
- public void setSafe(boolean isSafe) {
- for (RequestVariable header : getHeaders()) {
- // setSafe() considers whether attack and safe values exist for this parameter before
- // setting isSafe true or false. So you don't have to check that here.
- header.setSafe(isSafe);
- }
- for (RequestVariable cookie : getCookies()) {
- cookie.setSafe(isSafe);
- }
- for (RequestVariable getParam : getGetParams()) {
- getParam.setSafe(isSafe);
- }
- for (RequestVariable formParam : getFormParams()) {
- formParam.setSafe(isSafe);
- }
- }
-
- @Override
- public String toString() {
- return this.getClass().getSimpleName()
- + " [category="
- + category
- + ", name="
- + name
- + ", uiTemplateFile="
- + new File(uiTemplateFile).getName()
- + ", templateFile="
- + new File(templateFile).getName()
- + ", sourceFile="
- + sourceFile
- + ", sourceUIType="
- + sourceUIType
- + ", dataflowFile="
- + dataflowFile
- + ", sinkFile="
- + sinkFile
- + ", fullURL="
- + fullURL
- + ", getParams="
- + getParams
- + ", headers="
- + headers
- + ", cookies="
- + cookies
- + ", formParams="
- + formParams
- + ", isUnverifiable="
- + isUnverifiable
- + ", isVulnerability="
- + isVulnerability
- + ", attackSuccessString="
- + attackSuccessString
- + ", isSafe="
- + isSafe()
- + ", query="
- + query
- + ", tcType="
- + tcType
- + "]";
- }
-}
diff --git a/plugin/src/main/java/org/owasp/benchmarkutils/tools/BenchmarkCrawler.java b/plugin/src/main/java/org/owasp/benchmarkutils/tools/BenchmarkCrawler.java
index bc768bd2..0be47e24 100644
--- a/plugin/src/main/java/org/owasp/benchmarkutils/tools/BenchmarkCrawler.java
+++ b/plugin/src/main/java/org/owasp/benchmarkutils/tools/BenchmarkCrawler.java
@@ -17,18 +17,24 @@
*/
package org.owasp.benchmarkutils.tools;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.net.URI;
import java.net.URISyntaxException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
-import java.util.concurrent.TimeUnit;
+import java.util.StringJoiner;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import org.apache.commons.cli.CommandLine;
@@ -41,7 +47,6 @@
import org.apache.commons.lang.time.StopWatch;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.classic.methods.HttpUriRequest;
-import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
@@ -49,9 +54,8 @@
import org.apache.hc.client5.http.io.HttpClientConnectionManager;
import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
-import org.apache.hc.client5.http.ssl.TrustSelfSignedStrategy;
+import org.apache.hc.client5.http.ssl.TrustAllStrategy;
import org.apache.hc.core5.http.HttpEntity;
-import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.ssl.SSLContextBuilder;
import org.apache.maven.plugin.AbstractMojo;
@@ -60,8 +64,16 @@
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
+import org.owasp.benchmarkutils.entities.CliRequest;
+import org.owasp.benchmarkutils.entities.CliResponseInfo;
+import org.owasp.benchmarkutils.entities.ExecutableTestCaseInput;
+import org.owasp.benchmarkutils.entities.HttpResponseInfo;
+import org.owasp.benchmarkutils.entities.HttpTestCaseInput;
+import org.owasp.benchmarkutils.entities.RequestVariable;
+import org.owasp.benchmarkutils.entities.ResponseInfo;
+import org.owasp.benchmarkutils.entities.TestCase;
+import org.owasp.benchmarkutils.entities.TestSuite;
import org.owasp.benchmarkutils.helpers.Categories;
-import org.owasp.benchmarkutils.helpers.TestSuite;
import org.owasp.benchmarkutils.helpers.Utils;
import org.owasp.benchmarkutils.score.BenchmarkScore;
@@ -71,20 +83,44 @@ public class BenchmarkCrawler extends AbstractMojo {
// Intended to be a Singleton. So when instantiated, put it here:
static BenchmarkCrawler thisInstance = null;
- static final long MAX_NETWORK_TIMEOUT = 15; // seconds
- public static String proxyHost, proxyPort;
+ @Parameter(property = "crawlerFile")
+ String pluginFilenameParam;
+
+ @Parameter(property = "testCaseName")
+ String pluginTestCaseNameParam;
/*
- * Attaching the @Parameter property to the crawlerFile variable allows you to set the value directly when invoking via maven.
- * For example: -DcrawlerFile=data/benchmark-crawler-http.xml
+ * Attaching the @Parameter property to the crawlerFile variable directly didn't work for some
+ * reason. So I attached it to a new String variable, and set it later. No clue why it doesn't
+ * work. But for now, leaving it this way because it works.
+ *
+ * If you run the mvn command with -X, when invoking this plugin, you'd see something like
+ * this at the end:
+ *
+ * [DEBUG] (s) crawlerFile = /Users/PATH/TO/BenchmarkJava/data/benchmark-crawler-http.xml
+ * [DEBUG] -- end configuration --
+ * but the crawlerFile variable would be null.
+ *
+ * When it should be:
+ * [DEBUG] (f) crawlerFile = data/benchmark-crawler-http.xml
+ * [DEBUG] -- end configuration --
+ *
+ * So after changing this, I now get:
+ * [DEBUG] (f) pluginFilenameParam = data/benchmark-crawler-http.xml
+ * [DEBUG] -- end configuration --
+ * and the pluginFilenameParam variable value is set properly.
*/
- @Parameter(property = "crawlerFile")
- String crawlerFile = null;
+ String crawlerFile;
File theCrawlerFile;
String selectedTestCaseName = null;
TestSuite testSuite;
+ BenchmarkCrawler() {
+ // A default constructor required to support Maven plugin API.
+ // The theCrawlerFile has to be instantiated before a crawl can be done.
+ }
+
/** Crawl the target test suite. */
protected void run() {
try {
@@ -103,15 +139,16 @@ void load() {
new Categories(categoriesFileStream);
this.testSuite = Utils.parseHttpFile(this.theCrawlerFile);
+ // System.out.println("Test suite: " + this.testSuite);
Collections.sort(
this.testSuite.getTestCases(),
- AbstractTestCaseRequest.getNameComparator()); // Probably not necessary
+ TestCase.getNameComparator()); // Probably not necessary
// This allows a single test case to be tested, rather than all of them.
if (selectedTestCaseName != null) {
- for (AbstractTestCaseRequest request : this.testSuite.getTestCases()) {
+ for (TestCase request : this.testSuite.getTestCases()) {
if (request.getName().equals(selectedTestCaseName)) {
- List requests = new ArrayList<>();
+ List requests = new ArrayList<>();
requests.add(request);
this.testSuite = new TestSuite();
this.testSuite.setTestCases(requests);
@@ -127,64 +164,79 @@ void load() {
}
}
- /**
- * Load the crawler file that defines all the test cases, including their endpoints, and how to
- * crawl them.
- *
- * @param targetFileName The crawler file name
- * @throws RuntimeException If the file doesn't exist or can't be opened for some reason.
- */
- public void setCrawlerFile(String targetFileName) throws RuntimeException {
- File targetFile = new File(targetFileName);
- if (targetFile.exists()) {
- this.crawlerFile = targetFileName;
- this.theCrawlerFile = targetFile;
- } else {
- throw new RuntimeException(
- "Could not find crawler configuration file: '" + targetFileName + "'");
- }
+ public void setCrawlerFile(File theCrawlerFile) {
+ this.theCrawlerFile = theCrawlerFile;
}
/**
* This method could be static, but needs to be an instance method so Verification crawler can
- * overload this method.
+ * override this method.
*
* @param testSuite The TestSuite to crawl.
- * @throws Exception If crawler configuration is messed up somehow.
+ * @throws Exception
*/
protected void crawl(TestSuite testSuite) throws Exception {
- CloseableHttpClient httpclient =
- createAcceptSelfSignedCertificateClient(
- MAX_NETWORK_TIMEOUT); // Max 15 seconds for timeouts
- long start = System.currentTimeMillis();
+ // Use try-with-resources to close this resource before returning
+ try (CloseableHttpClient httpClient = createAcceptSelfSignedCertificateClient()) {
+ long start = System.currentTimeMillis();
+
+ // Iterate through TestCase objects instead.
+ // Execution of the test case depends on the type of TestCase.getTestCaseInput()
+ // Where should the code that executes the test case go?
+ // Maybe I need a TestCaseExecuter that takes a TestCaseInput to initialize.
+ // for (TestCase testCase : testSuite.getTestCases()) {
+ // if (testCase.getTestCaseInput() instanceof HttpTestCaseInput) {
+ // HttpUriRequest request =
+ // testCase.getAttackTestCaseRequest().buildAttackRequest();
+ // sendRequest(httpclient, request);
+ // } else if (testCase.getTestCaseInput() instanceof ExecutableTestCaseInput) {
+ // // Execute the testCase using exec()
+ // }
+ // }
+
+ for (TestCase testCase : testSuite.getTestCases()) {
+ System.out.println("Executing test case: " + testCase.getName()); // DEBUG
+ if (testCase.getTestCaseInput() instanceof HttpTestCaseInput) {
+ HttpTestCaseInput httpTestCaseInput =
+ (HttpTestCaseInput) testCase.getTestCaseInput();
+
+ HttpUriRequest attackRequest = httpTestCaseInput.buildAttackRequest();
+
+ // Send the next test case request with its attack payload
+ sendRequest(httpClient, attackRequest, true);
+ } else if (testCase.getTestCaseInput() instanceof ExecutableTestCaseInput) {
+ ExecutableTestCaseInput executableTestCaseInput =
+ (ExecutableTestCaseInput) testCase.getTestCaseInput();
+
+ CliRequest attackRequest = executableTestCaseInput.buildAttackRequest();
+
+ // Send the next test case request with its attack payload
+ execute(attackRequest);
+ }
+ }
- for (AbstractTestCaseRequest requestTemplate : testSuite.getTestCases()) {
+ // Log the elapsed time for all test cases
+ long stop = System.currentTimeMillis();
+ int seconds = (int) (stop - start) / 1000;
- HttpUriRequest request = requestTemplate.buildSafeRequest();
+ Date now = new Date();
- // Send the next test case request
- sendRequest(httpclient, request);
+ System.out.printf(
+ "Crawl ran on %tF % (%d : %d sec)%n", statusCode, seconds);
+ System.out.printf("--> (%d : %d sec)%n", statusCode, seconds);
- try {
- if (entity != null) {
- responseInfo.setResponseString(EntityUtils.toString(entity));
- EntityUtils.consume(entity);
- } else
- // Can occur when there is a 204 No Content response
- responseInfo.setResponseString("");
- } catch (IOException | org.apache.hc.core5.http.ParseException e) {
- e.printStackTrace();
- }
- } else { // This can occur when the test case never responds, throwing an exception.
- responseInfo.setStatusCode(-1); // since no response at all
- System.out.printf("--> (%d : %d sec)%n", -1, seconds);
- responseInfo.setResponseString("NONE!");
+ try {
+ responseInfo.setResponseString(EntityUtils.toString(entity));
+ EntityUtils.consume(entity);
+ } catch (IOException | org.apache.hc.core5.http.ParseException e) {
+ e.printStackTrace();
}
} finally {
if (response != null)
@@ -296,6 +334,78 @@ static ResponseInfo sendRequest(CloseableHttpClient httpclient, HttpUriRequest r
return responseInfo;
}
+ /**
+ * Issue the requested request, measure the time required to execute, then output both to stdout
+ * and the global variable timeString the URL tested, the time required to execute and the
+ * response code. By default, this assumes a normal 'safe' request.
+ *
+ * @param request - The CLI request to issue
+ */
+ static ResponseInfo execute(CliRequest request) {
+ return execute(request, false);
+ }
+
+ /**
+ * Issue the requested request, measure the time required to execute, then output both to stdout
+ * and the global variable timeString the URL tested, the time required to execute and the
+ * response code.
+ *
+ * @param request - The CLI request to issue
+ * @param attackRequest - True if executing an attack, false otherwise
+ */
+ static ResponseInfo execute(CliRequest request, boolean attackRequest) {
+ CliResponseInfo responseInfo = new CliResponseInfo(attackRequest);
+ responseInfo.setRequest(request);
+ // responseInfo.setRequestBase(request);
+
+ ArrayList executeArgs =
+ new ArrayList<>(Arrays.asList(request.getCommand().split(" ")));
+ for (RequestVariable arg : request.getArgs()) {
+ // System.out.println("Adding arg: " + arg.getValue());
+ executeArgs.add(arg.getValue());
+ }
+ // System.out.println(String.join(" ", executeArgs));
+
+ StopWatch watch = new StopWatch();
+
+ watch.start();
+ try {
+ // response = httpclient.execute(request);
+ ProcessBuilder builder = new ProcessBuilder(executeArgs);
+ // FIXME: Do not hardcode this path
+ builder.directory(new File("../../julietpy/testcode"));
+ builder.redirectErrorStream(true);
+ Process process = builder.start();
+ try (BufferedReader reader =
+ new BufferedReader(new InputStreamReader(process.getInputStream()));
+ BufferedWriter writer =
+ new BufferedWriter(
+ new OutputStreamWriter(process.getOutputStream())); ) {
+ if (request.getStdinData() != null) {
+ writer.write(request.getStdinData().getValue());
+ writer.flush();
+ writer.close();
+ }
+
+ StringJoiner sj = new StringJoiner(System.getProperty("line.separator"));
+ reader.lines().iterator().forEachRemaining(sj::add);
+ String output = sj.toString();
+ responseInfo.setResponseString(output);
+ int exitValue = process.waitFor();
+ // attackPayloadResponseInfo = new ResponseInfo();
+ // System.out.printf("Program terminated with return code: %s%n",
+ // exitValue);
+ responseInfo.setStatusCode(exitValue);
+ }
+
+ } catch (IOException | InterruptedException e) {
+ e.printStackTrace();
+ }
+ watch.stop();
+
+ return responseInfo;
+ }
+
/**
* Process the command line arguments that make any configuration changes.
*
@@ -329,7 +439,14 @@ protected void processCommandLineArgs(String[] args) {
CommandLine line = parser.parse(options, args);
if (line.hasOption("f")) {
- setCrawlerFile(line.getOptionValue("f"));
+ this.crawlerFile = line.getOptionValue("f");
+ File targetFile = new File(this.crawlerFile);
+ if (targetFile.exists()) {
+ setCrawlerFile(targetFile);
+ } else {
+ throw new RuntimeException(
+ "Could not find crawler configuration file '" + this.crawlerFile + "'");
+ }
}
if (line.hasOption("h")) {
formatter.printHelp("BenchmarkCrawlerVerification", options, true);
@@ -343,21 +460,30 @@ protected void processCommandLineArgs(String[] args) {
}
}
+ /**
+ * The execute() method is invoked when this class is invoked as a maven plugin, rather than via
+ * the command line. So what we do here is set up the command line parameters and then invoke
+ * main() so this can be called both as a plugin, or via the command line.
+ */
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
- if (thisInstance == null) thisInstance = this;
-
- if (null == this.crawlerFile) {
+ if (null == this.pluginFilenameParam) {
System.out.println("ERROR: A crawlerFile parameter must be specified.");
} else {
- String[] mainArgs = {"-f", this.crawlerFile};
- main(mainArgs);
+ List mainArgs = new ArrayList<>();
+ mainArgs.add("-f");
+ mainArgs.add(this.pluginFilenameParam);
+ if (this.pluginTestCaseNameParam != null) {
+ mainArgs.add("-n");
+ mainArgs.add(this.pluginTestCaseNameParam);
+ }
+ main(mainArgs.stream().toArray(String[]::new));
}
}
public static void main(String[] args) {
// thisInstance can be set from execute() or here, depending on how this class is invoked
- // (via maven or commmand line)
+ // (via maven or command line)
if (thisInstance == null) {
thisInstance = new BenchmarkCrawler();
}
diff --git a/plugin/src/main/java/org/owasp/benchmarkutils/tools/BenchmarkCrawlerVerification.java b/plugin/src/main/java/org/owasp/benchmarkutils/tools/BenchmarkCrawlerVerification.java
index 70698b47..1ed37949 100644
--- a/plugin/src/main/java/org/owasp/benchmarkutils/tools/BenchmarkCrawlerVerification.java
+++ b/plugin/src/main/java/org/owasp/benchmarkutils/tools/BenchmarkCrawlerVerification.java
@@ -17,13 +17,21 @@
*/
package org.owasp.benchmarkutils.tools;
+import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
+import java.io.FileWriter;
import java.io.IOException;
-import java.net.URISyntaxException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Date;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import javax.xml.bind.JAXBException;
+import javax.xml.parsers.ParserConfigurationException;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
@@ -37,8 +45,21 @@
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
-import org.owasp.benchmarkutils.helpers.TestSuite;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.owasp.benchmarkutils.entities.CliRequest;
+import org.owasp.benchmarkutils.entities.CliResponseInfo;
+import org.owasp.benchmarkutils.entities.ExecutableTestCaseInput;
+import org.owasp.benchmarkutils.entities.HttpResponseInfo;
+import org.owasp.benchmarkutils.entities.HttpTestCaseInput;
+import org.owasp.benchmarkutils.entities.ResponseInfo;
+import org.owasp.benchmarkutils.entities.TestCase;
+import org.owasp.benchmarkutils.entities.TestCaseSetup;
+import org.owasp.benchmarkutils.entities.TestCaseSetupException;
+import org.owasp.benchmarkutils.entities.TestSuite;
+import org.owasp.benchmarkutils.entities.VerifyFixOutput;
+import org.owasp.benchmarkutils.entities.VerifyFixesOutput;
import org.owasp.benchmarkutils.helpers.Utils;
+import org.xml.sax.SAXException;
/**
* TODO: Refactor this class. There is way too much duplication of code in BenchmarkCrawler here.
@@ -51,225 +72,763 @@ public class BenchmarkCrawlerVerification extends BenchmarkCrawler {
private static int maxTimeInSeconds = 2;
private static boolean isTimingEnabled = false;
+ // private boolean verifyFixed = false; // DEBUG
+ private String configurationDirectory = Utils.DATA_DIR;
+ private String defaultOutputDirectory = configurationDirectory;
+ // private String defaultFixedOutputDirectory = Paths.get(Utils.DATA_DIR,
+ // "fixstatus").toString();
+ // private String defaultUnfixedSrcDirectory =
+ // new File(new File(Utils.DATA_DIR).getParent(), "before_data")
+ // .getAbsolutePath(); // DEBUG: Utils.DATA_DIR;
private static final String FILENAME_TIMES_ALL = "crawlerTimes.txt";
private static final String FILENAME_TIMES = "crawlerSlowTimes.txt";
private static final String FILENAME_NON_DISCRIMINATORY_LOG = "nonDiscriminatoryTestCases.txt";
private static final String FILENAME_ERRORS_LOG = "errorTestCases.txt";
private static final String FILENAME_UNVERIFIABLE_LOG = "unverifiableTestCases.txt";
- // The following is reconfigurable via parameters to main()
- private static String CRAWLER_DATA_DIR = Utils.DATA_DIR; // default data dir
+ // FIXME: This constant is also used by RegressionUtils and should not be duplicated.
+ private static final String FILENAME_TC_VERIF_RESULTS_JSON = "testCaseVerificationResults.json";
+ private static final String FILENAME_VERIFY_FIX_RESULT = "verifyFixedResult.json";
+ // The following is reconfigurable via parameters to main()
+ // private String CRAWLER_DATA_DIR = Utils.DATA_DIR; // default data dir
SimpleFileLogger tLogger;
SimpleFileLogger ndLogger;
SimpleFileLogger eLogger;
SimpleFileLogger uLogger;
- /**
- * Overload the base crawl() method to send both attack and safe requests, and verify whether
- * the test exploit worked or not based on the results that came back in both the attack
- * response and safe response and whether this test case is a true positive or not.
- *
- * @param testSuite The TestSuite to crawl.
- * @throws Exception If crawler configuration is messed up somehow.
- */
- @Override
- protected void crawl(TestSuite testSuite) throws Exception {
- CloseableHttpClient httpclient =
- createAcceptSelfSignedCertificateClient(MAX_NETWORK_TIMEOUT);
- long start = System.currentTimeMillis();
- List responseInfoList = new ArrayList();
- List results = new ArrayList();
-
- final File FILE_NON_DISCRIMINATORY_LOG =
- new File(CRAWLER_DATA_DIR, FILENAME_NON_DISCRIMINATORY_LOG);
- final File FILE_ERRORS_LOG = new File(CRAWLER_DATA_DIR, FILENAME_ERRORS_LOG);
- final File FILE_TIMES_LOG;
- if (isTimingEnabled) {
- FILE_TIMES_LOG = new File(CRAWLER_DATA_DIR, FILENAME_TIMES);
- } else {
- FILE_TIMES_LOG = new File(CRAWLER_DATA_DIR, FILENAME_TIMES_ALL);
- }
- final File FILE_UNVERIFIABLE_LOG = new File(CRAWLER_DATA_DIR, FILENAME_UNVERIFIABLE_LOG);
- SimpleFileLogger.setFile("TIMES", FILE_TIMES_LOG);
- SimpleFileLogger.setFile("NONDISCRIMINATORY", FILE_NON_DISCRIMINATORY_LOG);
- SimpleFileLogger.setFile("ERRORS", FILE_ERRORS_LOG);
- SimpleFileLogger.setFile("UNVERIFIABLE", FILE_UNVERIFIABLE_LOG);
+ @Parameter(property = "generateJSONResults", defaultValue = "false")
+ private String generateJSONResults;
- String completionMessage = null;
+ @Parameter(property = "verifyFixed", defaultValue = "false")
+ private String verifyFixed;
- try (SimpleFileLogger nl = SimpleFileLogger.getLogger("NONDISCRIMINATORY");
- SimpleFileLogger el = SimpleFileLogger.getLogger("ERRORS");
- SimpleFileLogger ul = SimpleFileLogger.getLogger("UNVERIFIABLE");
- SimpleFileLogger tl = SimpleFileLogger.getLogger("TIMES")) {
+ @Parameter(property = "unfixedSrcDirectory")
+ private String unfixedSourceDirectory;
- ndLogger = nl;
- eLogger = el;
- uLogger = ul;
- tLogger = tl;
+ @Parameter(property = "fixedSrcDirectory")
+ private String fixedSourceDirectory;
- for (AbstractTestCaseRequest requestTemplate : testSuite.getTestCases()) {
+ @Parameter(property = "outputDirectory")
+ private String outputDirectory;
- HttpUriRequest attackRequest = requestTemplate.buildAttackRequest();
- HttpUriRequest safeRequest = requestTemplate.buildSafeRequest();
+ @Parameter(property = "testCaseName")
+ private String selectedTestCaseName;
- // Send the next test case request with its attack payload
- ResponseInfo attackPayloadResponseInfo = sendRequest(httpclient, attackRequest);
- responseInfoList.add(attackPayloadResponseInfo);
+ private Map testCaseNameToTestCaseVerificationResultsMap =
+ new HashMap<>();
- // Log the response
- log(attackPayloadResponseInfo);
+ private List vulnerableTestcases = new ArrayList<>();
+ private List notVulnerableTestcases = new ArrayList<>();
+ private List notVerifiableModifiedVulnerableTestcases = new ArrayList<>();
+ private List exploitedModifiedVulnerableTestcases = new ArrayList<>();
+ private List brokenModifiedVulnerableTestcases = new ArrayList<>();
+ private List notVerifiableModifiedNotVulnerableTestcases = new ArrayList<>();
+ private List exploitedModifiedNotVulnerableTestcases = new ArrayList<>();
+ private List brokenModifiedNotVulnerableTestcases = new ArrayList<>();
- ResponseInfo safePayloadResponseInfo = null;
- if (!requestTemplate.isUnverifiable()) {
- // Send the next test case request with its safe payload
- safePayloadResponseInfo = sendRequest(httpclient, safeRequest);
- responseInfoList.add(safePayloadResponseInfo);
+ private List verifyFixesOutputList = new ArrayList<>();
- // Log the response
- log(safePayloadResponseInfo);
- }
+ BenchmarkCrawlerVerification() {
+ // A default constructor required to support Maven plugin API.
+ // The theCrawlerFile has to be instantiated before a crawl can be done.
+ }
- TestCaseVerificationResults result =
- new TestCaseVerificationResults(
- attackRequest,
- safeRequest,
- requestTemplate,
- attackPayloadResponseInfo,
- safePayloadResponseInfo);
- results.add(result);
+ @Override
+ protected void crawl(TestSuite testSuite) throws Exception {
- // Verify the response
- if (RegressionTesting.isTestingEnabled) {
- handleResponse(result);
- }
+ // FIXME: Initialize all setup resources that are required
+ // List setups = getTestCaseSetups(testSuite);
+ // initializeSetups(setups);
+ try (CloseableHttpClient httpClient = createAcceptSelfSignedCertificateClient()) {
+
+ long start = System.currentTimeMillis();
+ List responseInfoList = new ArrayList();
+ List results =
+ new ArrayList();
+
+ Files.createDirectories(Paths.get(getOutputDirectory()));
+ final File FILE_NON_DISCRIMINATORY_LOG =
+ new File(getOutputDirectory(), FILENAME_NON_DISCRIMINATORY_LOG);
+ final File FILE_ERRORS_LOG = new File(getOutputDirectory(), FILENAME_ERRORS_LOG);
+ final File FILE_TIMES_LOG;
+ if (isTimingEnabled) {
+ FILE_TIMES_LOG = new File(getOutputDirectory(), FILENAME_TIMES);
+ } else {
+ FILE_TIMES_LOG = new File(getOutputDirectory(), FILENAME_TIMES_ALL);
}
+ final File FILE_UNVERIFIABLE_LOG =
+ new File(getOutputDirectory(), FILENAME_UNVERIFIABLE_LOG);
+ SimpleFileLogger.setFile("TIMES", FILE_TIMES_LOG);
+ SimpleFileLogger.setFile("NONDISCRIMINATORY", FILE_NON_DISCRIMINATORY_LOG);
+ SimpleFileLogger.setFile("ERRORS", FILE_ERRORS_LOG);
+ SimpleFileLogger.setFile("UNVERIFIABLE", FILE_UNVERIFIABLE_LOG);
+
+ String completionMessage = null;
+
+ try (SimpleFileLogger nl = SimpleFileLogger.getLogger("NONDISCRIMINATORY");
+ SimpleFileLogger el = SimpleFileLogger.getLogger("ERRORS");
+ SimpleFileLogger ul = SimpleFileLogger.getLogger("UNVERIFIABLE");
+ SimpleFileLogger tl = SimpleFileLogger.getLogger("TIMES")) {
+
+ ndLogger = nl;
+ eLogger = el;
+ uLogger = ul;
+ tLogger = tl;
+
+ List filteredList;
+
+ if (Boolean.parseBoolean(verifyFixed)) {
+ filteredList =
+ testSuite.getTestCases().stream()
+ .filter(
+ testCase ->
+ !isTestCaseIdentical(
+ unfixedSourceDirectory,
+ fixedSourceDirectory,
+ testCase.getName()))
+ .collect(Collectors.toList());
+ } else {
+ filteredList = testSuite.getTestCases();
+ }
+ if (selectedTestCaseName != null) {
+ filteredList =
+ filteredList.stream()
+ .filter(
+ testCase ->
+ testCase.getName().equals(selectedTestCaseName))
+ .collect(Collectors.toList());
+ }
+ for (TestCase testCase : filteredList) {
+
+ // if (this.selectedTestCaseName != null) {
+ // if
+ // (!testCase.getName().equals(this.selectedTestCaseName)) {
+ // continue;
+ // }
+ // }
+
+ // TestCaseVerificationResults result = testCase.execute();
+ // results.add(result);
+
+ TestExecutor attackExecutor = null;
+ TestExecutor safeExecutor = null;
+ ResponseInfo attackPayloadResponseInfo = null;
+ ResponseInfo safePayloadResponseInfo = null;
+ if (testCase.getTestCaseInput() instanceof HttpTestCaseInput) {
+ HttpTestCaseInput httpTestCaseInput =
+ (HttpTestCaseInput) testCase.getTestCaseInput();
+
+ // TestExecutor attackTestExecutor =
+ // httpTestCase.getTestCaseInput().buildAttackExecutor();
+ // TestExecutor safeTestExecutor =
+ // httpTestCase.getTestCaseInput().buildSafeExecutor();
+
+ // FIXME: What would the executable testcase's attackRequest look like?
+ HttpUriRequest attackRequest = httpTestCaseInput.buildAttackRequest();
+ HttpUriRequest safeRequest = httpTestCaseInput.buildSafeRequest();
+ attackExecutor = new HttpExecutor(attackRequest);
+ safeExecutor = new HttpExecutor(safeRequest);
+
+ // Send the next test case request with its attack payload
+ attackPayloadResponseInfo = sendRequest(httpClient, attackRequest, true);
+ responseInfoList.add(attackPayloadResponseInfo);
+
+ // Log the response
+ log(attackPayloadResponseInfo);
+
+ safePayloadResponseInfo = null;
+ if (!testCase.isUnverifiable()) {
+ // Send the next test case request with its safe payload
+ safePayloadResponseInfo = sendRequest(httpClient, safeRequest);
+ responseInfoList.add(safePayloadResponseInfo);
+
+ // Log the response
+ log(safePayloadResponseInfo);
+ }
+
+ // TestCaseVerificationResults result =
+ // new TestCaseVerificationResults(
+ // attackExecutor,
+ // safeExecutor,
+ // httpTestCase,
+ // attackPayloadResponseInfo,
+ // safePayloadResponseInfo);
+ // results.add(result);
+ //
+ // // Verify the response
+ // if (RegressionTesting.isTestingEnabled) {
+ // handleResponse(result);
+ // }
+ } else if (testCase.getTestCaseInput() instanceof ExecutableTestCaseInput) {
+ ExecutableTestCaseInput executableTestCaseInput =
+ (ExecutableTestCaseInput) testCase.getTestCaseInput();
+
+ // FIXME: A bit of a hack
+ CliRequest attackRequest = executableTestCaseInput.buildAttackRequest();
+ CliRequest safeRequest = executableTestCaseInput.buildSafeRequest();
+ attackExecutor = new CliExecutor(attackRequest);
+ safeExecutor = new CliExecutor(safeRequest);
+
+ // Send the next test case request with its attack payload
+ System.out.println("Executing attack request: " + attackRequest);
+ attackPayloadResponseInfo = execute(attackRequest, true);
+ //// executeArgs.add(payload);
+ // ProcessBuilder builder = new
+ // ProcessBuilder(executeArgs);
+ // final Process process = builder.start();
+ // int exitValue = process.waitFor();
+ // attackPayloadResponseInfo = new ResponseInfo();
+ // System.out.printf("Program terminated with return code:
+ // %s%n", exitValue);
+ responseInfoList.add(attackPayloadResponseInfo);
+
+ // Log the response
+ log(attackPayloadResponseInfo);
+
+ safePayloadResponseInfo = null;
+ if (!testCase.isUnverifiable()) {
+ // Send the next test case request with its safe payload
+ System.out.println("Executing safe request: " + safeRequest);
+ safePayloadResponseInfo = execute(safeRequest);
+ responseInfoList.add(safePayloadResponseInfo);
+
+ // Log the response
+ log(safePayloadResponseInfo);
+ }
+
+ // TestCaseVerificationResults result =
+ // new TestCaseVerificationResults(
+ // attackRequest,
+ // safeRequest,
+ // cliTestCase,
+ // attackPayloadResponseInfo,
+ // safePayloadResponseInfo);
+ // results.add(result);
+ //
+ // // Verify the response
+ // if (RegressionTesting.isTestingEnabled) {
+ // handleResponse(result);
+ // }
+ }
+ TestCaseVerificationResults result =
+ new TestCaseVerificationResults(
+ attackExecutor.getExecutorDescription(),
+ safeExecutor.getExecutorDescription(),
+ testCase,
+ attackPayloadResponseInfo,
+ safePayloadResponseInfo);
+ results.add(result);
+
+ // Verify the response
+ if (RegressionTesting.isTestingEnabled) {
+ handleResponse(result);
+ }
+ }
+ TestCaseVerificationResultsCollection resultsCollection =
+ new TestCaseVerificationResultsCollection();
+ resultsCollection.setResultsObjects(results);
- // Log the elapsed time for all test cases
- long stop = System.currentTimeMillis();
- int seconds = (int) (stop - start) / 1000;
+ // Log the elapsed time for all test cases
+ long stop = System.currentTimeMillis();
+ int seconds = (int) (stop - start) / 1000;
- Date now = new Date();
+ Date now = new Date();
- completionMessage =
- String.format(
- "Verification crawl ran on %tF % 0) {
+ System.out.printf(
+ "Details of non-discriminatory test cases written to: %s%n",
+ FILE_NON_DISCRIMINATORY_LOG);
+ }
+ if (FILE_ERRORS_LOG.length() > 0) {
+ System.out.printf(
+ "Details of errors/exceptions in test cases written to: %s%n",
+ FILE_ERRORS_LOG);
+ }
+ if (FILE_UNVERIFIABLE_LOG.length() > 0) {
+ System.out.printf(
+ "Details of unverifiable test cases written to: %s%n",
+ FILE_UNVERIFIABLE_LOG);
+ }
+
+ VerifyFixesOutput verifyFixesOutput = new VerifyFixesOutput();
+ verifyFixesOutput.setList(verifyFixesOutputList);
+
+ File verifyFixResultFile = new File(getOutputDirectory(), FILENAME_VERIFY_FIX_RESULT);
+ try (BufferedWriter writer = new BufferedWriter(new FileWriter(verifyFixResultFile))) {
+ String output = Utils.objectToJson(verifyFixesOutput);
+ // System.out.println(output);
+ writer.write(output);
+ } catch (IOException e) {
+ System.out.println(
+ "ERROR: Could not write VerifyFixOutputList to file "
+ + verifyFixResultFile);
+ e.printStackTrace();
+ } catch (JAXBException e) {
+ System.out.println("ERROR: Could not marshall VerifyFixOutputList to JSON");
+ e.printStackTrace();
+ }
+
+ System.out.printf("Test case time measurements written to: %s%n", FILE_TIMES_LOG);
+
+ RegressionTesting.printCrawlSummary(results);
+ if (Boolean.parseBoolean(verifyFixed)) {
+ printFixVerificationSummary();
+ }
+ System.out.println();
+ System.out.println(completionMessage);
}
- if (FILE_NON_DISCRIMINATORY_LOG.length() > 0) {
- System.out.printf(
- "Details of non-discriminatory test cases written to: %s%n",
- FILE_NON_DISCRIMINATORY_LOG);
+ // FIXME: Use a finally to cleanup all setup resources that were required
+ // cleanupSetups(setups);
+ }
+
+ private boolean isTestCaseIdentical(
+ String unfixedSourceDirectory, String fixedSourceDirectory, String testCaseName) {
+ // FIXME: Generalize this so it can support languages other than Java and multiple
+ // source files per testcase.
+ String unfixedSourceFile =
+ Paths.get(unfixedSourceDirectory, testCaseName).toString() + ".java";
+ String fixedSourceFile = Paths.get(fixedSourceDirectory, testCaseName).toString() + ".java";
+ String unfixedSourceFileContents = null;
+ try {
+ unfixedSourceFileContents =
+ new String(Files.readAllBytes(Paths.get(unfixedSourceFile)));
+ } catch (IOException e) {
+ System.out.println("ERROR: Could not read testcase source file " + unfixedSourceFile);
+ e.printStackTrace();
}
- if (FILE_ERRORS_LOG.length() > 0) {
- System.out.printf(
- "Details of errors/exceptions in test cases written to: %s%n", FILE_ERRORS_LOG);
+ String fixedSourceFileContents = null;
+ try {
+ fixedSourceFileContents = new String(Files.readAllBytes(Paths.get(fixedSourceFile)));
+ } catch (IOException e) {
+ System.out.println("ERROR: Could not read testcase source file " + fixedSourceFile);
+ e.printStackTrace();
}
- if (FILE_UNVERIFIABLE_LOG.length() > 0) {
- System.out.printf(
- "Details of unverifiable test cases written to: %s%n", FILE_UNVERIFIABLE_LOG);
+ // DEBUG
+ // System.out.println(
+ // testCaseName
+ // + ": isTestCaseIdentical() returning "
+ // + (unfixedSourceFileContents != null
+ // && fixedSourceFileContents != null
+ // && unfixedSourceFileContents.equals(fixedSourceFileContents)));
+
+ // Skip testcase in verifyFixed mode if fixed source code is unchanged.
+ return unfixedSourceFileContents != null
+ && fixedSourceFileContents != null
+ && unfixedSourceFileContents.equals(fixedSourceFileContents);
+ }
+
+ private void printFixVerificationSummary() {
+ System.out.println();
+ System.out.println("Fix verification summary");
+ System.out.println();
+ System.out.println("Total vulnerable test cases: " + vulnerableTestcases.size());
+ System.out.println(
+ "\tProperly fixed (not exploitable):\t"
+ + (vulnerableTestcases.size()
+ - exploitedModifiedVulnerableTestcases.size()));
+ System.out.println(
+ "\tNot correctly fixed (still exploitable):\t"
+ + exploitedModifiedVulnerableTestcases.size());
+ System.out.println(
+ "\tNot auto-verifiable (can't tell if exploitable):\t"
+ + notVerifiableModifiedVulnerableTestcases.size());
+ System.out.println(
+ "\tFunctionality broken/modified:\t" + brokenModifiedVulnerableTestcases.size());
+ System.out.println();
+ System.out.println("Total not vulnerable test cases: " + notVulnerableTestcases.size());
+ System.out.println(
+ "\tStill not exploitable:\t"
+ + (notVulnerableTestcases.size()
+ - exploitedModifiedNotVulnerableTestcases.size()));
+ System.out.println("\tNow exploitable:\t" + exploitedModifiedNotVulnerableTestcases.size());
+ System.out.println(
+ "\tNot auto-verifiable (can't tell if exploitable):\t"
+ + notVerifiableModifiedNotVulnerableTestcases.size());
+ System.out.println(
+ "\tFunctionality broken/modified:\t" + brokenModifiedNotVulnerableTestcases.size());
+ }
+
+ /**
+ * @param testSuite
+ * @throws Exception
+ */
+ protected void crawlToVerifyFix(TestSuite testSuite, String unfixedOutputDirectory)
+ throws Exception {
+
+ // Get config directory for RUN 1 from CLI option
+ // Get output directory for RUN 1
+ // Get test case directory of RUN 2 from CLI option
+ // Get output directory for RUN 2
+ // Create a copy of TestSuite from the RUN 1 config
+ // Modify TestSuite object so that every TestCase has isVulnerability = false
+ // Call crawl(TestSuite) on the new TestSuite
+ // Compare output of RUN 1 to output of RUN 2 and report result
+ }
+
+ private List getTestCaseSetups(TestSuite testSuite) {
+ List testCaseSetups = new ArrayList();
+ for (TestCase testCase : testSuite.getTestCases()) {
+ TestCaseSetup testCaseSetup = testCase.getTestCaseSetup();
+ if (testCaseSetup != null) {
+ testCaseSetups.add(testCaseSetup);
+ }
}
- System.out.printf("Test case time measurements written to: %s%n", FILE_TIMES_LOG);
+ return testCaseSetups;
+ }
- RegressionTesting.printCrawlSummary(results);
- System.out.println();
- System.out.println(completionMessage);
+ private void initializeSetups(List testCaseSetups)
+ throws TestCaseSetupException {
+ for (TestCaseSetup testCaseSetup : testCaseSetups) {
+ testCaseSetup.setup();
+ }
+ }
+
+ private void cleanupSetups(List testCaseSetups) throws TestCaseSetupException {
+ for (TestCaseSetup testCaseSetup : testCaseSetups) {
+ testCaseSetup.close();
+ }
+ }
+
+ protected String getConfigurationDirectory() {
+ return configurationDirectory;
+ }
+
+ protected String getOutputDirectory() {
+ return outputDirectory;
}
private void log(ResponseInfo responseInfo) throws IOException {
- // Log the response
- HttpUriRequest requestBase = responseInfo.getRequestBase();
- String outputString =
- String.format(
- "--> (%d : %d sec)%n",
- responseInfo.getStatusCode(), responseInfo.getTimeInSeconds());
- try {
+ if (responseInfo instanceof HttpResponseInfo) {
+ HttpResponseInfo httpResponseInfo = (HttpResponseInfo) responseInfo;
+
+ // Log the response
+ // HttpUriRequest requestBase = httpResponseInfo.getRequestBase();
+ String outputString =
+ String.format(
+ "--> (%d : %d sec)%n",
+ httpResponseInfo.getStatusCode(), httpResponseInfo.getTimeInSeconds());
if (isTimingEnabled) {
- if (responseInfo.getTimeInSeconds() >= maxTimeInSeconds) {
- tLogger.println(requestBase.getMethod() + " " + requestBase.getUri());
+ if (httpResponseInfo.getTimeInSeconds() >= maxTimeInSeconds) {
+ tLogger.println(httpResponseInfo.getMethod() + " " + httpResponseInfo.getUri());
tLogger.println(outputString);
- } // else do nothing
+ }
} else {
- tLogger.println(requestBase.getMethod() + " " + requestBase.getUri());
+ tLogger.println(httpResponseInfo.getMethod() + " " + httpResponseInfo.getUri());
tLogger.println(outputString);
}
- } catch (URISyntaxException e) {
- String errMsg = requestBase.getMethod() + " COULDN'T LOG URI due to URISyntaxException";
- tLogger.println(errMsg);
- tLogger.println(outputString);
- System.out.println(errMsg);
- e.printStackTrace();
+
+ } else if (responseInfo instanceof CliResponseInfo) {
+ CliResponseInfo cliResponseInfo = (CliResponseInfo) responseInfo;
+
+ // Log the response
+ CliRequest request = cliResponseInfo.getRequest();
+ String responseString =
+ String.format(
+ "--> (%d : %d sec)%n",
+ cliResponseInfo.getStatusCode(), cliResponseInfo.getTimeInSeconds());
+ if (isTimingEnabled) {
+ if (cliResponseInfo.getTimeInSeconds() >= maxTimeInSeconds) {
+ tLogger.println(request.getCommand());
+ tLogger.println(responseString);
+ }
+ } else {
+ tLogger.println(request.getCommand());
+ tLogger.println(responseString);
+ }
}
}
/**
* For the verification crawler, processing the result means verifying whether the test case is
* actually vulnerable or not, relative to whether it is supposed to be vulnerable. This method
- * has a side-affect of setting request.setPassed() for the current test case. Passing means it
+ * has a side-effect of setting request.setPassed() for the current test case. Passing means it
* was exploitable for a True Positive and appears to not be exploitable for a False Positive.
*
* @param result - The results required to verify this test case.
* @throws FileNotFoundException
* @throws LoggerConfigurationException
*/
- protected static void handleResponse(TestCaseVerificationResults result)
+ protected void handleResponse(TestCaseVerificationResults results)
throws FileNotFoundException, LoggerConfigurationException {
// Check to see if this specific test case has a specified expected response value.
// If so, run it through verification using it's specific attackSuccessIndicator.
// Note that a specific success indicator overrides any generic category tests, if
// specified.
- RegressionTesting.verifyTestCase(result);
+ RegressionTesting.verifyTestCase(results);
+
+ if (Boolean.parseBoolean(verifyFixed)) {
+ String unfixedOutputDirectory = configurationDirectory;
+
+ TestCaseVerificationResults fixedResults = results;
+ TestCaseVerificationResultsCollection unfixedResultsCollection =
+ loadTestCaseVerificationResults(unfixedOutputDirectory);
+ TestCaseVerificationResults unfixedResults =
+ testCaseNameToTestCaseVerificationResultsMap.get(
+ fixedResults.getTestCase().getName());
+ if (unfixedResults
+ .getTestCase()
+ .getName()
+ .equals(fixedResults.getTestCase().getName())) {
+
+ // // FIXME: Generalize this so it can support languages other than Java and
+ // multiple
+ // // source files per testcase.
+ // String unfixedSourceFile =
+ // Paths.get(unfixedSourceDirectory, unfixedResults.getTestCase().getName())
+ // .toString()
+ // + ".java";
+ // String fixedSourceFile =
+ // Paths.get(fixedSourceDirectory, fixedResults.getTestCase().getName())
+ // .toString()
+ // + ".java";
+ // String unfixedSourceFileContents =
+ // new String(Files.readAllBytes(Paths.get(unfixedSourceFile)));
+ // String fixedSourceFileContents =
+ // new String(Files.readAllBytes(Paths.get(fixedSourceFile)));
+
+ // // Skip testcase in verifyFixed mode if fixed source code is unchanged.
+ // if (Boolean.parseBoolean(verifyFixed)
+ // && unfixedSourceFileContents.equals(fixedSourceFileContents)) {
+ // // System.out.println(
+ // // "WARNING: Testcase "
+ // // + fixedResults.getTestCase().getName()
+ // // + " source file unmodified");
+ // } else {
+ // verifyFix(unfixedResults, fixedResults);
+ // }
+ // if (Boolean.parseBoolean(verifyFixed)
+ // && !isTestCaseIdentical(
+ // unfixedSourceDirectory,
+ // fixedSourceDirectory,
+ // unfixedResults.getTestCase().getName())) {
+ // verifyFix(unfixedResults, fixedResults);
+ // }
+ verifyFix(unfixedResults, fixedResults);
+ } else {
+ System.out.println(
+ "WARNING: After fix testcase is "
+ + fixedResults.getTestCase().getName()
+ + " but before fix testcase is "
+ + unfixedResults.getTestCase().getName());
+ }
+ }
+ }
+
+ private TestCaseVerificationResultsCollection loadTestCaseVerificationResults(
+ String directory) {
+
+ TestCaseVerificationResultsCollection results = null;
+ try {
+ results =
+ Utils.jsonToTestCaseVerificationResultsList(
+ new File(directory, FILENAME_TC_VERIF_RESULTS_JSON));
+ for (TestCaseVerificationResults testCaseResults : results.getResultsObjects()) {
+ testCaseNameToTestCaseVerificationResultsMap.put(
+ testCaseResults.getTestCase().getName(), testCaseResults);
+ }
+
+ } catch (JAXBException
+ | FileNotFoundException
+ | SAXException
+ | ParserConfigurationException e) {
+ System.out.println("ERROR: Could not unmarshall JSON file content.");
+ e.printStackTrace();
+ }
+
+ // FIXME: Replace this with code to load results from JSON data files
+ // TestExecutor attackTestExecutor = new HttpExecutor(null);
+ // TestExecutor safeTestExecutor = new HttpExecutor(null);
+ // TestCase testCase = new TestCase();
+ // ResponseInfo responseToAttackValue = new HttpResponseInfo(); // FIXME: Or
+ // CliResponseInfo
+ // responseToAttackValue.setResponseString("");
+ // ResponseInfo responseToSafeValue = new HttpResponseInfo();
+ // responseToSafeValue.setResponseString("");
+ // TestCaseVerificationResults results =
+ // new TestCaseVerificationResults(
+ // attackTestExecutor,
+ // safeTestExecutor,
+ // testCase,
+ // responseToAttackValue,
+ // responseToSafeValue);
+
+ return results;
+ }
+
+ private boolean verifyFix(
+ TestCaseVerificationResults unfixedResults, TestCaseVerificationResults fixedResults) {
+
+ // DEBUG
+ try {
+ String unfixedResultsJson = Utils.objectToJson(unfixedResults);
+ System.out.println("unfixedResults JSON: " + unfixedResultsJson);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ boolean isVulnerable = fixedResults.getTestCase().isVulnerability();
+ // boolean wasNotVerifiable =
+ // fixedResults.getTestCase().isVulnerability()
+ // && fixedResults.getTestCase().isUnverifiable()
+ // && fixedResults.isPassed();
+ boolean wasNotVerifiable = fixedResults.getTestCase().isUnverifiable();
+ boolean wasExploited =
+ fixedResults.getTestCase().isVulnerability()
+ && !fixedResults.getTestCase().isUnverifiable()
+ && fixedResults.isPassed();
+ boolean wasBroken = false;
+ if (fixedResults.getTestCase().isUnverifiable()) {
+ wasBroken = false;
+ } else {
+ // There will be no safe response info if testcase is not verifiable.
+ wasBroken =
+ !unfixedResults
+ .getResponseToSafeValue()
+ .getResponseString()
+ .equals(fixedResults.getResponseToSafeValue().getResponseString());
+ }
+
+ VerifyFixOutput verifyFixOutput = new VerifyFixOutput();
+ verifyFixOutput.setTestCaseName(fixedResults.getTestCase().getName());
+ verifyFixOutput.setUnfixedSafeResponseInfo(unfixedResults.getResponseToSafeValue());
+ verifyFixOutput.setUnfixedAttackResponseInfo(unfixedResults.getResponseToAttackValue());
+ verifyFixOutput.setFixedSafeResponseInfo(fixedResults.getResponseToSafeValue());
+ verifyFixOutput.setFixedAttackResponseInfo(fixedResults.getResponseToAttackValue());
+ verifyFixOutput.setWasNotVerifiable(wasNotVerifiable);
+ verifyFixOutput.setWasExploited(wasExploited);
+ verifyFixOutput.setWasBroken(wasBroken);
+
+ // DEBUG
+ try {
+ String verifyFixOutputJson = Utils.objectToJson(verifyFixOutput);
+ System.out.println("verifyFixOutput JSON: " + verifyFixOutputJson);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ if (isVulnerable) {
+ vulnerableTestcases.add(verifyFixOutput);
+ if (wasNotVerifiable) {
+ System.out.println("NOT FIXED: Vulnerability could not be verified");
+ notVerifiableModifiedVulnerableTestcases.add(verifyFixOutput);
+ } else {
+ if (wasExploited) {
+ System.out.println("NOT FIXED: Vulnerability was exploited");
+ exploitedModifiedVulnerableTestcases.add(verifyFixOutput);
+ }
+ if (wasBroken) {
+ System.out.println("NOT FIXED: Functionality was broken");
+ brokenModifiedVulnerableTestcases.add(verifyFixOutput);
+ }
+ }
+ } else {
+ notVulnerableTestcases.add(verifyFixOutput);
+ if (wasNotVerifiable) {
+ notVerifiableModifiedNotVulnerableTestcases.add(verifyFixOutput);
+ } else {
+ if (wasExploited) {
+ exploitedModifiedNotVulnerableTestcases.add(verifyFixOutput);
+ }
+ if (wasBroken) {
+ brokenModifiedNotVulnerableTestcases.add(verifyFixOutput);
+ }
+ }
+ }
+
+ verifyFixesOutputList.add(verifyFixOutput);
+
+ return !wasNotVerifiable && !wasExploited && !wasBroken;
+ }
+
+ private boolean verifyFixes(
+ TestCaseVerificationResultsCollection beforeFixResultsCollection,
+ TestCaseVerificationResultsCollection afterFixResultsCollection) {
+
+ boolean isVerified = true;
+
+ // This assumes that the results are ordered.
+ if (beforeFixResultsCollection.getResultsObjects().size()
+ != afterFixResultsCollection.getResultsObjects().size()) {
+ System.out.println("ERROR: Results lists are not the same size");
+ isVerified = false;
+ } else {
+ int count = beforeFixResultsCollection.getResultsObjects().size();
+ for (int i = 0; i < count; i++) {
+ TestCaseVerificationResults beforeFixResults =
+ beforeFixResultsCollection.getResultsObjects().get(i);
+ TestCaseVerificationResults afterFixResults =
+ afterFixResultsCollection.getResultsObjects().get(i);
+ isVerified &= verifyFix(beforeFixResults, afterFixResults);
+ }
+ }
+ return isVerified;
}
/**
@@ -278,15 +837,12 @@ protected static void handleResponse(TestCaseVerificationResults result)
* @param args - args passed to main().
* @return specified crawler file if valid command line arguments provided. Null otherwise.
*/
- @Override
protected void processCommandLineArgs(String[] args) {
+ System.out.println("Maven user selected verifyFixed=" + verifyFixed);
- // Set default attack crawler file, if it exists
- // This value can be changed by the -f parameter for other test suites with different names
- File defaultAttackCrawlerFile = new File(Utils.DATA_DIR, "benchmark-attack-http.xml");
- if (defaultAttackCrawlerFile.exists()) {
- setCrawlerFile(defaultAttackCrawlerFile.getPath());
- }
+ // Set default attack crawler file
+ String crawlerFileName = new File(Utils.DATA_DIR, "benchmark-attack-http.xml").getPath();
+ this.theCrawlerFile = new File(crawlerFileName);
RegressionTesting.isTestingEnabled = true;
@@ -297,14 +853,40 @@ protected void processCommandLineArgs(String[] args) {
// Create the Options
Options options = new Options();
+ options.addOption(
+ Option.builder("b")
+ .longOpt("unfixedSrcDirectory")
+ .desc("source directory before fixes")
+ .hasArg()
+ .build());
+ options.addOption(
+ Option.builder("a")
+ .longOpt("fixedSrcDirectory")
+ .desc("source directory after fixes")
+ .hasArg()
+ .build());
+ options.addOption(
+ Option.builder("o")
+ .longOpt("outputDirectory")
+ .desc("output directory")
+ .hasArg()
+ .build());
options.addOption(
Option.builder("f")
.longOpt("file")
- .desc("a TESTSUITE-attack-http.xml file")
+ .desc("a TESTSUITE-crawler-http.xml file")
.hasArg()
.required()
.build());
options.addOption(Option.builder("h").longOpt("help").desc("Usage").build());
+ options.addOption(
+ Option.builder("j")
+ .longOpt("generateJSONResults")
+ .desc("generate json version of verification results")
+ .build());
+ // options.addOption("m", "verifyFixed", false, "verify fixed test suite");
+ options.addOption(
+ Option.builder("m").longOpt("verifyFixed").desc("verify fixed test suite").build());
options.addOption(
Option.builder("n")
.longOpt("name")
@@ -323,42 +905,102 @@ protected void processCommandLineArgs(String[] args) {
// Parse the command line arguments
CommandLine line = parser.parse(options, args);
+ if (line.hasOption("o")) {
+ outputDirectory = line.getOptionValue("o");
+ } else {
+ outputDirectory = defaultOutputDirectory;
+ }
+ // Required if in verifyFix mode
+ if (line.hasOption("b")) {
+ unfixedSourceDirectory = line.getOptionValue("b");
+ }
+ // Required if in verifyFix mode
+ if (line.hasOption("a")) {
+ fixedSourceDirectory = line.getOptionValue("a");
+ }
if (line.hasOption("f")) {
- // Following throws a RuntimeException if the target file doesn't exist
- setCrawlerFile(line.getOptionValue("f"));
- // Crawler output files go into the same directory as the crawler config file
- CRAWLER_DATA_DIR = this.theCrawlerFile.getParent() + File.separator;
+ this.crawlerFile = line.getOptionValue("f");
+ File targetFile = new File(this.crawlerFile);
+ if (targetFile.exists()) {
+ setCrawlerFile(targetFile);
+ // Crawler output files go into the same directory as the crawler config file
+ configurationDirectory = targetFile.getParent();
+ } else {
+ throw new RuntimeException(
+ "Could not find crawler configuration file '" + this.crawlerFile + "'");
+ }
}
if (line.hasOption("h")) {
formatter.printHelp("BenchmarkCrawlerVerification", options, true);
}
+ if (line.hasOption("j")) {
+ generateJSONResults = "true";
+ }
+ if (line.hasOption("m")) {
+ System.out.println("User selected to verify fixes");
+ verifyFixed = "true";
+ }
if (line.hasOption("n")) {
selectedTestCaseName = line.getOptionValue("n");
}
if (line.hasOption("t")) {
maxTimeInSeconds = (Integer) line.getParsedOptionValue("t");
}
+
+ // The default is different if we are in verifyFix mode
+ if (Boolean.parseBoolean(this.verifyFixed)) {
+ outputDirectory = Paths.get(Utils.DATA_DIR, "fixstatus").toString();
+ }
} catch (ParseException e) {
formatter.printHelp("BenchmarkCrawlerVerification", options);
throw new RuntimeException("Error parsing arguments: ", e);
}
}
+ /**
+ * The execute() method is invoked when this class is invoked as a maven plugin, rather than via
+ * the command line. So what we do here is set up the command line parameters and then invoke
+ * main() so this can be called both as a plugin, or via the command line.
+ */
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
- if (thisInstance == null) thisInstance = this;
-
- if (null == this.crawlerFile) {
- System.out.println("ERROR: An attack crawlerFile parameter must be specified.");
+ if (null == this.pluginFilenameParam) {
+ System.out.println("ERROR: A crawlerFile parameter must be specified.");
} else {
- String[] mainArgs = {"-f", this.crawlerFile};
- main(mainArgs);
+ List mainArgs = new ArrayList<>();
+ mainArgs.add("-f");
+ mainArgs.add(this.pluginFilenameParam);
+ if (this.outputDirectory != null) {
+ mainArgs.add("-o");
+ mainArgs.add(this.outputDirectory);
+ }
+ if (this.unfixedSourceDirectory != null) {
+ mainArgs.add("-b");
+ mainArgs.add(this.unfixedSourceDirectory);
+ }
+ if (this.fixedSourceDirectory != null) {
+ mainArgs.add("-a");
+ mainArgs.add(this.fixedSourceDirectory);
+ }
+ if (this.pluginTestCaseNameParam != null) {
+ mainArgs.add("-n");
+ mainArgs.add(this.pluginTestCaseNameParam);
+ }
+ if (Boolean.parseBoolean(this.verifyFixed)) {
+ // At the command line, only the -m is required, with no value needed
+ mainArgs.add("-m");
+ }
+ if (Boolean.parseBoolean(this.generateJSONResults)) {
+ // At the command line, only the -j is required, with no value needed
+ mainArgs.add("-j");
+ }
+ main(mainArgs.stream().toArray(String[]::new));
}
}
public static void main(String[] args) {
// thisInstance can be set from execute() or here, depending on how this class is invoked
- // (via maven or commmand line)
+ // (via maven or command line)
if (thisInstance == null) {
thisInstance = new BenchmarkCrawlerVerification();
}
diff --git a/plugin/src/main/java/org/owasp/benchmarkutils/tools/CalculateToolCodeBlocksSupport.java b/plugin/src/main/java/org/owasp/benchmarkutils/tools/CalculateToolCodeBlocksSupport.java
index 1e5655b3..9ee70b15 100644
--- a/plugin/src/main/java/org/owasp/benchmarkutils/tools/CalculateToolCodeBlocksSupport.java
+++ b/plugin/src/main/java/org/owasp/benchmarkutils/tools/CalculateToolCodeBlocksSupport.java
@@ -40,6 +40,7 @@
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
+import org.owasp.benchmarkutils.entities.TestCase;
import org.owasp.benchmarkutils.helpers.CodeblockUtils;
import org.owasp.benchmarkutils.helpers.Utils;
import org.owasp.benchmarkutils.score.TestCaseResult;
@@ -83,100 +84,6 @@ public void setScorecardResultsFile(String targetFileName)
}
}
- /**
- * Process the command line arguments that make any configuration changes.
- *
- * @param args - args passed to main().
- * @return specified crawler file if valid command line arguments provided. Null otherwise.
- */
- @Override
- protected void processCommandLineArgs(String[] args) {
-
- // 1. Load testsuite-attack XML file so we have the codeblocks for every test case - DONE
- // 1a. TestSuite instance has all the TestCases
- // 1b. Codeblocks are attributes of each instance of TestCase, created/read in from XML.
-
- // Example: -DcrawlerFile=data/benchmark-attack-http.xml
-
- // 2. Load the .csv results for the selected tool - DONE
-
- // Example:
- // -DresultsCSVFile=../../BenchmarkJavaBaseApp/scorecard/Benchmark_v1.3_Scorecard_for_Contrast_Assess_v4.8.0.csv
-
- // Do the work in run().
-
- // Set default attack crawler file, if it exists
- // This value can be changed by the -f parameter for other test suites with different names
- File defaultAttackCrawlerFile = new File(Utils.DATA_DIR, "benchmark-attack-http.xml");
- if (defaultAttackCrawlerFile.exists()) {
- setCrawlerFile(defaultAttackCrawlerFile.getPath());
- }
-
- RegressionTesting.isTestingEnabled = true;
-
- // Create the command line parser
- CommandLineParser parser = new DefaultParser();
-
- HelpFormatter formatter = new HelpFormatter();
-
- // Create the Options
- Options options = new Options();
- options.addOption(
- Option.builder("f")
- .longOpt("file")
- .desc("a TESTSUITE-attack-http.xml file")
- .hasArg()
- .required()
- .build());
- options.addOption(
- Option.builder("r")
- .longOpt("file")
- .desc("a scorecard generated toolResults.csv file")
- .hasArg()
- .required()
- .build());
- /* options.addOption(Option.builder("h").longOpt("help").desc("Usage").build());
- options.addOption(
- Option.builder("n")
- .longOpt("name")
- .desc("tescase name (e.g. BenchmarkTestCase00025)")
- .hasArg()
- .build());
- options.addOption(
- Option.builder("t")
- .longOpt("time")
- .desc("testcase timeout (in seconds)")
- .hasArg()
- .type(Integer.class)
- .build());
- */
- try {
- // Parse the command line arguments
- CommandLine line = parser.parse(options, args);
-
- if (line.hasOption("f")) {
- // Following throws a RuntimeException if the attack crawler file doesn't exist
- setCrawlerFile(line.getOptionValue("f"));
- }
- if (line.hasOption("r")) {
- setScorecardResultsFile(line.getOptionValue("r"));
- }
- /* if (line.hasOption("h")) {
- formatter.printHelp("BenchmarkCrawlerVerification", options, true);
- }
- if (line.hasOption("n")) {
- selectedTestCaseName = line.getOptionValue("n");
- }
- if (line.hasOption("t")) {
- maxTimeInSeconds = (Integer) line.getParsedOptionValue("t");
- }
- */
- } catch (ParseException | IOException e) {
- formatter.printHelp("CalculateToolCodeBlocksSupport", options);
- throw new RuntimeException("Error parsing arguments: ", e);
- }
- }
-
/** Calculate the code block support for the specified tool for the specified test suite. */
@Override
protected void run() {
@@ -198,7 +105,7 @@ protected void run() {
new TestSuiteResults(this.testSuite.getName(), false, ToolType.SAST);
// Get all the TestCase info loaded from TESTSUITE-attack-http.xml file
- List theTestcases = this.testSuite.getTestCases();
+ List theTestcases = this.testSuite.getTestCases();
int testSuiteSize = theTestcases.size();
try {
@@ -634,6 +541,99 @@ public void execute() throws MojoExecutionException, MojoFailureException {
}
}
+ /**
+ * Process the command line arguments that make any configuration changes.
+ *
+ * @param args - args passed to main().
+ * @return specified crawler file if valid command line arguments provided. Null otherwise.
+ */
+ protected void processCommandLineArgs(String[] args) {
+
+ // 1. Load testsuite-attack XML file so we have the codeblocks for every test case - DONE
+ // 1a. TestSuite instance has all the TestCases
+ // 1b. Codeblocks are attributes of each instance of TestCase, created/read in from XML.
+
+ // Example: -DcrawlerFile=data/benchmark-attack-http.xml
+
+ // 2. Load the .csv results for the selected tool - DONE
+
+ // Example:
+ // -DresultsCSVFile=../../BenchmarkJavaBaseApp/scorecard/Benchmark_v1.3_Scorecard_for_Contrast_Assess_v4.8.0.csv
+
+ // Do the work in run().
+
+ // Set default attack crawler file, if it exists
+ // This value can be changed by the -f parameter for other test suites with different names
+ File defaultAttackCrawlerFile = new File(Utils.DATA_DIR, "benchmark-attack-http.xml");
+ if (defaultAttackCrawlerFile.exists()) {
+ setCrawlerFile(defaultAttackCrawlerFile);
+ }
+
+ RegressionTesting.isTestingEnabled = true;
+
+ // Create the command line parser
+ CommandLineParser parser = new DefaultParser();
+
+ HelpFormatter formatter = new HelpFormatter();
+
+ // Create the Options
+ Options options = new Options();
+ options.addOption(
+ Option.builder("f")
+ .longOpt("file")
+ .desc("a TESTSUITE-attack-http.xml file")
+ .hasArg()
+ .required()
+ .build());
+ options.addOption(
+ Option.builder("r")
+ .longOpt("file")
+ .desc("a scorecard generated toolResults.csv file")
+ .hasArg()
+ .required()
+ .build());
+ /* options.addOption(Option.builder("h").longOpt("help").desc("Usage").build());
+ options.addOption(
+ Option.builder("n")
+ .longOpt("name")
+ .desc("tescase name (e.g. BenchmarkTestCase00025)")
+ .hasArg()
+ .build());
+ options.addOption(
+ Option.builder("t")
+ .longOpt("time")
+ .desc("testcase timeout (in seconds)")
+ .hasArg()
+ .type(Integer.class)
+ .build());
+ */
+ try {
+ // Parse the command line arguments
+ CommandLine line = parser.parse(options, args);
+
+ if (line.hasOption("f")) {
+ // Following throws a RuntimeException if the attack crawler file doesn't exist
+ setCrawlerFile(new File(line.getOptionValue("f")));
+ }
+ if (line.hasOption("r")) {
+ setScorecardResultsFile(line.getOptionValue("r"));
+ }
+ /* if (line.hasOption("h")) {
+ formatter.printHelp("BenchmarkCrawlerVerification", options, true);
+ }
+ if (line.hasOption("n")) {
+ selectedTestCaseName = line.getOptionValue("n");
+ }
+ if (line.hasOption("t")) {
+ maxTimeInSeconds = (Integer) line.getParsedOptionValue("t");
+ }
+ */
+ } catch (ParseException | IOException e) {
+ formatter.printHelp("CalculateToolCodeBlocksSupport", options);
+ throw new RuntimeException("Error parsing arguments: ", e);
+ }
+ }
+
public static void main(String[] args) {
// thisInstance can be set from execute() or here, depending on how this class is invoked
// (via maven or command line)
diff --git a/plugin/src/main/java/org/owasp/benchmarkutils/tools/CliExecutor.java b/plugin/src/main/java/org/owasp/benchmarkutils/tools/CliExecutor.java
new file mode 100644
index 00000000..9e45f347
--- /dev/null
+++ b/plugin/src/main/java/org/owasp/benchmarkutils/tools/CliExecutor.java
@@ -0,0 +1,54 @@
+/**
+ * OWASP Benchmark Project
+ *
+ *
This file is part of the Open Web Application Security Project (OWASP) Benchmark Project For
+ * details, please see https://owasp.org/www-project-benchmark/.
+ *
+ *
The OWASP Benchmark is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, version 2.
+ *
+ *
The OWASP Benchmark is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * @author David Anderson
+ * @created 2024
+ */
+package org.owasp.benchmarkutils.tools;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.xml.bind.annotation.XmlRootElement;
+import org.owasp.benchmarkutils.entities.CliRequest;
+import org.owasp.benchmarkutils.entities.RequestVariable;
+
+@XmlRootElement(name = "CliRequest")
+public class CliExecutor extends TestExecutor {
+ CliRequest cliRequest;
+
+ public CliExecutor() {}
+
+ public CliExecutor(CliRequest cliRequest) {
+ super();
+ this.cliRequest = cliRequest;
+ }
+
+ public CliRequest getCliRequest() {
+ return cliRequest;
+ }
+
+ public void setCliRequest(CliRequest cliRequest) {
+ this.cliRequest = cliRequest;
+ }
+
+ public String getExecutorDescription() {
+ List commandTokens = new ArrayList<>();
+ commandTokens.add(cliRequest.getCommand());
+ for (RequestVariable requestVariable : cliRequest.getArgs()) {
+ commandTokens.add(String.format("%s%n", requestVariable.getValue()));
+ }
+
+ return commandTokens.toString();
+ }
+}
diff --git a/plugin/src/main/java/org/owasp/benchmarkutils/tools/CodeBlockSupportResults.java b/plugin/src/main/java/org/owasp/benchmarkutils/tools/CodeBlockSupportResults.java
index d4672f8d..8b70a7f7 100644
--- a/plugin/src/main/java/org/owasp/benchmarkutils/tools/CodeBlockSupportResults.java
+++ b/plugin/src/main/java/org/owasp/benchmarkutils/tools/CodeBlockSupportResults.java
@@ -1,3 +1,20 @@
+/**
+ * OWASP Benchmark Project
+ *
+ *
This file is part of the Open Web Application Security Project (OWASP) Benchmark Project For
+ * details, please see https://owasp.org/www-project-benchmark/.
+ *
+ *
The OWASP Benchmark is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, version 2.
+ *
+ *
The OWASP Benchmark is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * @author Dave Wichers
+ * @created 2023
+ */
package org.owasp.benchmarkutils.tools;
/*
diff --git a/plugin/src/main/java/org/owasp/benchmarkutils/tools/HttpExecutor.java b/plugin/src/main/java/org/owasp/benchmarkutils/tools/HttpExecutor.java
new file mode 100644
index 00000000..b4c535d6
--- /dev/null
+++ b/plugin/src/main/java/org/owasp/benchmarkutils/tools/HttpExecutor.java
@@ -0,0 +1,73 @@
+/**
+ * OWASP Benchmark Project
+ *
+ *
This file is part of the Open Web Application Security Project (OWASP) Benchmark Project For
+ * details, please see https://owasp.org/www-project-benchmark/.
+ *
+ *
The OWASP Benchmark is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, version 2.
+ *
+ *
The OWASP Benchmark is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * @author David Anderson
+ * @created 2024
+ */
+package org.owasp.benchmarkutils.tools;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.nio.charset.StandardCharsets;
+import javax.xml.bind.annotation.XmlRootElement;
+import org.apache.commons.io.IOUtils;
+import org.apache.hc.client5.http.classic.methods.HttpPost;
+import org.apache.hc.client5.http.classic.methods.HttpUriRequest;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HttpEntity;
+
+@XmlRootElement(name = "HttpRequest")
+public class HttpExecutor extends TestExecutor {
+ HttpUriRequest httpRequest;
+
+ public HttpExecutor() {}
+
+ public HttpExecutor(HttpUriRequest httpRequest) {
+ super();
+ this.httpRequest = httpRequest;
+ }
+
+ public HttpUriRequest getHttpRequest() {
+ return httpRequest;
+ }
+
+ public void setHttpRequest(HttpUriRequest httpRequest) {
+ this.httpRequest = httpRequest;
+ }
+
+ public String getExecutorDescription() {
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter out = new PrintWriter(stringWriter);
+
+ out.println(httpRequest.toString());
+ for (Header header : httpRequest.getHeaders()) {
+ out.printf("%s:%s%n", header.getName(), header.getValue());
+ }
+ if (httpRequest instanceof HttpPost) {
+ HttpPost postHttpRequest = (HttpPost) httpRequest;
+ try {
+ HttpEntity entity = postHttpRequest.getEntity();
+ if (entity != null) {
+ out.print(IOUtils.toString(entity.getContent(), StandardCharsets.UTF_8));
+ }
+ } catch (IOException e) {
+ System.out.println("ERROR: Could not parse HttpPost entities");
+ e.printStackTrace();
+ }
+ }
+ out.flush();
+ return stringWriter.toString();
+ }
+}
diff --git a/plugin/src/main/java/org/owasp/benchmarkutils/tools/Logger.java b/plugin/src/main/java/org/owasp/benchmarkutils/tools/Logger.java
index b063ec8a..600c57e4 100644
--- a/plugin/src/main/java/org/owasp/benchmarkutils/tools/Logger.java
+++ b/plugin/src/main/java/org/owasp/benchmarkutils/tools/Logger.java
@@ -1,3 +1,20 @@
+/**
+ * OWASP Benchmark Project
+ *
+ *
This file is part of the Open Web Application Security Project (OWASP) Benchmark Project For
+ * details, please see https://owasp.org/www-project-benchmark/.
+ *
+ *
The OWASP Benchmark is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, version 2.
+ *
+ *
The OWASP Benchmark is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * @author Juan Gama
+ * @created 2017
+ */
package org.owasp.benchmarkutils.tools;
public interface Logger {
diff --git a/plugin/src/main/java/org/owasp/benchmarkutils/tools/LoggerConfigurationException.java b/plugin/src/main/java/org/owasp/benchmarkutils/tools/LoggerConfigurationException.java
index c9213b02..45e05709 100644
--- a/plugin/src/main/java/org/owasp/benchmarkutils/tools/LoggerConfigurationException.java
+++ b/plugin/src/main/java/org/owasp/benchmarkutils/tools/LoggerConfigurationException.java
@@ -1,3 +1,20 @@
+/**
+ * OWASP Benchmark Project
+ *
+ *
This file is part of the Open Web Application Security Project (OWASP) Benchmark Project For
+ * details, please see https://owasp.org/www-project-benchmark/.
+ *
+ *
The OWASP Benchmark is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, version 2.
+ *
+ *
The OWASP Benchmark is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * @author Juan Gama
+ * @created 2017
+ */
package org.owasp.benchmarkutils.tools;
public class LoggerConfigurationException extends Exception {
diff --git a/plugin/src/main/java/org/owasp/benchmarkutils/tools/RegressionTesting.java b/plugin/src/main/java/org/owasp/benchmarkutils/tools/RegressionTesting.java
index 0383cf45..43cd04cd 100644
--- a/plugin/src/main/java/org/owasp/benchmarkutils/tools/RegressionTesting.java
+++ b/plugin/src/main/java/org/owasp/benchmarkutils/tools/RegressionTesting.java
@@ -29,11 +29,18 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import javax.xml.bind.JAXBException;
import org.apache.commons.io.IOUtils;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.HttpMessage;
+import org.owasp.benchmarkutils.entities.CliResponseInfo;
+import org.owasp.benchmarkutils.entities.HttpResponseInfo;
+import org.owasp.benchmarkutils.entities.HttpTestCaseInput;
+import org.owasp.benchmarkutils.entities.ResponseInfo;
+import org.owasp.benchmarkutils.entities.TestCase;
+import org.owasp.benchmarkutils.helpers.Utils;
/**
* Test all supported test cases to verify that the results are as expected and write the report to
@@ -63,21 +70,55 @@ public class RegressionTesting {
static SortedMultiset nonDiscriminatorySinks = TreeMultiset.create();
static SortedMultiset failSinks = TreeMultiset.create();
- static Map failedTruePositivesList =
- new LinkedHashMap();
- static Map failedFalsePositivesList =
- new LinkedHashMap();
+ static Map failedTruePositivesList = new LinkedHashMap<>();
+ static Map failedFalsePositivesList = new LinkedHashMap<>();
// TODO: Make this flag configurable via command line parameter
private static boolean isVerbosityOn = false;
private static final String FILENAME_FAILEDTC = "failedTestCases.txt";
+ private static final String FILENAME_TC_VERIF_RESULTS_JSON = "testCaseVerificationResults.json";
/** The list of categories that will be included in the regression test. */
private static final List CATEGORIES_INCLUDED_IN_TEST =
Arrays.asList(new String[] {"xss", "xxe"});
- // TODO: Since this is static, we might only be able to use (close) it once.
+ // TODO: Since these logs are static, we might only be able to use (close) it once.
+ static SimpleFileLogger tcJsonLogger;
static SimpleFileLogger ftcLogger;
+ /**
+ * Write a log file containing the verification results of all test cases, as JSON.
+ *
+ * @param results The verification results to log
+ * @param dataDir The directory to write the logfile to
+ * @param generate If true, generate log file, otherwise skip
+ * @throws IOException
+ * @throws LoggerConfigurationException
+ */
+ public static void genAllTCResultsToJsonFile(
+ TestCaseVerificationResultsCollection resultsCollection,
+ String dataDir,
+ boolean generate)
+ throws IOException, LoggerConfigurationException {
+
+ if (generate) {
+
+ final File FILE_TC_VERIF_RESULTS_JSON =
+ new File(dataDir, FILENAME_TC_VERIF_RESULTS_JSON);
+ SimpleFileLogger.setFile("TC_VERIF_RESULTS_JSON", FILE_TC_VERIF_RESULTS_JSON);
+
+ try (SimpleFileLogger tcJSON = SimpleFileLogger.getLogger("TC_VERIF_RESULTS_JSON")) {
+
+ tcJsonLogger = tcJSON;
+
+ // Create JSON version of verification results for ALL test cases
+ tcJsonLogger.println(Utils.objectToJson(resultsCollection));
+ } catch (JAXBException e) {
+ System.out.println("Fatal Error trying to convert verification results to JSON");
+ e.printStackTrace();
+ System.exit(-1);
+ }
+ }
+ }
/**
* Write a log file containing the details of all failed test cases.
@@ -100,10 +141,12 @@ public static void genFailedTCFile(List results, St
totalCount = results.size();
for (TestCaseVerificationResults result : results) {
- AbstractTestCaseRequest requestTemplate = result.getRequestTemplate();
+ // AbstractTestCaseRequest requestTemplate =
+ // result.getRequestTemplate();
+ TestCase testCase = result.getTestCase();
String sink = null;
- String sinkMetaDataFilePath = requestTemplate.getSinkFile();
+ String sinkMetaDataFilePath = testCase.getSinkFile();
if (sinkMetaDataFilePath != null) {
String sinkMetaDataFilename = new File(sinkMetaDataFilePath).getName();
sink = sinkMetaDataFilename.substring(0, sinkMetaDataFilename.indexOf('.'));
@@ -116,17 +159,17 @@ public static void genFailedTCFile(List results, St
undeclaredUnverifiable++;
if (sink == null) {
System.out.printf(
- "ERROR: No sink for request %s%n", requestTemplate.getName());
+ "ERROR: No sink for request %s%n", testCase.getName());
} else {
undeclaredUnverifiableSinks.add(sink);
}
}
} else {
if (result.isPassed()) {
- if (requestTemplate.isVulnerability()) truePositivePassedCount++;
+ if (testCase.isVulnerability()) truePositivePassedCount++;
else falsePositivePassedCount++;
} else {
- if (requestTemplate.isVulnerability()) truePositiveFailedCount++;
+ if (testCase.isVulnerability()) truePositiveFailedCount++;
else falsePositiveFailedCount++;
}
verifiedCount++;
@@ -135,26 +178,33 @@ public static void genFailedTCFile(List results, St
if (truePositiveFailedCount + falsePositiveFailedCount > 0) {
for (TestCaseVerificationResults result : results) {
- AbstractTestCaseRequest requestTemplate = result.getRequestTemplate();
- if (isIncludedInTest(requestTemplate)) {
+ TestCase testCase = result.getTestCase();
+ if (isIncludedInTest(testCase)) {
if (isVerbosityOn) {
System.out.println();
System.out.printf(
"Test case request %s (category: %s, isVulnerability: %b, isNonverifiable: %b, isPassed: %b)%n",
- requestTemplate.getName(),
- requestTemplate.getCategory().toString(),
- requestTemplate.isVulnerability(),
+ testCase.getName(),
+ testCase.getCategory().toString(),
+ testCase.isVulnerability(),
result.isUnverifiable(),
result.isPassed());
- System.out.println(requestTemplate.getFullURL());
+ HttpTestCaseInput httpTestCaseInput =
+ (HttpTestCaseInput) testCase.getTestCaseInput();
+ System.out.println(httpTestCaseInput.getUrl());
}
- if (!result.isUnverifiable() && !result.isPassed()) {
- System.out.printf(
- "FAILURE: %s positive %s test case request %s%n",
- requestTemplate.isVulnerability() ? "True" : "False",
- requestTemplate.getCategory().toString(),
- requestTemplate.getName());
+ if (!result.isUnverifiable()) {
+ if (result.isPassed()) {
+ testCase.setVerificationResult("VERIFIED");
+ } else {
+ testCase.setVerificationResult("FAILURE");
+ System.out.printf(
+ "FAILURE: %s positive %s test case request %s%n",
+ testCase.isVulnerability() ? "True" : "False",
+ testCase.getCategory().toString(),
+ testCase.getName());
+ }
}
}
}
@@ -162,11 +212,11 @@ public static void genFailedTCFile(List results, St
if (truePositiveFailedCount + falsePositiveFailedCount > 0) {
for (TestCaseVerificationResults result : results) {
- AbstractTestCaseRequest requestTemplate = result.getRequestTemplate();
- if (isIncludedInTest(requestTemplate)) {
+ TestCase testCase = result.getTestCase();
+ if (isIncludedInTest(testCase)) {
if (!result.isUnverifiable() && !result.isPassed()) {
ftcLogger.print("FAILURE: ");
- printTestCaseDetails(result, ftcLogger);
+ printTestCaseDetailsAsText(result, ftcLogger);
}
}
}
@@ -198,35 +248,59 @@ private static void printHttpRequest(HttpMessage request, Logger out) {
}
}
- private static void printTestCaseDetails(TestCaseVerificationResults result, Logger out) {
- AbstractTestCaseRequest requestTemplate = result.getRequestTemplate();
+ private static void printTestCaseDetailsAsText(TestCaseVerificationResults result, Logger out) {
+
+ TestCase testCase = result.getTestCase();
ResponseInfo attackResponseInfo = result.getResponseToAttackValue();
ResponseInfo safeResponseInfo = result.getResponseToSafeValue();
out.printf(
"%s positive %s test case request %s%n",
- requestTemplate.isVulnerability() ? "True" : "False",
- requestTemplate.getCategory().toString(),
- requestTemplate.getName());
- // Print out all the attributes of the request, including the templates used to create it
- out.println(requestTemplate.toString());
+ testCase.isVulnerability() ? "True" : "False",
+ testCase.getCategory().toString(),
+ testCase.getName());
+ // Print out all attributes of the request, including the templates used to create it
+ out.println(testCase.toString());
out.println();
out.println("Attack request:");
- printHttpRequest(result.getAttackRequest(), out);
- out.println();
- out.printf("Attack response: [%d]:%n", attackResponseInfo.getStatusCode());
- out.println(attackResponseInfo == null ? "null" : attackResponseInfo.getResponseString());
+ out.println(result.getAttackTestExecutorDescription());
+ if (attackResponseInfo instanceof HttpResponseInfo) {
+ out.printf(
+ "Attack response: [%d]:%n",
+ ((HttpResponseInfo) attackResponseInfo).getStatusCode());
+ out.println(
+ attackResponseInfo == null
+ ? "null"
+ : ((HttpResponseInfo) attackResponseInfo).getResponseString());
+ } else if (attackResponseInfo instanceof CliResponseInfo) {
+ out.printf(
+ "Attack response: [%d]:%n",
+ ((CliResponseInfo) attackResponseInfo).getStatusCode());
+ out.println(
+ attackResponseInfo == null
+ ? "null"
+ : ((CliResponseInfo) attackResponseInfo).getResponseString());
+ }
out.println();
out.println("Safe request:");
- printHttpRequest(result.getSafeRequest(), out);
- out.println();
- out.printf("Safe response: [%d]:%n", attackResponseInfo.getStatusCode());
- out.println(safeResponseInfo == null ? "null" : safeResponseInfo.getResponseString());
+ out.println(result.getSafeTestExecutorDescription());
+ if (safeResponseInfo instanceof HttpResponseInfo) {
+ out.printf(
+ "Safe response: [%d]:%n",
+ ((HttpResponseInfo) safeResponseInfo).getStatusCode());
+ out.println(
+ safeResponseInfo == null
+ ? "null"
+ : ((HttpResponseInfo) safeResponseInfo).getResponseString());
+ } else if (safeResponseInfo instanceof CliResponseInfo) {
+ out.printf(
+ "Safe response: [%d]:%n", ((CliResponseInfo) safeResponseInfo).getStatusCode());
+ out.println(
+ safeResponseInfo == null
+ ? "null"
+ : ((CliResponseInfo) safeResponseInfo).getResponseString());
+ }
out.println();
- String negatedAttackSuccessString =
- (requestTemplate.getAttackSuccessStringPresent() ? "" : "Failure ");
- out.printf(
- "Attack success %sindicator: -->%s<--%n",
- negatedAttackSuccessString, requestTemplate.getAttackSuccessString());
+ out.printf("Attack success indicator: -->%s<--%n", testCase.getAttackSuccessString());
out.printf("-----------------------------------------------------------%n%n");
}
@@ -320,40 +394,38 @@ public static void verifyTestCase(TestCaseVerificationResults result)
result.setUnverifiable(false); // Default
result.setDeclaredUnverifiable(false); // Default
- if (result.getRequestTemplate().isUnverifiable()) {
+ TestCase testCase = result.getTestCase();
+ if (testCase.isUnverifiable()) {
// Count this as "declared unverifiable" and return
result.setUnverifiable(true);
result.setDeclaredUnverifiable(true);
- } else if (result.getRequestTemplate().getAttackSuccessString() == null) {
+ } else if (testCase.getAttackSuccessString() == null) {
// Count this as "undeclared unverifiable" and return
result.setUnverifiable(true);
result.setDeclaredUnverifiable(false);
uLogger.print("UNVERIFIABLE: ");
- printTestCaseDetails(result, uLogger);
+ printTestCaseDetailsAsText(result, uLogger);
}
List reasons = new ArrayList<>();
String sink = null;
- String sinkMetaDataFilePath = result.getRequestTemplate().getSinkFile();
+ String sinkMetaDataFilePath = testCase.getSinkFile();
if (sinkMetaDataFilePath != null) {
String sinkMetaDataFilename = new File(sinkMetaDataFilePath).getName();
sink = sinkMetaDataFilename.substring(0, sinkMetaDataFilename.indexOf('.'));
}
if (!result.isUnverifiable()) {
- AbstractTestCaseRequest requestTemplate = result.getRequestTemplate();
boolean isAttackValueVerified =
verifyResponse(
result.getResponseToAttackValue().getResponseString(),
- requestTemplate.getAttackSuccessString(),
- requestTemplate.getAttackSuccessStringPresent());
+ testCase.getAttackSuccessString());
boolean isSafeValueVerified =
verifyResponse(
result.getResponseToSafeValue().getResponseString(),
- requestTemplate.getAttackSuccessString(),
- requestTemplate.getAttackSuccessStringPresent());
- if (result.getRequestTemplate().isVulnerability()) {
+ testCase.getAttackSuccessString());
+ if (testCase.isVulnerability()) {
// True positive success?
if (isAttackValueVerified) {
result.setPassed(true);
@@ -361,9 +433,8 @@ public static void verifyTestCase(TestCaseVerificationResults result)
ndLogger.printf(
"Non-discriminatory true positive test %s: The attack-success-string: \"%s\" was found in the response to both the safe and attack requests.%n"
+ "\tTo verify that a test case is a true positive, the attack-success-string should be in the attack response, and not%n\tthe safe response. Please change the attack-success-string and/or the test case sink itself to ensure that the%n\tattack-success-string response is present only in a response to a successful attack.%n",
- result.getRequestTemplate().getName(),
- result.getRequestTemplate().getAttackSuccessString());
- printTestCaseDetails(result, ndLogger);
+ testCase.getName(), testCase.getAttackSuccessString());
+ printTestCaseDetailsAsText(result, ndLogger);
nonDiscriminatorySinks.add(sink);
}
} else {
@@ -381,9 +452,8 @@ public static void verifyTestCase(TestCaseVerificationResults result)
ndLogger.printf(
"Non-discriminatory false positive test %s: The attack-success-string: \"%s\" was found in the response to the safe request.%n"
+ "\tTo verify that a test case is a false positive, the attack-success-string should not be in any response to this test%n\tcase. Please change the attack-success-string and/or the test case sink itself to ensure that the%n\tattack-success-string response is present only in a response to a successful attack.%n",
- result.getRequestTemplate().getName(),
- result.getRequestTemplate().getAttackSuccessString());
- printTestCaseDetails(result, ndLogger);
+ testCase.getName(), testCase.getAttackSuccessString());
+ printTestCaseDetailsAsText(result, ndLogger);
nonDiscriminatorySinks.add(sink);
}
}
@@ -395,17 +465,17 @@ public static void verifyTestCase(TestCaseVerificationResults result)
String compositeReason = "\t- " + String.join(", ", reasons);
- if (result.getRequestTemplate().isVulnerability()) {
+ if (testCase.isVulnerability()) {
truePositives++;
if (hasErrors) {
failedTruePositives++;
- failedTruePositivesList.put(result.getRequestTemplate(), compositeReason);
+ failedTruePositivesList.put(testCase, compositeReason);
}
} else {
falsePositives++;
if (hasErrors) {
failedFalsePositives++;
- failedFalsePositivesList.put(result.getRequestTemplate(), compositeReason);
+ failedFalsePositivesList.put(testCase, compositeReason);
}
}
}
@@ -423,8 +493,16 @@ private static List findErrors(ResponseInfo responseInfo, String prefix)
List reasons = new ArrayList<>();
if (responseInfo != null) {
- if (responseInfo.getStatusCode() != 200) {
- reasons.add(prefix + " response code: " + responseInfo.getStatusCode());
+ if (responseInfo instanceof HttpResponseInfo) {
+ int statusCode = ((HttpResponseInfo) responseInfo).getStatusCode();
+ if (statusCode != 200) {
+ reasons.add(prefix + " response code: " + statusCode);
+ }
+ } else if (responseInfo instanceof CliResponseInfo) {
+ int returnCode = ((CliResponseInfo) responseInfo).getStatusCode();
+ if (returnCode != 0) {
+ reasons.add(prefix + " response code: " + returnCode);
+ }
}
if (responseInfo.getResponseString().toLowerCase().contains("error")) {
reasons.add(prefix + " response contains: error");
@@ -442,21 +520,18 @@ private static List findErrors(ResponseInfo responseInfo, String prefix)
* @param response - The response from this test case.
* @param attackSuccessIndicator - The value to look for in the response to determine if the
* attack was successful.
- * @param attackSuccessStringPresent - boolean indicating if attack success indicator must be
- * present (or absent) to pass.
* @return true if the response passes the described checks. False otherwise.
*/
- public static boolean verifyResponse(
- String response, String attackSuccessIndicator, boolean attackSuccessStringPresent) {
+ public static boolean verifyResponse(String response, String attackSuccessIndicator) {
// Rip out any REFERER values
attackSuccessIndicator = attackSuccessIndicator.replace("REFERER", "");
- return (response.contains(attackSuccessIndicator) == attackSuccessStringPresent);
+ return response.contains(attackSuccessIndicator);
}
- private static boolean isIncludedInTest(AbstractTestCaseRequest testCaseRequestTemplate) {
- return CATEGORIES_INCLUDED_IN_TEST.contains(testCaseRequestTemplate.getCategory().getId())
- || (testCaseRequestTemplate.getAttackSuccessString() != null);
+ private static boolean isIncludedInTest(TestCase testCase) {
+ return CATEGORIES_INCLUDED_IN_TEST.contains(testCase.getCategory().getId())
+ || (testCase.getAttackSuccessString() != null);
}
}
diff --git a/plugin/src/main/java/org/owasp/benchmarkutils/helpers/TestCaseRequestFileParseException.java b/plugin/src/main/java/org/owasp/benchmarkutils/tools/TestCaseRequestFileParseException.java
similarity index 96%
rename from plugin/src/main/java/org/owasp/benchmarkutils/helpers/TestCaseRequestFileParseException.java
rename to plugin/src/main/java/org/owasp/benchmarkutils/tools/TestCaseRequestFileParseException.java
index 49c9ed57..68b10c9a 100644
--- a/plugin/src/main/java/org/owasp/benchmarkutils/helpers/TestCaseRequestFileParseException.java
+++ b/plugin/src/main/java/org/owasp/benchmarkutils/tools/TestCaseRequestFileParseException.java
@@ -15,7 +15,7 @@
* @author David Anderson
* @created 2021
*/
-package org.owasp.benchmarkutils.helpers;
+package org.owasp.benchmarkutils.tools;
public class TestCaseRequestFileParseException extends Exception {
diff --git a/plugin/src/main/java/org/owasp/benchmarkutils/tools/TestCaseVerificationResults.java b/plugin/src/main/java/org/owasp/benchmarkutils/tools/TestCaseVerificationResults.java
index 8e01abf7..c3ebb603 100644
--- a/plugin/src/main/java/org/owasp/benchmarkutils/tools/TestCaseVerificationResults.java
+++ b/plugin/src/main/java/org/owasp/benchmarkutils/tools/TestCaseVerificationResults.java
@@ -17,9 +17,14 @@
*/
package org.owasp.benchmarkutils.tools;
-import org.apache.hc.client5.http.classic.methods.HttpUriRequest;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import org.owasp.benchmarkutils.entities.ResponseInfo;
+import org.owasp.benchmarkutils.entities.TestCase;
/** Not a great class name. */
+@XmlRootElement(name = "TestCaseVerificationResult")
public class TestCaseVerificationResults {
private ResponseInfo responseToAttackValue;
@@ -32,22 +37,26 @@ public class TestCaseVerificationResults {
private boolean isPassed;
- private HttpUriRequest attackRequest;
+ private String attackTestExecutorDescription;
- private HttpUriRequest safeRequest;
+ private String safeTestExecutorDescription;
- private AbstractTestCaseRequest requestTemplate;
+ private TestCase testCase;
+
+ public TestCaseVerificationResults() {
+ super();
+ }
public TestCaseVerificationResults(
- HttpUriRequest attackRequest,
- HttpUriRequest safeRequest,
- AbstractTestCaseRequest requestTemplate,
+ String attackTestExecutorDescription,
+ String safeTestExecutorDescription,
+ TestCase testCase,
ResponseInfo responseToAttackValue,
ResponseInfo responseToSafeValue) {
this(
- attackRequest,
- safeRequest,
- requestTemplate,
+ attackTestExecutorDescription,
+ safeTestExecutorDescription,
+ testCase,
responseToAttackValue,
responseToSafeValue,
true,
@@ -56,18 +65,18 @@ public TestCaseVerificationResults(
}
public TestCaseVerificationResults(
- HttpUriRequest attackRequest,
- HttpUriRequest safeRequest,
- AbstractTestCaseRequest requestTemplate,
+ String attackTestExecutorDescription,
+ String safeTestExecutorDescription,
+ TestCase testCase,
ResponseInfo responseToAttackValue,
ResponseInfo responseToSafeValue,
boolean isUnverifiable,
boolean isDeclaredVerifiable,
boolean isPassed) {
super();
- this.attackRequest = attackRequest;
- this.safeRequest = safeRequest;
- this.requestTemplate = requestTemplate;
+ this.attackTestExecutorDescription = attackTestExecutorDescription;
+ this.safeTestExecutorDescription = safeTestExecutorDescription;
+ this.testCase = testCase;
this.responseToAttackValue = responseToAttackValue;
this.responseToSafeValue = responseToSafeValue;
this.isUnverifiable = isUnverifiable;
@@ -75,6 +84,7 @@ public TestCaseVerificationResults(
this.isPassed = isPassed;
}
+ @XmlElement(name = "AttackResponseInfo")
public ResponseInfo getResponseToAttackValue() {
return responseToAttackValue;
}
@@ -83,6 +93,7 @@ public void setResponseToAttackValue(ResponseInfo responseToAttackValue) {
this.responseToAttackValue = responseToAttackValue;
}
+ @XmlElement(name = "SafeResponseInfo", required = true)
public ResponseInfo getResponseToSafeValue() {
return responseToSafeValue;
}
@@ -91,6 +102,7 @@ public void setResponseToSafeValue(ResponseInfo responseToSafeValue) {
this.responseToSafeValue = responseToSafeValue;
}
+ @XmlAttribute(name = "Unverifiable")
public boolean isUnverifiable() {
return isUnverifiable;
}
@@ -99,6 +111,7 @@ public void setUnverifiable(boolean isUnverifiable) {
this.isUnverifiable = isUnverifiable;
}
+ @XmlAttribute(name = "DeclaredUnverifiable")
public boolean isDeclaredUnverifiable() {
return isDeclaredUnverifiable;
}
@@ -107,35 +120,39 @@ public void setDeclaredUnverifiable(boolean isDeclaredUnverifiable) {
this.isDeclaredUnverifiable = isDeclaredUnverifiable;
}
- public void setPassed(boolean isPassed) {
- this.isPassed = isPassed;
- }
-
+ @XmlAttribute(name = "Passed")
public boolean isPassed() {
return isPassed;
}
- public HttpUriRequest getAttackRequest() {
- return attackRequest;
+ public void setPassed(boolean isPassed) {
+ this.isPassed = isPassed;
+ }
+
+ @XmlElement(name = "AttackRequestInfo")
+ public String getAttackTestExecutorDescription() {
+ return attackTestExecutorDescription;
}
- public void setAttackRequest(HttpUriRequest attackRequest) {
- this.attackRequest = attackRequest;
+ public void setAttackTestExecutorDescription(String attackTestExecutor) {
+ this.attackTestExecutorDescription = attackTestExecutorDescription;
}
- public HttpUriRequest getSafeRequest() {
- return safeRequest;
+ @XmlElement(name = "SafeRequestInfo")
+ public String getSafeTestExecutorDescription() {
+ return safeTestExecutorDescription;
}
- public void setSafeRequest(HttpUriRequest safeRequest) {
- this.safeRequest = safeRequest;
+ public void setSafeTestExecutorDescription(String safeTestExecutor) {
+ this.safeTestExecutorDescription = safeTestExecutorDescription;
}
- public AbstractTestCaseRequest getRequestTemplate() {
- return requestTemplate;
+ @XmlElement(name = "TestCase")
+ public TestCase getTestCase() {
+ return testCase;
}
- public void setRequestTemplate(AbstractTestCaseRequest requestTemplate) {
- this.requestTemplate = requestTemplate;
+ public void setTestCase(TestCase testCase) {
+ this.testCase = testCase;
}
}
diff --git a/plugin/src/main/java/org/owasp/benchmarkutils/tools/TestCaseVerificationResultsCollection.java b/plugin/src/main/java/org/owasp/benchmarkutils/tools/TestCaseVerificationResultsCollection.java
new file mode 100644
index 00000000..38d301cb
--- /dev/null
+++ b/plugin/src/main/java/org/owasp/benchmarkutils/tools/TestCaseVerificationResultsCollection.java
@@ -0,0 +1,20 @@
+package org.owasp.benchmarkutils.tools;
+
+import java.util.List;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement(name = "TestCaseVerificationResultsCollection")
+public class TestCaseVerificationResultsCollection {
+
+ private List resultsObjects;
+
+ @XmlElement
+ public List getResultsObjects() {
+ return resultsObjects;
+ }
+
+ public void setResultsObjects(List resultsObjects) {
+ this.resultsObjects = resultsObjects;
+ }
+}
diff --git a/plugin/src/main/java/org/owasp/benchmarkutils/tools/TestExecutor.java b/plugin/src/main/java/org/owasp/benchmarkutils/tools/TestExecutor.java
new file mode 100644
index 00000000..03fc4443
--- /dev/null
+++ b/plugin/src/main/java/org/owasp/benchmarkutils/tools/TestExecutor.java
@@ -0,0 +1,31 @@
+/**
+ * OWASP Benchmark Project
+ *
+ *
This file is part of the Open Web Application Security Project (OWASP) Benchmark Project For
+ * details, please see https://owasp.org/www-project-benchmark/.
+ *
+ *
The OWASP Benchmark is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, version 2.
+ *
+ *
The OWASP Benchmark is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * @author David Anderson
+ * @created 2024
+ */
+package org.owasp.benchmarkutils.tools;
+
+import javax.validation.constraints.NotNull;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlSeeAlso;
+
+@XmlRootElement(name = "TestCaseRequest")
+@XmlSeeAlso({CliExecutor.class, HttpExecutor.class})
+public abstract class TestExecutor {
+ @XmlAttribute(name = "RequestDescription", required = true)
+ @NotNull
+ public abstract String getExecutorDescription();
+}
diff --git a/plugin/src/test/java/org/owasp/benchmarkutils/score/parsers/sarif/PrecautionReaderTest.java b/plugin/src/test/java/org/owasp/benchmarkutils/score/parsers/sarif/PrecautionReaderTest.java
index 2733c810..efb8ada4 100644
--- a/plugin/src/test/java/org/owasp/benchmarkutils/score/parsers/sarif/PrecautionReaderTest.java
+++ b/plugin/src/test/java/org/owasp/benchmarkutils/score/parsers/sarif/PrecautionReaderTest.java
@@ -28,7 +28,6 @@
import org.owasp.benchmarkutils.score.TestHelper;
import org.owasp.benchmarkutils.score.TestSuiteResults;
import org.owasp.benchmarkutils.score.parsers.ReaderTestBase;
-import org.owasp.benchmarkutils.score.parsers.sarif.PrecautionReader;
class PrecautionReaderTest extends ReaderTestBase {
diff --git a/plugin/src/test/java/org/owasp/benchmarkutils/score/parsers/sarif/SarifReaderTest.java b/plugin/src/test/java/org/owasp/benchmarkutils/score/parsers/sarif/SarifReaderTest.java
index 99455f0d..f5019161 100644
--- a/plugin/src/test/java/org/owasp/benchmarkutils/score/parsers/sarif/SarifReaderTest.java
+++ b/plugin/src/test/java/org/owasp/benchmarkutils/score/parsers/sarif/SarifReaderTest.java
@@ -21,7 +21,6 @@
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
-import org.owasp.benchmarkutils.score.parsers.sarif.SarifReader;
public class SarifReaderTest {
diff --git a/pom.xml b/pom.xml
old mode 100644
new mode 100755
index 8596b6d7..4d9654f7
--- a/pom.xml
+++ b/pom.xml
@@ -34,6 +34,64 @@
plugin
+
+
+
+ com.google.guava
+ guava
+ 33.1.0-jre
+
+
+
+ commons-lang
+ commons-lang
+ 2.6
+
+
+
+ javax.xml.bind
+ jaxb-api
+ 2.4.0-b180830.0359
+
+
+
+ org.apache.httpcomponents.client5
+ httpclient5
+ 5.3.1
+
+
+
+ org.apache.httpcomponents.core5
+ httpcore5
+ 5.2.4
+
+
+
+
+ org.eclipse.persistence
+ org.eclipse.persistence.core
+ ${version.eclipse.persistence}
+
+
+ org.eclipse.persistence
+ org.eclipse.persistence.moxy
+ ${version.eclipse.persistence}
+
+
+
+ xml-apis
+ xml-apis
+
+ 1.4.01
+
+
+ javax.json
+ javax.json-api
+ 1.1.4
+
+
+
+ benchmarkutils
@@ -358,6 +416,10 @@
UTF-811${project.build.directory}/log
+
+
+ 2.7.14
+
2.0.1