diff --git a/sechub-api-java/build.gradle b/sechub-api-java/build.gradle index e524be45e1..976f58ab00 100644 --- a/sechub-api-java/build.gradle +++ b/sechub-api-java/build.gradle @@ -20,6 +20,7 @@ dependencies { testImplementation spring_boot_dependency.junit_jupiter testImplementation library.apache_commons_io testImplementation library.mockito_inline + testImplementation library.wiremock } /* diff --git a/sechub-api-java/src/main/java/com/mercedesbenz/sechub/api/AbstractSecHubClient.java b/sechub-api-java/src/main/java/com/mercedesbenz/sechub/api/AbstractSecHubClient.java index 915b4df0a2..6aa0243310 100644 --- a/sechub-api-java/src/main/java/com/mercedesbenz/sechub/api/AbstractSecHubClient.java +++ b/sechub-api-java/src/main/java/com/mercedesbenz/sechub/api/AbstractSecHubClient.java @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT package com.mercedesbenz.sechub.api; import java.net.URI; @@ -18,8 +19,14 @@ public abstract class AbstractSecHubClient implements SecHubClient { private Set secHubClientListeners; - public AbstractSecHubClient() { - secHubClientListeners = new LinkedHashSet<>(); + public AbstractSecHubClient(URI serverUri, String username, String apiToken, boolean trustAll) { + this.serverUri = serverUri; + this.trustAll = trustAll; + + this.secHubClientListeners = new LinkedHashSet<>(); + + setUsername(username); + setApiToken(apiToken); } public void setUsername(String username) { @@ -30,14 +37,6 @@ public void setApiToken(String apiToken) { this.sealedApiToken = apiTokenAccess.seal(apiToken); } - public void setServerUri(URI serverUri) { - this.serverUri = serverUri; - } - - public void setTrustAll(boolean trustAll) { - this.trustAll = trustAll; - } - @Override public boolean isTrustAll() { return trustAll; diff --git a/sechub-api-java/src/main/java/com/mercedesbenz/sechub/api/DefaultSecHubClient.java b/sechub-api-java/src/main/java/com/mercedesbenz/sechub/api/DefaultSecHubClient.java index 4df61339bb..64d51bb5b9 100644 --- a/sechub-api-java/src/main/java/com/mercedesbenz/sechub/api/DefaultSecHubClient.java +++ b/sechub-api-java/src/main/java/com/mercedesbenz/sechub/api/DefaultSecHubClient.java @@ -70,10 +70,7 @@ public DefaultSecHubClient(URI serverUri, String username, String apiToken) { } public DefaultSecHubClient(URI serverUri, String username, String apiToken, boolean trustAll) { - setUsername(username); - setApiToken(apiToken); - setServerUri(serverUri); - setTrustAll(trustAll); + super(serverUri, username, apiToken, trustAll); apiClient = new ApiClientBuilder().createApiClient(this, mapper); @@ -442,27 +439,21 @@ public void approveJob(String projectId, UUID jobUUID) throws SecHubClientExcept private void runOrFail(RunOrFail failable, String failureMessage) throws SecHubClientException { try { failable.runOrFail(); - } catch (ApiException e) { + } catch (Exception e) { throw createClientException(failureMessage, e); } } - private SecHubClientException createClientException(String message, Exception cause) throws SecHubClientException { - return new SecHubClientException(message + " - " + cause.getMessage(), cause); - } - private T runOrFail(Callable callable, String failureMessage) throws SecHubClientException { try { return callable.call(); - } catch (ApiException e) { - throw createClientException(failureMessage, e); } catch (Exception e) { - if (e instanceof RuntimeException) { - RuntimeException re = (RuntimeException) e; - throw re; - } - throw new IllegalStateException("Unhandled exception - should not happen", e); + throw createClientException(failureMessage, e); } } + private SecHubClientException createClientException(String message, Exception cause) throws SecHubClientException { + return new SecHubClientException(message + " - " + cause.getMessage(), cause); + } + } diff --git a/sechub-api-java/src/main/java/com/mercedesbenz/sechub/api/ExecutionProfile.java b/sechub-api-java/src/main/java/com/mercedesbenz/sechub/api/ExecutionProfile.java index 95cecebff0..7f04d35f85 100644 --- a/sechub-api-java/src/main/java/com/mercedesbenz/sechub/api/ExecutionProfile.java +++ b/sechub-api-java/src/main/java/com/mercedesbenz/sechub/api/ExecutionProfile.java @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT package com.mercedesbenz.sechub.api; import java.util.HashSet; diff --git a/sechub-api-java/src/main/java/com/mercedesbenz/sechub/api/Job.java b/sechub-api-java/src/main/java/com/mercedesbenz/sechub/api/Job.java index 9655db8748..d9af73ce7f 100644 --- a/sechub-api-java/src/main/java/com/mercedesbenz/sechub/api/Job.java +++ b/sechub-api-java/src/main/java/com/mercedesbenz/sechub/api/Job.java @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT package com.mercedesbenz.sechub.api; import java.util.UUID; diff --git a/sechub-api-java/src/main/java/com/mercedesbenz/sechub/api/MockedSecHubClient.java b/sechub-api-java/src/main/java/com/mercedesbenz/sechub/api/MockedSecHubClient.java index 615fcb082b..58c28d8982 100644 --- a/sechub-api-java/src/main/java/com/mercedesbenz/sechub/api/MockedSecHubClient.java +++ b/sechub-api-java/src/main/java/com/mercedesbenz/sechub/api/MockedSecHubClient.java @@ -1,5 +1,7 @@ +// SPDX-License-Identifier: MIT package com.mercedesbenz.sechub.api; +import java.net.URI; import java.nio.file.Path; import java.util.ArrayList; import java.util.HashMap; @@ -33,6 +35,7 @@ public class MockedSecHubClient extends AbstractSecHubClient { private Set userToProjectAssignments = new HashSet<>(); public MockedSecHubClient() { + super(URI.create("https://localhost/mocked-sechub"), "mockuser", "pseudo-token", true); mockDataAccess = new MockDataAccess(); } diff --git a/sechub-api-java/src/main/java/com/mercedesbenz/sechub/api/SecHubClient.java b/sechub-api-java/src/main/java/com/mercedesbenz/sechub/api/SecHubClient.java index 9473190224..badac03999 100644 --- a/sechub-api-java/src/main/java/com/mercedesbenz/sechub/api/SecHubClient.java +++ b/sechub-api-java/src/main/java/com/mercedesbenz/sechub/api/SecHubClient.java @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT package com.mercedesbenz.sechub.api; import java.net.URI; diff --git a/sechub-api-java/src/main/java/com/mercedesbenz/sechub/api/User.java b/sechub-api-java/src/main/java/com/mercedesbenz/sechub/api/User.java index 7ceed78343..30bbe30321 100644 --- a/sechub-api-java/src/main/java/com/mercedesbenz/sechub/api/User.java +++ b/sechub-api-java/src/main/java/com/mercedesbenz/sechub/api/User.java @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT package com.mercedesbenz.sechub.api; public class User { diff --git a/sechub-api-java/src/main/java/com/mercedesbenz/sechub/api/internal/ApiClientBuilder.java b/sechub-api-java/src/main/java/com/mercedesbenz/sechub/api/internal/ApiClientBuilder.java index ca1a2a9aff..22b9ef3e7e 100644 --- a/sechub-api-java/src/main/java/com/mercedesbenz/sechub/api/internal/ApiClientBuilder.java +++ b/sechub-api-java/src/main/java/com/mercedesbenz/sechub/api/internal/ApiClientBuilder.java @@ -1,15 +1,18 @@ // SPDX-License-Identifier: MIT package com.mercedesbenz.sechub.api.internal; +import java.net.Socket; import java.net.http.HttpClient; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; +import java.util.Base64; import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; import javax.net.ssl.TrustManager; -import javax.net.ssl.X509TrustManager; +import javax.net.ssl.X509ExtendedTrustManager; import com.fasterxml.jackson.databind.ObjectMapper; import com.mercedesbenz.sechub.api.SecHubClient; @@ -18,36 +21,30 @@ public class ApiClientBuilder { public ApiClient createApiClient(SecHubClient client, ObjectMapper mapper) { - HttpClient.Builder builder = HttpClient.newBuilder().authenticator(new SecHubClientAuthenticator(client)); + HttpClient.Builder builder = HttpClient.newBuilder(); if (client.isTrustAll()) { builder.sslContext(createTrustAllSSLContext()); } + ApiClient apiClient = new ApiClient(builder, mapper, client.getServerUri().toString()); + apiClient.setRequestInterceptor((request) -> { + request.setHeader("Authorization", createBasicAuthenticationHeader(client)); + }); return apiClient; } + private static final String createBasicAuthenticationHeader(SecHubClient client) { + String valueToEncode = client.getUsername() + ":" + client.getSealedApiToken(); + return "Basic " + Base64.getEncoder().encodeToString(valueToEncode.getBytes()); + } + private SSLContext createTrustAllSSLContext() { SSLContext sslContext = null; try { sslContext = SSLContext.getInstance("TLS"); - TrustManager trustManager = new X509TrustManager() { - - private X509Certificate[] emptyCertificatesArray = new X509Certificate[] {}; - - public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { - /* we do not check the client - we trust all */ - } - - public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { - /* we do not check the server - we trust all */ - } - - public X509Certificate[] getAcceptedIssuers() { - return emptyCertificatesArray; - } - }; + TrustManager trustManager = new TrustAllManager(); sslContext.init(null, new TrustManager[] { trustManager }, null); @@ -57,4 +54,42 @@ public X509Certificate[] getAcceptedIssuers() { } } + + private class TrustAllManager extends X509ExtendedTrustManager { + + private X509Certificate[] emptyCertificatesArray = new X509Certificate[] {}; + + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + /* we do not check - we trust all */ + } + + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + /* we do not check - we trust all */ + } + + public X509Certificate[] getAcceptedIssuers() { + return emptyCertificatesArray; + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { + /* we do not check - we trust all */ + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { + /* we do not check - we trust all */ + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { + /* we do not check - we trust all */ + + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { + /* we do not check - we trust all */ + } + }; } diff --git a/sechub-api-java/src/main/java/com/mercedesbenz/sechub/api/internal/SecHubClientAuthenticator.java b/sechub-api-java/src/main/java/com/mercedesbenz/sechub/api/internal/SecHubClientAuthenticator.java deleted file mode 100644 index 64ccc526cb..0000000000 --- a/sechub-api-java/src/main/java/com/mercedesbenz/sechub/api/internal/SecHubClientAuthenticator.java +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: MIT -package com.mercedesbenz.sechub.api.internal; - -import java.net.Authenticator; -import java.net.PasswordAuthentication; - -import com.mercedesbenz.sechub.api.SecHubClient; - -public class SecHubClientAuthenticator extends Authenticator { - private PasswordAuthentication paswordAuthentication; - - public SecHubClientAuthenticator(SecHubClient client) { - this.paswordAuthentication = new PasswordAuthentication(client.getUsername(), client.getSealedApiToken().toCharArray()); - } - - @Override - protected PasswordAuthentication getPasswordAuthentication() { - return paswordAuthentication; - } -} \ No newline at end of file diff --git a/sechub-api-java/src/test/java/com/mercedesbenz/sechub/api/DefaultSechubClientWireMockTest.java b/sechub-api-java/src/test/java/com/mercedesbenz/sechub/api/DefaultSechubClientWireMockTest.java new file mode 100644 index 0000000000..ef430212bd --- /dev/null +++ b/sechub-api-java/src/test/java/com/mercedesbenz/sechub/api/DefaultSechubClientWireMockTest.java @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: MIT +package com.mercedesbenz.sechub.api; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.*; +import static org.junit.Assert.*; + +import java.net.URI; + +import org.junit.Rule; +import org.junit.Test; + +import com.github.tomakehurst.wiremock.junit.WireMockRule; +import com.mercedesbenz.sechub.test.TestPortProvider; + +import wiremock.org.apache.http.HttpStatus; + +/** + * Junit 4 test because of missing official WireMock Junit5 extension - so we + * use WireMock Rule and Junit4. + * + * @author Albert Tregnaghi + * + */ +public class DefaultSechubClientWireMockTest { + + private static final String EXAMPLE_TOKEN = "example-token"; + + private static final String EXAMPLE_USER = "example-user"; + private static final String APPLICATION_JSON = "application/json"; + + private static final int HTTPS_PORT = TestPortProvider.DEFAULT_INSTANCE.getWireMockTestHTTPSPort(); + + private static final int HTTP_PORT = TestPortProvider.DEFAULT_INSTANCE.getWireMockTestHTTPPort(); + + @Rule + public WireMockRule wireMockRule = new WireMockRule(wireMockConfig().port(HTTP_PORT).httpsPort(HTTPS_PORT)); + + @Test + public void fetch_sechub_status_with_basic_auth() throws Exception { + + /* prepare */ + String statusBody = """ + [ { + "key" : "status.scheduler.enabled", + "value" : "true" + }, { + "key" : "status.scheduler.jobs.all", + "value" : "2" + } ] + """; + stubFor(get(urlEqualTo("/api/admin/status")).withBasicAuth(EXAMPLE_USER, EXAMPLE_TOKEN) + .willReturn(aResponse().withStatus(HttpStatus.SC_OK).withHeader("Content-Type", APPLICATION_JSON).withBody(statusBody))); + + DefaultSecHubClient client = createTestClientWithExampleCredentials(); + + /* execute */ + SecHubStatus status = client.fetchSecHubStatus(); + + /* test */ + verify(getRequestedFor(urlEqualTo("/api/admin/status"))); + assertNotNull(status); + assertEquals("true", status.getStatusInformationMap().get("status.scheduler.enabled")); + assertEquals("2", status.getStatusInformationMap().get("status.scheduler.jobs.all")); + + } + + @Test + public void credential_changes_on_client_after_creation_are_possible() throws Exception { + + /* prepare */ + String statusBody = """ + [ { + "key" : "status.scheduler.enabled", + "value" : "false" + }, { + "key" : "status.scheduler.jobs.all", + "value" : "0" + } ] + """; + stubFor(get(urlEqualTo("/api/admin/status")).withBasicAuth("other-user", "other-token") + .willReturn(aResponse().withStatus(HttpStatus.SC_OK).withHeader("Content-Type", APPLICATION_JSON).withBody(statusBody))); + + DefaultSecHubClient client = createTestClientWithExampleCredentials(); + + /* execute */ + client.setApiToken("other-token"); + client.setUsername("other-user"); + + /* test */ + SecHubStatus status = client.fetchSecHubStatus(); + verify(getRequestedFor(urlEqualTo("/api/admin/status"))); + + assertEquals("false", status.getStatusInformationMap().get("status.scheduler.enabled")); + assertEquals("0", status.getStatusInformationMap().get("status.scheduler.jobs.all")); + } + + private DefaultSecHubClient createTestClientWithExampleCredentials() { + DefaultSecHubClient client = new DefaultSecHubClient(URI.create(wireMockRule.baseUrl()), EXAMPLE_USER, EXAMPLE_TOKEN, true); + return client; + } +} diff --git a/sechub-api-java/src/test/java/com/mercedesbenz/sechub/api/MockedSecHubClientTest.java b/sechub-api-java/src/test/java/com/mercedesbenz/sechub/api/MockedSecHubClientTest.java index 57df05b663..05639da7bb 100644 --- a/sechub-api-java/src/test/java/com/mercedesbenz/sechub/api/MockedSecHubClientTest.java +++ b/sechub-api-java/src/test/java/com/mercedesbenz/sechub/api/MockedSecHubClientTest.java @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT package com.mercedesbenz.sechub.api; import static org.junit.jupiter.api.Assertions.*; diff --git a/sechub-commons-archive/src/main/java/com/mercedesbenz/sechub/commons/archive/ArchiveSupport.java b/sechub-commons-archive/src/main/java/com/mercedesbenz/sechub/commons/archive/ArchiveSupport.java index b57b288455..f7208bfb28 100644 --- a/sechub-commons-archive/src/main/java/com/mercedesbenz/sechub/commons/archive/ArchiveSupport.java +++ b/sechub-commons-archive/src/main/java/com/mercedesbenz/sechub/commons/archive/ArchiveSupport.java @@ -168,6 +168,7 @@ private File createArchive(ArchiveType archiveType, SecHubConfigurationModel con @SuppressWarnings("resource") TarArchiveOutputStream tarOutputStream = (TarArchiveOutputStream) outputStream; tarOutputStream.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX); + tarOutputStream.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_POSIX); } Map uniqueNameToPaths = creationContext.getUniqueToPathsMap(); diff --git a/sechub-commons-model/src/main/java/com/mercedesbenz/sechub/commons/model/SecHubConfigurationModelValidationError.java b/sechub-commons-model/src/main/java/com/mercedesbenz/sechub/commons/model/SecHubConfigurationModelValidationError.java index 157e5d2928..93d0978a48 100644 --- a/sechub-commons-model/src/main/java/com/mercedesbenz/sechub/commons/model/SecHubConfigurationModelValidationError.java +++ b/sechub-commons-model/src/main/java/com/mercedesbenz/sechub/commons/model/SecHubConfigurationModelValidationError.java @@ -29,6 +29,10 @@ public enum SecHubConfigurationModelValidationError { WEB_SCAN_NO_HEADER_VALUE_DEFINED("The value for a HTTP header is not defined!"), + WEB_SCAN_INCLUDE_INVALID("The value of an include is invalid!"), + + WEB_SCAN_EXCLUDE_INVALID("The value of an exclude is invalid!"), + WEB_SCAN_HTTP_HEADER_ONLY_FOR_URL_IS_NOT_A_VALID_URL("The URL for a HTTP header is not a valid URL!"), WEB_SCAN_HTTP_HEADER_ONLY_FOR_URL_DOES_NOT_CONTAIN_TARGET_URL("The URL for a HTTP header does not contain the base URL that shall be scanned!"), diff --git a/sechub-commons-model/src/main/java/com/mercedesbenz/sechub/commons/model/SecHubConfigurationModelValidator.java b/sechub-commons-model/src/main/java/com/mercedesbenz/sechub/commons/model/SecHubConfigurationModelValidator.java index 6e21832d43..fd81cc8652 100644 --- a/sechub-commons-model/src/main/java/com/mercedesbenz/sechub/commons/model/SecHubConfigurationModelValidator.java +++ b/sechub-commons-model/src/main/java/com/mercedesbenz/sechub/commons/model/SecHubConfigurationModelValidator.java @@ -10,6 +10,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; @@ -35,6 +36,10 @@ public class SecHubConfigurationModelValidator { private static final int MAX_METADATA_LABEL_VALUE_LENGTH = 150; private static final int MAX_METADATA_LABEL_AMOUNT = 20; + private static final int MAX_LIST_SIZE_INCLUDES = 500; + private static final int MAX_LIST_SIZE_EXCLUDES = 500; + private static final int MAX_LENGTH_PATH_SIZE = 2048; + SecHubConfigurationModelSupport modelSupport = new SecHubConfigurationModelSupport(); private List supportedVersions; @@ -262,18 +267,36 @@ private void handleWebScanConfiguration(InternalValidationContext context) { if (SimpleNetworkUtils.isURINullOrEmpty(uri)) { context.result.addError(WEB_SCAN_HAS_NO_URL_DEFINED); + return; } else if (!SimpleNetworkUtils.isHttpProtocol(uri)) { String schema = SimpleStringUtils.truncateWhenTooLong(uri.getScheme(), 5); context.result.addError(WEB_SCAN_URL_HAS_UNSUPPORTED_SCHEMA, "Schema was: " + schema + " but supported is only HTTP/HTTPS"); + return; } + handleIncludesAndExcludes(context, webScan); handleApi(context, webScan); handleHTTPHeaders(context, webScan); } + private void handleIncludesAndExcludes(InternalValidationContext context, SecHubWebScanConfiguration webScan) { + String targetUrl = webScan.getUrl().toString(); + WebScanConfigurationModelValidationContext webScanContext = new WebScanConfigurationModelValidationContext(context, + addTrailingSlashToUrlWhenMissingAndLowerCase(targetUrl), Collections.emptyList()); + + if (webScan.getExcludes().isPresent()) { + List excludes = webScan.getExcludes().get(); + validateExcludesOrIncludes(webScanContext, excludes, false); + } + if (webScan.getIncludes().isPresent()) { + List includes = webScan.getIncludes().get(); + validateExcludesOrIncludes(webScanContext, includes, true); + } + } + private void handleApi(InternalValidationContext context, SecHubWebScanConfiguration webScan) { Optional apiOpt = webScan.getApi(); if (!apiOpt.isPresent()) { @@ -300,6 +323,66 @@ private void handleHTTPHeaders(InternalValidationContext context, SecHubWebScanC validateHeaderOnlyForUrlNotDuplicated(webScanContext); } + private void validateExcludesOrIncludes(WebScanConfigurationModelValidationContext webScanContext, List urlList, boolean include) { + String term = "excludes"; + SecHubConfigurationModelValidationError validationError = WEB_SCAN_EXCLUDE_INVALID; + int maxListSize = MAX_LIST_SIZE_EXCLUDES; + + if (include) { + term = "includes"; + validationError = WEB_SCAN_INCLUDE_INVALID; + maxListSize = MAX_LIST_SIZE_INCLUDES; + } + + if (urlList.size() > maxListSize) { + webScanContext.markAsFailed(validationError, "A maximum of " + maxListSize + " " + term + " are allowed."); + return; + } + + for (String subUrlPattern : urlList) { + if (subUrlPattern.length() > MAX_LENGTH_PATH_SIZE) { + subUrlPattern = subUrlPattern.substring(0, MAX_LENGTH_PATH_SIZE); + webScanContext.markAsFailed(validationError, "Maximum URL length is " + MAX_LENGTH_PATH_SIZE + " characters. The first " + MAX_LENGTH_PATH_SIZE + + " characters of the URL in question: " + subUrlPattern); + return; + } + // we do not return if one include/exclude was wrong, + // to be able to tell the user all wrong includes and excludes + validateIncludeOrExcludePattern(webScanContext, subUrlPattern, include); + } + } + + private void validateIncludeOrExcludePattern(WebScanConfigurationModelValidationContext webScanContext, String subUrlPattern, boolean include) { + if (subUrlPattern.contains("//")) { + if (include) { + webScanContext.markAsFailed(WEB_SCAN_INCLUDE_INVALID, "The include: " + subUrlPattern + " contains '//'!"); + } else { + webScanContext.markAsFailed(WEB_SCAN_EXCLUDE_INVALID, "The exclude: " + subUrlPattern + " contains '//'!"); + } + return; + } + + String urlToCheck = webScanContext.sanatizedTargetUrl; + if (subUrlPattern.startsWith("/")) { + urlToCheck += subUrlPattern.substring(1); + } else { + urlToCheck += subUrlPattern; + } + + String createdIncludeOrExcludeUrl = createUrlWithoutWildCards(urlToCheck); + try { + new URI(createdIncludeOrExcludeUrl).toURL(); + } catch (URISyntaxException | MalformedURLException e) { + if (include) { + webScanContext.markAsFailed(WEB_SCAN_INCLUDE_INVALID, + "The include: " + subUrlPattern + " does create an invalid URL without the wild cards : " + createdIncludeOrExcludeUrl); + } else { + webScanContext.markAsFailed(WEB_SCAN_EXCLUDE_INVALID, + "The exclude: " + subUrlPattern + " does create an invalid URL without the wild cards : " + createdIncludeOrExcludeUrl); + } + } + } + private void validateHeaderOnlyForUrlNotDuplicated(WebScanConfigurationModelValidationContext webScanContext) { if (webScanContext.failed) { return; @@ -375,14 +458,14 @@ private String addTrailingSlashToUrlWhenMissingAndLowerCase(String url) { return resultUrl.toLowerCase(); } - private String createLowerCasedUrlAndAddTrailingSlashIfMissing(String onlyForUrl) { + private String createLowerCasedUrlAndAddTrailingSlashIfMissing(String url) { // ensure "https://mywebapp.com/" and "https://mywebapp.com" are accepted as the // same. This way we can check if this URL contains our scan target URL. - return addTrailingSlashToUrlWhenMissingAndLowerCase(onlyForUrl); + return addTrailingSlashToUrlWhenMissingAndLowerCase(url); } - private String createUrlWithoutWildCards(String onlyForUrl) { - return PATTERN_QUOTED_WEBSCAN_URL_WILDCARD_SYMBOL.matcher(onlyForUrl).replaceAll(""); + private String createUrlWithoutWildCards(String url) { + return PATTERN_QUOTED_WEBSCAN_URL_WILDCARD_SYMBOL.matcher(url).replaceAll(""); } private void handleInfraScanConfiguration(InternalValidationContext context) { diff --git a/sechub-commons-model/src/test/java/com/mercedesbenz/sechub/commons/model/SecHubConfigurationModelValidatorTest.java b/sechub-commons-model/src/test/java/com/mercedesbenz/sechub/commons/model/SecHubConfigurationModelValidatorTest.java index f9e0c6c5c2..e39353d73a 100644 --- a/sechub-commons-model/src/test/java/com/mercedesbenz/sechub/commons/model/SecHubConfigurationModelValidatorTest.java +++ b/sechub-commons-model/src/test/java/com/mercedesbenz/sechub/commons/model/SecHubConfigurationModelValidatorTest.java @@ -1,15 +1,22 @@ // SPDX-License-Identifier: MIT package com.mercedesbenz.sechub.commons.model; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import java.net.InetAddress; import java.net.URI; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashSet; +import java.util.LinkedList; import java.util.List; import java.util.Optional; import java.util.Set; @@ -1320,6 +1327,176 @@ void explicit_definitions_for_the_same_header_for_certain_urls_but_list_of_urls_ assertHasError(result, SecHubConfigurationModelValidationError.WEB_SCAN_NON_UNIQUE_HEADER_CONFIGURATION); } + @ParameterizedTest + @EmptySource + @ValueSource(strings = { "/", "<*>", "/<*>", "<*>/<*>", "/en/contacts", "/en/contacts/<*>", "<*>/en/contacts/<*>", "<*>/en/<*>/contacts/<*>", + "<*>/en/<*>/<*>/contacts/<*>", "<*>/en<*><*>contacts/<*>", "en/contacts/<*>", "en/contacts", "en/contacts/" }) + void valid_include_and_exclude_has_no_errors(String includeExcludeEntry) { + /* prepare */ + List entryAsList = Arrays.asList(includeExcludeEntry); + SecHubScanConfiguration sechubConfiguration = createSecHubConfigurationWithWebScanPart(); + + sechubConfiguration.getWebScan().get().excludes = Optional.of(entryAsList); + sechubConfiguration.getWebScan().get().includes = Optional.of(entryAsList); + + modelSupportCollectedScanTypes.add(ScanType.WEB_SCAN); + + /* execute */ + SecHubConfigurationModelValidationResult result = validatorToTest.validate(sechubConfiguration); + + /* test */ + assertHasNoErrors(result); + } + + @ParameterizedTest + @ValueSource(strings = { "//en/contacts", "/en//contacts", "/en/contacts//", "/en/ contacts/" }) + void double_slashes_include_exclude_has_errors(String includeExcludeEntry) { + /* prepare */ + List entryAsList = Arrays.asList(includeExcludeEntry); + SecHubScanConfiguration sechubConfiguration = createSecHubConfigurationWithWebScanPart(); + sechubConfiguration.getWebScan().get().excludes = Optional.of(entryAsList); + sechubConfiguration.getWebScan().get().includes = Optional.of(entryAsList); + + modelSupportCollectedScanTypes.add(ScanType.WEB_SCAN); + + /* execute */ + SecHubConfigurationModelValidationResult result = validatorToTest.validate(sechubConfiguration); + /* test */ + assertHasError(result, SecHubConfigurationModelValidationError.WEB_SCAN_EXCLUDE_INVALID); + assertHasError(result, SecHubConfigurationModelValidationError.WEB_SCAN_INCLUDE_INVALID); + } + + @ParameterizedTest + @ValueSource(strings = { " ", " /en/contacts", "/en/ contacts/", "/en/contacts " }) + void spaces_in_include_exclude_has_errors(String includeExcludeEntry) { + /* prepare */ + List entryAsList = Arrays.asList(includeExcludeEntry); + SecHubScanConfiguration sechubConfiguration = createSecHubConfigurationWithWebScanPart(); + sechubConfiguration.getWebScan().get().excludes = Optional.of(entryAsList); + sechubConfiguration.getWebScan().get().includes = Optional.of(entryAsList); + + modelSupportCollectedScanTypes.add(ScanType.WEB_SCAN); + + /* execute */ + SecHubConfigurationModelValidationResult result = validatorToTest.validate(sechubConfiguration); + /* test */ + assertHasError(result, SecHubConfigurationModelValidationError.WEB_SCAN_EXCLUDE_INVALID); + assertHasError(result, SecHubConfigurationModelValidationError.WEB_SCAN_INCLUDE_INVALID); + } + + @Test + void too_many_excludes_results_in_error() { + List excludes = createListWithTooManyIncludesOrExcludes(); + + SecHubScanConfiguration sechubConfiguration = createSecHubConfigurationWithWebScanPart(); + sechubConfiguration.getWebScan().get().excludes = Optional.of(excludes); + + modelSupportCollectedScanTypes.add(ScanType.WEB_SCAN); + + /* execute */ + SecHubConfigurationModelValidationResult result = validatorToTest.validate(sechubConfiguration); + + /* test */ + assertHasError(result, SecHubConfigurationModelValidationError.WEB_SCAN_EXCLUDE_INVALID); + } + + @Test + void too_many_includes_results_in_error() { + /* prepare */ + List includes = createListWithTooManyIncludesOrExcludes(); + + SecHubScanConfiguration sechubConfiguration = createSecHubConfigurationWithWebScanPart(); + sechubConfiguration.getWebScan().get().includes = Optional.of(includes); + + modelSupportCollectedScanTypes.add(ScanType.WEB_SCAN); + + /* execute */ + SecHubConfigurationModelValidationResult result = validatorToTest.validate(sechubConfiguration); + + /* test */ + assertHasError(result, SecHubConfigurationModelValidationError.WEB_SCAN_INCLUDE_INVALID); + } + + @Test + void exclude_too_long_results_in_error() { + /* prepare */ + List excludes = createTooLongIncludeOrExcludeEntry(); + + SecHubScanConfiguration sechubConfiguration = createSecHubConfigurationWithWebScanPart(); + sechubConfiguration.getWebScan().get().excludes = Optional.of(excludes); + + modelSupportCollectedScanTypes.add(ScanType.WEB_SCAN); + + /* execute */ + SecHubConfigurationModelValidationResult result = validatorToTest.validate(sechubConfiguration); + + /* test */ + assertHasError(result, SecHubConfigurationModelValidationError.WEB_SCAN_EXCLUDE_INVALID); + } + + @Test + void include_too_long_results_in_error() { + /* prepare */ + List includes = createTooLongIncludeOrExcludeEntry(); + + SecHubScanConfiguration sechubConfiguration = createSecHubConfigurationWithWebScanPart(); + sechubConfiguration.getWebScan().get().includes = Optional.of(includes); + + modelSupportCollectedScanTypes.add(ScanType.WEB_SCAN); + + /* execute */ + SecHubConfigurationModelValidationResult result = validatorToTest.validate(sechubConfiguration); + + /* test */ + assertHasError(result, SecHubConfigurationModelValidationError.WEB_SCAN_INCLUDE_INVALID); + } + + @Test + void can_read_sechub_web_scan_config_with_wildcards() { + /* prepare */ + String json = TestFileReader.loadTextFile("src/test/resources/sechub_config_web_scan_includes_excludes_with_wildcards.json"); + SecHubScanConfiguration sechubConfiguration = SecHubScanConfiguration.createFromJSON(json); + + modelSupportCollectedScanTypes.add(ScanType.WEB_SCAN); + + /* execute */ + SecHubConfigurationModelValidationResult result = validatorToTest.validate(sechubConfiguration); + + /* test */ + assertHasNoErrors(result); + } + + private SecHubScanConfiguration createSecHubConfigurationWithWebScanPart() { + SecHubWebScanConfiguration webScanConfig = new SecHubWebScanConfiguration(); + webScanConfig.url = URI.create("https://www.gamechanger.example.org/"); + + SecHubScanConfiguration sechubConfiguration = new SecHubScanConfiguration(); + sechubConfiguration.setApiVersion("1.0"); + sechubConfiguration.setWebScan(webScanConfig); + return sechubConfiguration; + } + + private List createListWithTooManyIncludesOrExcludes() { + List list = new LinkedList<>(); + for (int i = 1; i <= 501; i++) { + list.add("/myapp" + i); + } + return list; + } + + private List createTooLongIncludeOrExcludeEntry() { + StringBuilder sb = new StringBuilder(); + sb.append("/"); + + for (int i = 0; i < 64; i++) { + sb.append("abcdefghijklmnopqrstuvwxyz012345"); + } + + List list = new LinkedList<>(); + list.add(sb.toString()); + return list; + } + private SecHubWebScanConfiguration createWebScanConfigurationWithHeader(String targetUrl, String onlyForUrl) { String headerName = "Authorization"; String headerValue = "secret-key"; diff --git a/sechub-commons-model/src/test/resources/sechub_config_web_scan_includes_excludes_with_wildcards.json b/sechub-commons-model/src/test/resources/sechub_config_web_scan_includes_excludes_with_wildcards.json new file mode 100644 index 0000000000..a55f2b044d --- /dev/null +++ b/sechub-commons-model/src/test/resources/sechub_config_web_scan_includes_excludes_with_wildcards.json @@ -0,0 +1,8 @@ +{ + "apiVersion" : "1.0", + "webScan" : { + "url" : "https://www.gamechanger.example.org", + "includes" : [ "/special/include", "/special/include/<*>", "<*>/special/<*>/include/<*>", "<*>/special/include/<*>", "special/include/<*>", "special/include", "special/include/" ], + "excludes" : [ "/en/contact", "/en/contacts/<*>", "<*>/en/<*>/contacts/<*>", "<*>/en/contacts/<*>", "en/contacts/<*>", "en/contacts", "en/contacts/" ] + } +} \ No newline at end of file diff --git a/sechub-doc/src/docs/asciidoc/documents/shared/configuration/sechub_config.adoc b/sechub-doc/src/docs/asciidoc/documents/shared/configuration/sechub_config.adoc index dbc82da3b0..4bdafef0d6 100644 --- a/sechub-doc/src/docs/asciidoc/documents/shared/configuration/sechub_config.adoc +++ b/sechub-doc/src/docs/asciidoc/documents/shared/configuration/sechub_config.adoc @@ -179,16 +179,27 @@ include::sechub_config_example2_webscan_anonyous.json[] <1> Define web scan <2> The `URL` to scan. This `URL` must be whitelisted in `{sechub}` project. Normally without a slash `/` at the end. <3> *Optional*: Define includes, if you have a special path that is linked nowhere, - so the scanner can not detect it automatically while crawling the application. - Always use them starting with a slash (`/`) because they are interpreted relative to the `URL` provided before. + so the scanner can not detect it automatically while crawling the application. You can use wildcards by using the symbol `<*>` like in the example above. + To make the scan work the target URL will always be implicitly included with `"https://www.gamechanger.example.org<*>"` if no includes are specified. If includes are specified the scan is limited to this includes. + - Includes starting with a slash (`/`) like `"includes": [ "/special/include","/special/include/<*>"]` they are interpreted relative to the scan target `URL` provided before. + - Includes not starting with a slash (`/`) like `"includes": [ "<*>/en/contacts/<*>","en/contacts/<*>","en/contacts","en/contacts/"`] are interpreted as enclosed by wildcards like the first include in the list example: `"<*>/en/contacts/<*>"`. <4> *Optional*: Define excludes, if you have a special path you want to exclude, from the scan. - Always use them starting with a slash (`/`) because they are interpreted relative to the `URL` provided before. + You can use excludes the same way you can use the includes. + Excludes do always overwrite includes if the provided patterns for includes and excludes do have intersections. <5> *Optional*: Define the maximum duration the scan can take. Scanning a "large" web page/application can take several hours or even days. This option ensures the scan will only run for a limited time. <6> Define the unit of time. The unit can be: `millisecond`, `second`, `minute`, `hour`, `day`. +[CAUTION] +==== +Includes are a different from excludes looking at wildcards, because in includes they might not be resolved properly, if the pages behind the wildcards cannot be detected by a web crawler. + +If you only want to scan a specific part of your application e.g only the customers section `https://my-application.com/customer/`, +you can specify the target URL : `"url": "https://my-application.com"` and the wanted include starting with a slash like this : `"includes": [ "/customer/<*>"]`. +==== + ===== Login A web scan does work much better if it has got access to all content - so a login is necessary most time. If you do not define a login configuration diff --git a/sechub-doc/src/docs/asciidoc/documents/shared/configuration/sechub_config_example2_webscan_anonyous.json b/sechub-doc/src/docs/asciidoc/documents/shared/configuration/sechub_config_example2_webscan_anonyous.json index ac0c797046..7f21493180 100644 --- a/sechub-doc/src/docs/asciidoc/documents/shared/configuration/sechub_config_example2_webscan_anonyous.json +++ b/sechub-doc/src/docs/asciidoc/documents/shared/configuration/sechub_config_example2_webscan_anonyous.json @@ -2,13 +2,24 @@ "apiVersion": "1.0", "webScan": { //<1> "url": "https://www.gamechanger.example.org", //<2> - "includes": [ - "/special/include" - ], //<3> - "excludes": [ + "includes": [ //<3> + "/special/include", + "/special/include/<*>", + "<*>/special/<*>/include/<*>", + "<*>/special/include/<*>", + "special/include/<*>", + "special/include", + "special/include/" + ], + "excludes": [ //<4> "/en/contact", - "/contact" - ] //<4> + "/en/contacts/<*>", + "<*>/en/<*>/contacts/<*>", + "<*>/en/contacts/<*>", + "en/contacts/<*>", + "en/contacts", + "en/contacts/" + ] }, "maxScanDuration": { //<5> "duration": 1, diff --git a/sechub-doc/src/docs/asciidoc/documents/shared/systemtests/systemtests.adoc b/sechub-doc/src/docs/asciidoc/documents/shared/systemtests/systemtests.adoc index cce8d32122..3f72da353f 100644 --- a/sechub-doc/src/docs/asciidoc/documents/shared/systemtests/systemtests.adoc +++ b/sechub-doc/src/docs/asciidoc/documents/shared/systemtests/systemtests.adoc @@ -234,11 +234,15 @@ If not defined, the default credentials for integration test server will be used start the tests. ====== Start +With a <> the start process for the {sechub} server +instance(s) can be defined. ====== Stop +With a <> the stop process for the {sechub} server +instance(s) can be defined. ====== Configure - +The configuration can contain one or more product executor configurations. ===== Remote It is also possible to use an existing real {sechub} platform and run the defined tests there. @@ -321,13 +325,13 @@ Tests are defined at root level of the configuration file. It is possible to def multiple tests in one configurationfile. ===== Prepare -A test can have one ore more preparation steps where following commands can be executed: +A test can have one or more `preparation` steps where following commands can be executed: - `copy` + Provides parameters "from" and "to" and enables users to copy folders or files to other locations - `script` + - Provides the possibility to execute a script + Provides the possibility to execute a <> ===== Execute The test execution phase can contain currently: @@ -353,6 +357,16 @@ Asserts are done after the execution phase. Following steps are possible . `{sechub.jobuuid}` - can be used at any location inside the report if there is the job uuid inside the report . `{*:n}` - `n` represents the number for characters to be ignored. + +===== Cleanup +A test can have one or more `cleanup` steps where following commands can be executed: + +- `copy` + + Provides parameters "from" and "to" and enables users to copy folders or files to other locations + +- `script` + + Provides the possibility to execute a <> + ===== Example snippet @@ -373,7 +387,7 @@ Asserts are done after the execution phase. Following steps are possible } ], "codeScan" : { } } - }, + }, "assert" : [ { "sechubResult" : { //<3> "hasTrafficLight" : "YELLOW", diff --git a/sechub-doc/src/docs/asciidoc/documents/techdoc/03_coding_conventions.adoc b/sechub-doc/src/docs/asciidoc/documents/techdoc/03_coding_conventions.adoc index b846c27a72..13244ab720 100644 --- a/sechub-doc/src/docs/asciidoc/documents/techdoc/03_coding_conventions.adoc +++ b/sechub-doc/src/docs/asciidoc/documents/techdoc/03_coding_conventions.adoc @@ -14,9 +14,19 @@ When having details we always add an empty new line after summary and list detai the beginning to have a pretty output in `GitHub` === Database +[IMPORTANT] +==== +Currently, we use https://h2database.com/html/main.html[H2] and https://www.postgresql.org/[PostgreSQL] in parallel (dev/testing + production). + +Please make sure that the SQL statements work with both of them. +==== ==== Naming in DB -We are using PostgreSQL which does automatically use lower_case names +* We are using PostgreSQL which does automatically use lower_case names +* Numbering starts at `01`; maximum is `99` +* Naming scheme for constraints: `c__` + + Example: `c01_adm_user_emailadress` +* Naming scheme for indices: `i_
_` + + Example: `i01_statistic_job_run_data_filter` ==== Naming in JPA We are using upper cased names @@ -27,7 +37,7 @@ We are using upper cased names Currently we still have a lot of unconventional YAML files. But there is a GitHub issue to adopt them. -If you are writing new YAML files or adding new content to existing files, please follow +If you are writing new YAML files or adding new content to existing files, please follow always the instructions below. ==== @@ -40,11 +50,11 @@ always the instructions below. _(*2 spaces* should be preferred)_ - Define *strings* with *single apostrophes* or *double apostrophes* + - _(to make it clear that the value is a string)_ + _(to make it clear that the value is a string)_ - If possible, define *integers*, *long numbers* etc. as *numbers* - Prefer *hierarchical key structure* to long key variants - Use comments to explain details (when really necessary) -- Use *one space* between key and value definitions (e.g. `key-alias: 'tomcat'`) +- Use *one space* between key and value definitions (e.g. `key-alias: 'tomcat'`) .Example [source,yaml] @@ -53,12 +63,12 @@ always the instructions below. # This configuration setup is only for local development and needs # a generated certificate which is never shared. Because of this -# it is valid to define the credentials inside this file. +# it is valid to define the credentials inside this file. server: ssl: keyStoreType: 'PKCS12' - # we use a keystore location which is never tracked by git. + # we use a keystore location which is never tracked by git. # see dev-create_localhost_certificate.sh and dev-ensure_localhost_certificate.sh key-store: 'classpath:certificates-untracked/generated-dev-localhost-keystore.p12' key-store-password: '123456' @@ -72,12 +82,12 @@ pds: trigger: nextjob: initialdelay: 100 - delay: 500 - + delay: 500 + ---- ==== Templates -When we define YAML templates - e.g. for HELM charts - the template statement shall +When we define YAML templates - e.g. for HELM charts - the template statement shall start at the first column without indention. .Example diff --git a/sechub-doc/src/main/java/com/mercedesbenz/sechub/docgen/adopt/AdoptedSystemTestDefaultFallback.java b/sechub-doc/src/main/java/com/mercedesbenz/sechub/docgen/adopt/AdoptedSystemTestDefaultFallback.java index 1292f8a8e1..1ac46f173f 100644 --- a/sechub-doc/src/main/java/com/mercedesbenz/sechub/docgen/adopt/AdoptedSystemTestDefaultFallback.java +++ b/sechub-doc/src/main/java/com/mercedesbenz/sechub/docgen/adopt/AdoptedSystemTestDefaultFallback.java @@ -21,9 +21,9 @@ */ public enum AdoptedSystemTestDefaultFallback { - FALLBACK_PROJECT_NAME("default-test-project", "Project id"), + FALLBACK_PROJECT_NAME(StringConstants.DEFAULT_PROJECT_ID, "Project id"), - FALLBACK_PROFILE_ID("default-test-profile", "Profile id"), + FALLBACK_PROFILE_ID(StringConstants.DEFAULT_PROFILE_ID, "Profile id"), /** Same like default in /sechub-solution/env-sechub */ FALLBACK_LOCAL_SECHUB_URL("https://localhost:8443", "Local", "SecHub url"), @@ -53,7 +53,7 @@ public enum AdoptedSystemTestDefaultFallback { FALLBACK_SECHUB_WAIT_FOR_AVAILABLE("true", "SecHub wait for available"), - FALLBACK_UPLOAD_REF_ID("default-ref", "Upload reference id"), + FALLBACK_UPLOAD_REF_ID(StringConstants.DEFAULT_REFERENCE_ID, "Upload reference id"), ; @@ -61,6 +61,14 @@ public enum AdoptedSystemTestDefaultFallback { private String value; private String description; + public class StringConstants { + + public static final String DEFAULT_PROJECT_ID = "default-test-project"; + public static final String DEFAULT_PROFILE_ID = "default-test-profile"; + public static final String DEFAULT_REFERENCE_ID = "default-ref"; + + } + AdoptedSystemTestDefaultFallback(String value, String description) { this(value, null, description); } diff --git a/sechub-integrationtest/pds/product-scripts/integrationtest-codescan.sh b/sechub-integrationtest/pds/product-scripts/integrationtest-codescan.sh index 60411916c5..f032104b46 100755 --- a/sechub-integrationtest/pds/product-scripts/integrationtest-codescan.sh +++ b/sechub-integrationtest/pds/product-scripts/integrationtest-codescan.sh @@ -67,10 +67,34 @@ if [[ ! -f "${PDS_JOB_RESULT_FILE}" ]]; then echo "${PDS_JOB_RESULT_FILE} was missing - created empty file" fi +# ******************************************* +# Post processing of result file +# ******************************************* +# +# In the steps before we created a report file. The first step for the report is that the +# unit test does upload a archive file (zip or tar) which contains a directory structure with different simple +# text files. The content of each file consists of single text lines. Each line represents a simple +# definition for a pseudo vulnerability. After the archive file is extracted by the PDS, the content of the +# files were merged to one single report file. +# +# Furthermore, we have a special integration test importer in SecHub which can import such reports if they +# contain the marker #PDS_INTTEST_PRODUCT_CODESCAN in the first line. +# +# The second line is an additional pseudo vulnerability which is always inside the SecHub report. +# It is at info level and contains the parameters `pds.test.key.variantname` and `product1.level` as description. +# The variant name is used inside the script to have dedicated behaviors for different product executor configurations. +# For debugging purposes the developer can look into the sechub report and see which variant was used for this test. + +mv "${PDS_JOB_RESULT_FILE}" "${PDS_JOB_RESULT_FILE}_tmp" + echo "#PDS_INTTEST_PRODUCT_CODESCAN -info:pds.test.key.variantname as PDS_TEST_KEY_VARIANTNAME=$PDS_TEST_KEY_VARIANTNAME,product1.level as PRODUCT1_LEVEL=$PRODUCT1_LEVEL -$(cat ${PDS_JOB_RESULT_FILE})" > "${PDS_JOB_RESULT_FILE}" +info:pds.test.key.variantname as PDS_TEST_KEY_VARIANTNAME=$PDS_TEST_KEY_VARIANTNAME,product1.level as PRODUCT1_LEVEL=$PRODUCT1_LEVEL" > "${PDS_JOB_RESULT_FILE}" + +cat "${PDS_JOB_RESULT_FILE}_tmp" >> "${PDS_JOB_RESULT_FILE}" + +rm "${PDS_JOB_RESULT_FILE}_tmp" +# - End of result file post processing if [[ "$PDS_TEST_KEY_VARIANTNAME" = "f" ]]; then produceLargerOutputStreamContent diff --git a/sechub-integrationtest/pds/product-scripts/shared/shared-events.sh b/sechub-integrationtest/pds/product-scripts/shared/shared-events.sh index 4f8b877ac9..7abd29fe55 100644 --- a/sechub-integrationtest/pds/product-scripts/shared/shared-events.sh +++ b/sechub-integrationtest/pds/product-scripts/shared/shared-events.sh @@ -48,7 +48,7 @@ function waitForEventAndSendMessage(){ if [[ counter -ge $MAX_AMOUNT_OF_RETRIES ]]; then echo "|" echo "FAILED - event folder contains:" - ls -l $PDS_JOB_EVENTS_FOLDER + ls -l "$PDS_JOB_EVENTS_FOLDER" errorMessage "Operation did take too long! Even afer $counter retries the event type: '$EVENT_TYPE' was not recognized!" return $FUNCTION_RESULT_FALSE fi diff --git a/sechub-integrationtest/pds/product-scripts/shared/shared-merging.sh b/sechub-integrationtest/pds/product-scripts/shared/shared-merging.sh index 4525460a69..a855216135 100644 --- a/sechub-integrationtest/pds/product-scripts/shared/shared-merging.sh +++ b/sechub-integrationtest/pds/product-scripts/shared/shared-merging.sh @@ -11,9 +11,9 @@ function mergeFolderFilesRecursivelyIntoResultFile(){ debug "> reading $READING_TYPE from: ${FOLDER_TO_READ_AND_MERGE}" $DEBUG - find ${FOLDER_TO_READ_AND_MERGE} -type f | + find "${FOLDER_TO_READ_AND_MERGE}" -type f | while read src - do cat "${src}" >> ${RESULT_FILE} + do cat "${src}" >> "${RESULT_FILE}" debug "> appended '$src' to ${RESULT_FILE}" $DEBUG done } diff --git a/sechub-integrationtest/src/test/java/com/mercedesbenz/sechub/integrationtest/scenario1/AutoCleanupScenario1IntTest.java b/sechub-integrationtest/src/test/java/com/mercedesbenz/sechub/integrationtest/scenario1/AutoCleanupScenario1IntTest.java index b88e6a54d8..e20c95aa4a 100644 --- a/sechub-integrationtest/src/test/java/com/mercedesbenz/sechub/integrationtest/scenario1/AutoCleanupScenario1IntTest.java +++ b/sechub-integrationtest/src/test/java/com/mercedesbenz/sechub/integrationtest/scenario1/AutoCleanupScenario1IntTest.java @@ -54,8 +54,9 @@ public void auto_cleanup_executed_in_every_domain_when_admin_configures_cleanupd /* schedule domain */ addExpectedDeleteInspection("sechub-jobs","com.mercedesbenz.sechub.domain.schedule.autocleanup.ScheduleAutoCleanupService",0). addExpectedDeleteInspection("product-results","com.mercedesbenz.sechub.domain.scan.autocleanup.ScanAutoCleanupService",0). + addExpectedDeleteInspection("scan-reports","com.mercedesbenz.sechub.domain.scan.autocleanup.ScanAutoCleanupService",0). - addExpectedDifferentKindOfDeleteInspections(4). + addExpectedDifferentKindOfDeleteInspections(5). assertAsExpectedWithTimeOut(15); diff --git a/sechub-integrationtest/src/test/java/com/mercedesbenz/sechub/integrationtest/scenario12/PDSWebScanJobScenario12IntTest.java b/sechub-integrationtest/src/test/java/com/mercedesbenz/sechub/integrationtest/scenario12/PDSWebScanJobScenario12IntTest.java index 31599149c3..11ccb8db6b 100644 --- a/sechub-integrationtest/src/test/java/com/mercedesbenz/sechub/integrationtest/scenario12/PDSWebScanJobScenario12IntTest.java +++ b/sechub-integrationtest/src/test/java/com/mercedesbenz/sechub/integrationtest/scenario12/PDSWebScanJobScenario12IntTest.java @@ -95,6 +95,17 @@ public void pds_web_scan_has_expected_info_finding_with_given_target_url_and_pro assertNotNull(webConfiguration.getUrl()); assertEquals(JSONConverter.get().toJSON(configuration, true), JSONConverter.get().toJSON(returnedConfiguration, true)); + // config must contain the includes/excludes with wildcards + assertTrue("The web scan config hould contain includes!", webConfiguration.getIncludes().isPresent()); + assertTrue("The web scan config hould contain excludes!", webConfiguration.getExcludes().isPresent()); + + List includes = webConfiguration.getIncludes().get(); + List excludes = webConfiguration.getExcludes().get(); + + assertTrue(includes.contains("/customer/<*>")); + assertTrue(excludes.contains("<*>/admin/<*>")); + + // config must contain the expected headers assertExpectedHeaders(webConfiguration); /* additional testing : messages*/ diff --git a/sechub-integrationtest/src/test/java/com/mercedesbenz/sechub/integrationtest/scenario3/FalsePositivesScenario3IntTest.java b/sechub-integrationtest/src/test/java/com/mercedesbenz/sechub/integrationtest/scenario3/FalsePositivesScenario3IntTest.java index bda74c0848..f6773b1204 100644 --- a/sechub-integrationtest/src/test/java/com/mercedesbenz/sechub/integrationtest/scenario3/FalsePositivesScenario3IntTest.java +++ b/sechub-integrationtest/src/test/java/com/mercedesbenz/sechub/integrationtest/scenario3/FalsePositivesScenario3IntTest.java @@ -59,6 +59,15 @@ public void with_sechubclient_mark_falsepositives_of_only_existing_medium_will_r finding().id(1).name("Absolute Path Traversal").isNotContained(). hasTrafficLight(TrafficLight.GREEN); + /* execute 2 - duplicate call to mark false positives*/ + as(USER_1).withSecHubClient().startFalsePositiveDefinition(project,location).add(1, jobUUID).markAsFalsePositive(); + + /* test 2 - false positive works also after second call*/ + ExecutionResult result3 = as(USER_1).withSecHubClient().startSynchronScanFor(project, location); + assertReportUnordered(result3). + finding().id(1).name("Absolute Path Traversal").isNotContained(). + hasTrafficLight(TrafficLight.GREEN); + /* @formatter:on */ } diff --git a/sechub-integrationtest/src/test/resources/sechub-integrationtest-webscanconfig-all-options.json b/sechub-integrationtest/src/test/resources/sechub-integrationtest-webscanconfig-all-options.json index 95e7a7ae7c..f7e2b39d86 100644 --- a/sechub-integrationtest/src/test/resources/sechub-integrationtest-webscanconfig-all-options.json +++ b/sechub-integrationtest/src/test/resources/sechub-integrationtest-webscanconfig-all-options.json @@ -2,8 +2,8 @@ "apiVersion" : "1.0", "webScan" : { "url" : "https://demo.example.org/myapp", - "includes": [ "/portal/admin", "/abc.html", "/hidden" ], - "excludes": [ "/public/media", "/contact.html", "/static" ], + "includes": [ "/portal/admin", "/abc.html", "/hidden", "/customer/<*>" ], + "excludes": [ "/public/media", "/contact.html", "/static", "<*>/admin/<*>" ], "maxScanDuration" : { "duration" : 35, "unit" : "minutes" diff --git a/sechub-pds-solutions/multi/docker/Multi-Debian.dockerfile b/sechub-pds-solutions/multi/docker/Multi-Debian.dockerfile index 5eb2a776fd..1bb67d0d1d 100644 --- a/sechub-pds-solutions/multi/docker/Multi-Debian.dockerfile +++ b/sechub-pds-solutions/multi/docker/Multi-Debian.dockerfile @@ -28,7 +28,7 @@ COPY mocks "$MOCK_FOLDER" RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ apt-get --assume-yes upgrade && \ - apt-get --assume-yes install sed wget pip && \ + apt-get --assume-yes install sed wget pip git && \ apt-get --assume-yes clean # Install Flawfinder, Bandit, njsscan and mobsfscan @@ -39,6 +39,11 @@ COPY packages.txt $TOOL_FOLDER/packages.txt # Interesting idea, but not as useful inside a container, which in essence is already a virtual environment. # Use `--break-system-packages` to let the Python package manager `pip` mix packages from Debian and Python RUN pip install --break-system-packages -r $TOOL_FOLDER/packages.txt +# pip --editable option allows for installing from VCS Urls +# https://pip.pypa.io/en/stable/cli/pip_install/#cmdoption-e +# It is possible to specify a GIT ref in a VCS Url using @ delimeter +# https://pip.pypa.io/en/stable/topics/vcs-support/ +RUN pip install --break-system-packages --editable "git+https://github.com/alexdd/bandit-sarif-formatter@main#egg=bandit-sarif-formatter" # Create the PDS workspace WORKDIR "$WORKSPACE" diff --git a/sechub-pds-solutions/multi/docker/mocks/bandit_mock.sarif.json b/sechub-pds-solutions/multi/docker/mocks/bandit_mock.sarif.json index 36b0772226..f815afd536 100644 --- a/sechub-pds-solutions/multi/docker/mocks/bandit_mock.sarif.json +++ b/sechub-pds-solutions/multi/docker/mocks/bandit_mock.sarif.json @@ -6,64 +6,372 @@ "name": "Bandit", "rules": [ { - "id": "B409", + "id": "B404", "name": "blacklist", - "helpUri": "https://bandit.readthedocs.io/en/1.7.3/blacklists/blacklist_imports.html#b409-import-xml-pulldom" + "relationships": [ + { + "target": { + "id": "78", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "fe63bd69-5389-414a-a237-62247271a272" + } + }, + "kinds": [ + "superset" + ] + } + ], + "helpUri": "https://bandit.readthedocs.io/en/1.7.5/blacklists/blacklist_imports.html#b404-import-subprocess" }, { - "id": "B406", + "id": "B506", + "name": "yaml_load", + "relationships": [ + { + "target": { + "id": "20", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "7d8fec63-d2d4-420f-bcfe-3ddeb2e1c6cb" + } + }, + "kinds": [ + "superset" + ] + } + ], + "helpUri": "https://bandit.readthedocs.io/en/1.7.5/plugins/b506_yaml_load.html" + }, + { + "id": "B324", + "name": "hashlib", + "relationships": [ + { + "target": { + "id": "327", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "333a4f8b-57a3-478a-9053-9682a122855e" + } + }, + "kinds": [ + "superset" + ] + } + ], + "helpUri": "https://bandit.readthedocs.io/en/1.7.5/plugins/b324_hashlib.html" + }, + { + "id": "B307", "name": "blacklist", - "helpUri": "https://bandit.readthedocs.io/en/1.7.3/blacklists/blacklist_imports.html#b406-import-xml-sax" + "relationships": [ + { + "target": { + "id": "78", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "eb984ec0-8af6-41ab-b82b-fc812a602d29" + } + }, + "kinds": [ + "superset" + ] + } + ], + "helpUri": "https://bandit.readthedocs.io/en/1.7.5/blacklists/blacklist_calls.html#b307-eval" }, { - "id": "B404", + "id": "B602", + "name": "subprocess_popen_with_shell_equals_true", + "relationships": [ + { + "target": { + "id": "78", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "5726be7c-fb62-402b-a00c-b97086331f26" + } + }, + "kinds": [ + "superset" + ] + } + ], + "helpUri": "https://bandit.readthedocs.io/en/1.7.5/plugins/b602_subprocess_popen_with_shell_equals_true.html" + }, + { + "id": "B113", + "name": "request_without_timeout", + "relationships": [ + { + "target": { + "id": "400", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "0dbf60bb-53ce-4be1-a522-c302ee563be2" + } + }, + "kinds": [ + "superset" + ] + } + ], + "helpUri": "https://bandit.readthedocs.io/en/1.7.5/plugins/b113_request_without_timeout.html" + }, + { + "id": "B105", + "name": "hardcoded_password_string", + "relationships": [ + { + "target": { + "id": "259", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "84158e36-134f-4fe1-93fc-852512d8da80" + } + }, + "kinds": [ + "superset" + ] + } + ], + "helpUri": "https://bandit.readthedocs.io/en/1.7.5/plugins/b105_hardcoded_password_string.html" + }, + { + "id": "B409", + "name": "blacklist", + "relationships": [ + { + "target": { + "id": "20", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "78279d40-f51b-43cb-86b4-c12ba4a5b470" + } + }, + "kinds": [ + "superset" + ] + } + ], + "helpUri": "https://bandit.readthedocs.io/en/1.7.5/blacklists/blacklist_imports.html#b409-import-xml-pulldom" + }, + { + "id": "B406", "name": "blacklist", - "helpUri": "https://bandit.readthedocs.io/en/1.7.3/blacklists/blacklist_imports.html#b404-import-subprocess" + "relationships": [ + { + "target": { + "id": "20", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "74563cb3-89b9-4a66-a606-83fe898cb16b" + } + }, + "kinds": [ + "superset" + ] + } + ], + "helpUri": "https://bandit.readthedocs.io/en/1.7.5/blacklists/blacklist_imports.html#b406-import-xml-sax" }, { "id": "B403", "name": "blacklist", - "helpUri": "https://bandit.readthedocs.io/en/1.7.3/blacklists/blacklist_imports.html#b403-import-pickle" + "relationships": [ + { + "target": { + "id": "502", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "cce2454a-4686-4184-8c41-1cb5b7a94fb3" + } + }, + "kinds": [ + "superset" + ] + } + ], + "helpUri": "https://bandit.readthedocs.io/en/1.7.5/blacklists/blacklist_imports.html#b403-import-pickle" }, { "id": "B608", "name": "hardcoded_sql_expressions", - "helpUri": "https://bandit.readthedocs.io/en/1.7.3/plugins/b608_hardcoded_sql_expressions.html" + "relationships": [ + { + "target": { + "id": "89", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "171744a8-98a4-47a0-9b27-ce23cecc0a9f" + } + }, + "kinds": [ + "superset" + ] + } + ], + "helpUri": "https://bandit.readthedocs.io/en/1.7.5/plugins/b608_hardcoded_sql_expressions.html" }, { "id": "B301", "name": "blacklist", - "helpUri": "https://bandit.readthedocs.io/en/1.7.3/blacklists/blacklist_calls.html#b301-pickle" + "relationships": [ + { + "target": { + "id": "502", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "74118bc0-608d-46ce-b7e5-3381cb6091bf" + } + }, + "kinds": [ + "superset" + ] + } + ], + "helpUri": "https://bandit.readthedocs.io/en/1.7.5/blacklists/blacklist_calls.html#b301-pickle" }, { "id": "B317", "name": "blacklist", - "helpUri": "https://bandit.readthedocs.io/en/1.7.3/blacklists/blacklist_calls.html#b313-b320-xml-bad-sax" + "relationships": [ + { + "target": { + "id": "20", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "d7a4edbb-0e8f-4308-b09a-bc6066a7b737" + } + }, + "kinds": [ + "superset" + ] + } + ], + "helpUri": "https://bandit.readthedocs.io/en/1.7.5/blacklists/blacklist_calls.html#b313-b320-xml-bad-sax" }, { "id": "B319", "name": "blacklist", - "helpUri": "https://bandit.readthedocs.io/en/1.7.3/blacklists/blacklist_calls.html#b313-b320-xml-bad-pulldom" - }, - { - "id": "B602", - "name": "subprocess_popen_with_shell_equals_true", - "helpUri": "https://bandit.readthedocs.io/en/1.7.3/plugins/b602_subprocess_popen_with_shell_equals_true.html" + "relationships": [ + { + "target": { + "id": "20", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "13976a35-127a-45a7-980e-305071492d5c" + } + }, + "kinds": [ + "superset" + ] + } + ], + "helpUri": "https://bandit.readthedocs.io/en/1.7.5/blacklists/blacklist_calls.html#b313-b320-xml-bad-pulldom" }, { "id": "B311", "name": "blacklist", - "helpUri": "https://bandit.readthedocs.io/en/1.7.3/blacklists/blacklist_calls.html#b311-random" + "relationships": [ + { + "target": { + "id": "330", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "914e4d43-f404-43fa-bc49-c54d6de0374a" + } + }, + "kinds": [ + "superset" + ] + } + ], + "helpUri": "https://bandit.readthedocs.io/en/1.7.5/blacklists/blacklist_calls.html#b311-random" }, { - "id": "B506", - "name": "yaml_load", - "helpUri": "https://bandit.readthedocs.io/en/1.7.3/plugins/b506_yaml_load.html" + "id": "B106", + "name": "hardcoded_password_funcarg", + "relationships": [ + { + "target": { + "id": "259", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "aefc3def-f137-4bc5-811d-6967be8f9b24" + } + }, + "kinds": [ + "superset" + ] + } + ], + "helpUri": "https://bandit.readthedocs.io/en/1.7.5/plugins/b106_hardcoded_password_funcarg.html" }, { - "id": "B105", - "name": "hardcoded_password_string", - "helpUri": "https://bandit.readthedocs.io/en/1.7.3/plugins/b105_hardcoded_password_string.html" + "id": "B110", + "name": "try_except_pass", + "relationships": [ + { + "target": { + "id": "703", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "651d3ad2-fc6d-4507-8b3c-ed285251c503" + } + }, + "kinds": [ + "superset" + ] + } + ], + "helpUri": "https://bandit.readthedocs.io/en/1.7.5/plugins/b110_try_except_pass.html" + }, + { + "id": "B603", + "name": "subprocess_without_shell_equals_true", + "relationships": [ + { + "target": { + "id": "78", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "29f46b80-7831-4384-b949-58617e176855" + } + }, + "kinds": [ + "superset" + ] + } + ], + "helpUri": "https://bandit.readthedocs.io/en/1.7.5/plugins/b603_subprocess_without_shell_equals_true.html" + } + ], + "version": "1.7.5", + "informationUri": "https://github.com/PyCQA/bandit/tree/main", + "supportedTaxonomies": [ + { + "name": "CWE", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190" } ] } @@ -71,25 +379,38 @@ "invocations": [ { "executionSuccessful": true, - "endTimeUtc": "2022-03-01T10:58:59Z" + "endTimeUtc": "2023-10-05T13:40:48Z" } ], "properties": { "metrics": { "_totals": { - "loc": 750, + "loc": 2453, "nosec": 0, "skipped_tests": 0, "SEVERITY.UNDEFINED": 0, "CONFIDENCE.UNDEFINED": 0, - "SEVERITY.LOW": 7, - "CONFIDENCE.LOW": 1, - "SEVERITY.MEDIUM": 5, - "CONFIDENCE.MEDIUM": 1, - "SEVERITY.HIGH": 1, - "CONFIDENCE.HIGH": 11 + "SEVERITY.LOW": 27, + "CONFIDENCE.LOW": 5, + "SEVERITY.MEDIUM": 12, + "CONFIDENCE.MEDIUM": 13, + "SEVERITY.HIGH": 4, + "CONFIDENCE.HIGH": 25 + }, + "./pygoat/PyGoatBot.py": { + "loc": 55, + "nosec": 0, + "skipped_tests": 0, + "SEVERITY.UNDEFINED": 0, + "SEVERITY.LOW": 0, + "SEVERITY.MEDIUM": 0, + "SEVERITY.HIGH": 0, + "CONFIDENCE.UNDEFINED": 0, + "CONFIDENCE.LOW": 0, + "CONFIDENCE.MEDIUM": 0, + "CONFIDENCE.HIGH": 0 }, - "./pygoat/pygoat/introduction/__init__.py": { + "./pygoat/introduction/__init__.py": { "loc": 0, "nosec": 0, "skipped_tests": 0, @@ -102,8 +423,21 @@ "CONFIDENCE.MEDIUM": 0, "CONFIDENCE.HIGH": 0 }, - "./pygoat/pygoat/introduction/admin.py": { - "loc": 7, + "./pygoat/introduction/admin.py": { + "loc": 13, + "nosec": 0, + "skipped_tests": 0, + "SEVERITY.UNDEFINED": 0, + "SEVERITY.LOW": 0, + "SEVERITY.MEDIUM": 0, + "SEVERITY.HIGH": 0, + "CONFIDENCE.UNDEFINED": 0, + "CONFIDENCE.LOW": 0, + "CONFIDENCE.MEDIUM": 0, + "CONFIDENCE.HIGH": 0 + }, + "./pygoat/introduction/apis.py": { + "loc": 109, "nosec": 0, "skipped_tests": 0, "SEVERITY.UNDEFINED": 0, @@ -115,7 +449,7 @@ "CONFIDENCE.MEDIUM": 0, "CONFIDENCE.HIGH": 0 }, - "./pygoat/pygoat/introduction/apps.py": { + "./pygoat/introduction/apps.py": { "loc": 3, "nosec": 0, "skipped_tests": 0, @@ -128,8 +462,8 @@ "CONFIDENCE.MEDIUM": 0, "CONFIDENCE.HIGH": 0 }, - "./pygoat/pygoat/introduction/forms.py": { - "loc": 0, + "./pygoat/introduction/forms.py": { + "loc": 14, "nosec": 0, "skipped_tests": 0, "SEVERITY.UNDEFINED": 0, @@ -141,7 +475,20 @@ "CONFIDENCE.MEDIUM": 0, "CONFIDENCE.HIGH": 0 }, - "./pygoat/pygoat/introduction/migrations/0001_initial.py": { + "./pygoat/introduction/lab_code/test.py": { + "loc": 22, + "nosec": 0, + "skipped_tests": 0, + "SEVERITY.UNDEFINED": 0, + "SEVERITY.LOW": 1, + "SEVERITY.MEDIUM": 1, + "SEVERITY.HIGH": 0, + "CONFIDENCE.UNDEFINED": 0, + "CONFIDENCE.LOW": 0, + "CONFIDENCE.MEDIUM": 0, + "CONFIDENCE.HIGH": 2 + }, + "./pygoat/introduction/migrations/0001_initial.py": { "loc": 16, "nosec": 0, "skipped_tests": 0, @@ -154,7 +501,7 @@ "CONFIDENCE.MEDIUM": 0, "CONFIDENCE.HIGH": 0 }, - "./pygoat/pygoat/introduction/migrations/0002_auto_20210414_1510.py": { + "./pygoat/introduction/migrations/0002_auto_20210414_1510.py": { "loc": 25, "nosec": 0, "skipped_tests": 0, @@ -167,7 +514,7 @@ "CONFIDENCE.MEDIUM": 0, "CONFIDENCE.HIGH": 0 }, - "./pygoat/pygoat/introduction/migrations/0003_password_user.py": { + "./pygoat/introduction/migrations/0003_password_user.py": { "loc": 23, "nosec": 0, "skipped_tests": 0, @@ -180,7 +527,7 @@ "CONFIDENCE.MEDIUM": 0, "CONFIDENCE.HIGH": 0 }, - "./pygoat/pygoat/introduction/migrations/0004_auto_20210415_1722.py": { + "./pygoat/introduction/migrations/0004_auto_20210415_1722.py": { "loc": 16, "nosec": 0, "skipped_tests": 0, @@ -193,7 +540,7 @@ "CONFIDENCE.MEDIUM": 0, "CONFIDENCE.HIGH": 0 }, - "./pygoat/pygoat/introduction/migrations/0005_auto_20210415_1748.py": { + "./pygoat/introduction/migrations/0005_auto_20210415_1748.py": { "loc": 11, "nosec": 0, "skipped_tests": 0, @@ -206,7 +553,7 @@ "CONFIDENCE.MEDIUM": 0, "CONFIDENCE.HIGH": 0 }, - "./pygoat/pygoat/introduction/migrations/0006_comments.py": { + "./pygoat/introduction/migrations/0006_comments.py": { "loc": 15, "nosec": 0, "skipped_tests": 0, @@ -219,7 +566,7 @@ "CONFIDENCE.MEDIUM": 0, "CONFIDENCE.HIGH": 0 }, - "./pygoat/pygoat/introduction/migrations/0007_auto_20210418_0022.py": { + "./pygoat/introduction/migrations/0007_auto_20210418_0022.py": { "loc": 12, "nosec": 0, "skipped_tests": 0, @@ -232,7 +579,7 @@ "CONFIDENCE.MEDIUM": 0, "CONFIDENCE.HIGH": 0 }, - "./pygoat/pygoat/introduction/migrations/0008_otp.py": { + "./pygoat/introduction/migrations/0008_otp.py": { "loc": 15, "nosec": 0, "skipped_tests": 0, @@ -245,7 +592,7 @@ "CONFIDENCE.MEDIUM": 0, "CONFIDENCE.HIGH": 0 }, - "./pygoat/pygoat/introduction/migrations/0009_auto_20210517_2047.py": { + "./pygoat/introduction/migrations/0009_auto_20210517_2047.py": { "loc": 13, "nosec": 0, "skipped_tests": 0, @@ -258,8 +605,8 @@ "CONFIDENCE.MEDIUM": 0, "CONFIDENCE.HIGH": 0 }, - "./pygoat/pygoat/introduction/migrations/__init__.py": { - "loc": 0, + "./pygoat/introduction/migrations/0010_authlogin.py": { + "loc": 16, "nosec": 0, "skipped_tests": 0, "SEVERITY.UNDEFINED": 0, @@ -271,8 +618,8 @@ "CONFIDENCE.MEDIUM": 0, "CONFIDENCE.HIGH": 0 }, - "./pygoat/pygoat/introduction/models.py": { - "loc": 24, + "./pygoat/introduction/migrations/0011_tickits.py": { + "loc": 16, "nosec": 0, "skipped_tests": 0, "SEVERITY.UNDEFINED": 0, @@ -284,8 +631,8 @@ "CONFIDENCE.MEDIUM": 0, "CONFIDENCE.HIGH": 0 }, - "./pygoat/pygoat/introduction/tests.py": { - "loc": 1, + "./pygoat/introduction/migrations/0012_alter_tickits_user.py": { + "loc": 15, "nosec": 0, "skipped_tests": 0, "SEVERITY.UNDEFINED": 0, @@ -297,8 +644,8 @@ "CONFIDENCE.MEDIUM": 0, "CONFIDENCE.HIGH": 0 }, - "./pygoat/pygoat/introduction/urls.py": { - "loc": 44, + "./pygoat/introduction/migrations/0013_alter_comments_id_alter_faang_id_alter_info_id_and_more.py": { + "loc": 37, "nosec": 0, "skipped_tests": 0, "SEVERITY.UNDEFINED": 0, @@ -310,21 +657,21 @@ "CONFIDENCE.MEDIUM": 0, "CONFIDENCE.HIGH": 0 }, - "./pygoat/pygoat/introduction/views.py": { - "loc": 358, + "./pygoat/introduction/migrations/0014_sql_lab_table.py": { + "loc": 14, "nosec": 0, "skipped_tests": 0, "SEVERITY.UNDEFINED": 0, - "SEVERITY.LOW": 6, - "SEVERITY.MEDIUM": 5, - "SEVERITY.HIGH": 1, + "SEVERITY.LOW": 0, + "SEVERITY.MEDIUM": 0, + "SEVERITY.HIGH": 0, "CONFIDENCE.UNDEFINED": 0, - "CONFIDENCE.LOW": 1, + "CONFIDENCE.LOW": 0, "CONFIDENCE.MEDIUM": 0, - "CONFIDENCE.HIGH": 11 + "CONFIDENCE.HIGH": 0 }, - "./pygoat/pygoat/manage.py": { - "loc": 16, + "./pygoat/introduction/migrations/0015_blogs.py": { + "loc": 18, "nosec": 0, "skipped_tests": 0, "SEVERITY.UNDEFINED": 0, @@ -336,8 +683,8 @@ "CONFIDENCE.MEDIUM": 0, "CONFIDENCE.HIGH": 0 }, - "./pygoat/pygoat/pygoat/__init__.py": { - "loc": 0, + "./pygoat/introduction/migrations/0016_alter_blogs_blog_id.py": { + "loc": 12, "nosec": 0, "skipped_tests": 0, "SEVERITY.UNDEFINED": 0, @@ -349,8 +696,8 @@ "CONFIDENCE.MEDIUM": 0, "CONFIDENCE.HIGH": 0 }, - "./pygoat/pygoat/pygoat/asgi.py": { - "loc": 10, + "./pygoat/introduction/migrations/0017_cf_user.py": { + "loc": 15, "nosec": 0, "skipped_tests": 0, "SEVERITY.UNDEFINED": 0, @@ -362,21 +709,21 @@ "CONFIDENCE.MEDIUM": 0, "CONFIDENCE.HIGH": 0 }, - "./pygoat/pygoat/pygoat/settings.py": { - "loc": 105, + "./pygoat/introduction/migrations/0018_cf_user_password2.py": { + "loc": 13, "nosec": 0, "skipped_tests": 0, "SEVERITY.UNDEFINED": 0, - "SEVERITY.LOW": 1, + "SEVERITY.LOW": 0, "SEVERITY.MEDIUM": 0, "SEVERITY.HIGH": 0, "CONFIDENCE.UNDEFINED": 0, "CONFIDENCE.LOW": 0, - "CONFIDENCE.MEDIUM": 1, + "CONFIDENCE.MEDIUM": 0, "CONFIDENCE.HIGH": 0 }, - "./pygoat/pygoat/pygoat/urls.py": { - "loc": 26, + "./pygoat/introduction/migrations/0019_af_admin.py": { + "loc": 21, "nosec": 0, "skipped_tests": 0, "SEVERITY.UNDEFINED": 0, @@ -388,8 +735,8 @@ "CONFIDENCE.MEDIUM": 0, "CONFIDENCE.HIGH": 0 }, - "./pygoat/pygoat/pygoat/wsgi.py": { - "loc": 10, + "./pygoat/introduction/migrations/0020_af_session_id_alter_af_admin_lockout_cooldown.py": { + "loc": 20, "nosec": 0, "skipped_tests": 0, "SEVERITY.UNDEFINED": 0, @@ -400,115 +747,1660 @@ "CONFIDENCE.LOW": 0, "CONFIDENCE.MEDIUM": 0, "CONFIDENCE.HIGH": 0 - } - } - }, - "results": [ - { - "message": { - "text": "Using parseString to parse untrusted XML data is known to be vulnerable to XML attacks. Replace parseString with the equivalent defusedxml package, or make sure defusedxml.defuse_stdlib() is called." }, - "level": "note", - "locations": [ - { - "physicalLocation": { - "region": { - "snippet": { - "text": "from xml.dom.pulldom import parseString, START_ELEMENT\n" - }, - "startLine": 14 - }, - "artifactLocation": { - "uri": "pygoat/pygoat/introduction/views.py" - }, - "contextRegion": { - "snippet": { - "text": "from random import randint\nfrom xml.dom.pulldom import parseString, START_ELEMENT\nfrom xml.sax.handler import feature_external_ges\n" - }, - "endLine": 15, - "startLine": 13 - } - } - } - ], - "properties": { - "issue_confidence": "HIGH", - "issue_severity": "LOW" + "./pygoat/introduction/migrations/0021_csrf_user_tbl.py": { + "loc": 17, + "nosec": 0, + "skipped_tests": 0, + "SEVERITY.UNDEFINED": 0, + "SEVERITY.LOW": 0, + "SEVERITY.MEDIUM": 0, + "SEVERITY.HIGH": 0, + "CONFIDENCE.UNDEFINED": 0, + "CONFIDENCE.LOW": 0, + "CONFIDENCE.MEDIUM": 0, + "CONFIDENCE.HIGH": 0 }, - "ruleId": "B409", - "ruleIndex": 0 - }, - { - "message": { - "text": "Using feature_external_ges to parse untrusted XML data is known to be vulnerable to XML attacks. Replace feature_external_ges with the equivalent defusedxml package, or make sure defusedxml.defuse_stdlib() is called." + "./pygoat/introduction/migrations/__init__.py": { + "loc": 0, + "nosec": 0, + "skipped_tests": 0, + "SEVERITY.UNDEFINED": 0, + "SEVERITY.LOW": 0, + "SEVERITY.MEDIUM": 0, + "SEVERITY.HIGH": 0, + "CONFIDENCE.UNDEFINED": 0, + "CONFIDENCE.LOW": 0, + "CONFIDENCE.MEDIUM": 0, + "CONFIDENCE.HIGH": 0 }, - "level": "note", - "locations": [ - { - "physicalLocation": { - "region": { - "snippet": { - "text": "from xml.sax.handler import feature_external_ges\n" - }, - "startLine": 15 - }, - "artifactLocation": { - "uri": "pygoat/pygoat/introduction/views.py" - }, - "contextRegion": { - "snippet": { - "text": "from xml.dom.pulldom import parseString, START_ELEMENT\nfrom xml.sax.handler import feature_external_ges\nfrom xml.sax import make_parser\n" - }, - "endLine": 16, - "startLine": 14 - } - } - } - ], - "properties": { - "issue_confidence": "HIGH", - "issue_severity": "LOW" + "./pygoat/introduction/mitre.py": { + "loc": 194, + "nosec": 0, + "skipped_tests": 0, + "SEVERITY.UNDEFINED": 0, + "SEVERITY.LOW": 1, + "SEVERITY.MEDIUM": 1, + "SEVERITY.HIGH": 2, + "CONFIDENCE.UNDEFINED": 0, + "CONFIDENCE.LOW": 0, + "CONFIDENCE.MEDIUM": 0, + "CONFIDENCE.HIGH": 4 }, - "ruleId": "B406", - "ruleIndex": 1 - }, - { - "message": { - "text": "Using make_parser to parse untrusted XML data is known to be vulnerable to XML attacks. Replace make_parser with the equivalent defusedxml package, or make sure defusedxml.defuse_stdlib() is called." + "./pygoat/introduction/models.py": { + "loc": 78, + "nosec": 0, + "skipped_tests": 0, + "SEVERITY.UNDEFINED": 0, + "SEVERITY.LOW": 0, + "SEVERITY.MEDIUM": 0, + "SEVERITY.HIGH": 0, + "CONFIDENCE.UNDEFINED": 0, + "CONFIDENCE.LOW": 0, + "CONFIDENCE.MEDIUM": 0, + "CONFIDENCE.HIGH": 0 }, - "level": "note", - "locations": [ - { - "physicalLocation": { - "region": { - "snippet": { - "text": "from xml.sax import make_parser\n" - }, - "startLine": 16 - }, - "artifactLocation": { - "uri": "pygoat/pygoat/introduction/views.py" - }, - "contextRegion": { - "snippet": { - "text": "from xml.sax.handler import feature_external_ges\nfrom xml.sax import make_parser\nfrom django.views.decorators.csrf import csrf_exempt\n" - }, - "endLine": 17, - "startLine": 15 - } - } - } + "./pygoat/introduction/playground/A6/soln.py": { + "loc": 13, + "nosec": 0, + "skipped_tests": 0, + "SEVERITY.UNDEFINED": 0, + "SEVERITY.LOW": 0, + "SEVERITY.MEDIUM": 1, + "SEVERITY.HIGH": 0, + "CONFIDENCE.UNDEFINED": 0, + "CONFIDENCE.LOW": 1, + "CONFIDENCE.MEDIUM": 0, + "CONFIDENCE.HIGH": 0 + }, + "./pygoat/introduction/playground/A6/utility.py": { + "loc": 13, + "nosec": 0, + "skipped_tests": 0, + "SEVERITY.UNDEFINED": 0, + "SEVERITY.LOW": 0, + "SEVERITY.MEDIUM": 1, + "SEVERITY.HIGH": 0, + "CONFIDENCE.UNDEFINED": 0, + "CONFIDENCE.LOW": 1, + "CONFIDENCE.MEDIUM": 0, + "CONFIDENCE.HIGH": 0 + }, + "./pygoat/introduction/playground/A9/api.py": { + "loc": 30, + "nosec": 0, + "skipped_tests": 0, + "SEVERITY.UNDEFINED": 0, + "SEVERITY.LOW": 1, + "SEVERITY.MEDIUM": 0, + "SEVERITY.HIGH": 0, + "CONFIDENCE.UNDEFINED": 0, + "CONFIDENCE.LOW": 0, + "CONFIDENCE.MEDIUM": 1, + "CONFIDENCE.HIGH": 0 + }, + "./pygoat/introduction/playground/A9/archive.py": { + "loc": 49, + "nosec": 0, + "skipped_tests": 0, + "SEVERITY.UNDEFINED": 0, + "SEVERITY.LOW": 1, + "SEVERITY.MEDIUM": 0, + "SEVERITY.HIGH": 0, + "CONFIDENCE.UNDEFINED": 0, + "CONFIDENCE.LOW": 0, + "CONFIDENCE.MEDIUM": 1, + "CONFIDENCE.HIGH": 0 + }, + "./pygoat/introduction/playground/A9/main.py": { + "loc": 10, + "nosec": 0, + "skipped_tests": 0, + "SEVERITY.UNDEFINED": 0, + "SEVERITY.LOW": 0, + "SEVERITY.MEDIUM": 0, + "SEVERITY.HIGH": 0, + "CONFIDENCE.UNDEFINED": 0, + "CONFIDENCE.LOW": 0, + "CONFIDENCE.MEDIUM": 0, + "CONFIDENCE.HIGH": 0 + }, + "./pygoat/introduction/playground/__init__.py": { + "loc": 0, + "nosec": 0, + "skipped_tests": 0, + "SEVERITY.UNDEFINED": 0, + "SEVERITY.LOW": 0, + "SEVERITY.MEDIUM": 0, + "SEVERITY.HIGH": 0, + "CONFIDENCE.UNDEFINED": 0, + "CONFIDENCE.LOW": 0, + "CONFIDENCE.MEDIUM": 0, + "CONFIDENCE.HIGH": 0 + }, + "./pygoat/introduction/playground/ssrf/__init__.py": { + "loc": 0, + "nosec": 0, + "skipped_tests": 0, + "SEVERITY.UNDEFINED": 0, + "SEVERITY.LOW": 0, + "SEVERITY.MEDIUM": 0, + "SEVERITY.HIGH": 0, + "CONFIDENCE.UNDEFINED": 0, + "CONFIDENCE.LOW": 0, + "CONFIDENCE.MEDIUM": 0, + "CONFIDENCE.HIGH": 0 + }, + "./pygoat/introduction/playground/ssrf/main.py": { + "loc": 10, + "nosec": 0, + "skipped_tests": 0, + "SEVERITY.UNDEFINED": 0, + "SEVERITY.LOW": 0, + "SEVERITY.MEDIUM": 0, + "SEVERITY.HIGH": 0, + "CONFIDENCE.UNDEFINED": 0, + "CONFIDENCE.LOW": 0, + "CONFIDENCE.MEDIUM": 0, + "CONFIDENCE.HIGH": 0 + }, + "./pygoat/introduction/playground/ssrf/test.py": { + "loc": 29, + "nosec": 0, + "skipped_tests": 0, + "SEVERITY.UNDEFINED": 0, + "SEVERITY.LOW": 0, + "SEVERITY.MEDIUM": 0, + "SEVERITY.HIGH": 0, + "CONFIDENCE.UNDEFINED": 0, + "CONFIDENCE.LOW": 0, + "CONFIDENCE.MEDIUM": 0, + "CONFIDENCE.HIGH": 0 + }, + "./pygoat/introduction/tests.py": { + "loc": 1, + "nosec": 0, + "skipped_tests": 0, + "SEVERITY.UNDEFINED": 0, + "SEVERITY.LOW": 0, + "SEVERITY.MEDIUM": 0, + "SEVERITY.HIGH": 0, + "CONFIDENCE.UNDEFINED": 0, + "CONFIDENCE.LOW": 0, + "CONFIDENCE.MEDIUM": 0, + "CONFIDENCE.HIGH": 0 + }, + "./pygoat/introduction/urls.py": { + "loc": 119, + "nosec": 0, + "skipped_tests": 0, + "SEVERITY.UNDEFINED": 0, + "SEVERITY.LOW": 0, + "SEVERITY.MEDIUM": 0, + "SEVERITY.HIGH": 0, + "CONFIDENCE.UNDEFINED": 0, + "CONFIDENCE.LOW": 0, + "CONFIDENCE.MEDIUM": 0, + "CONFIDENCE.HIGH": 0 + }, + "./pygoat/introduction/utility.py": { + "loc": 47, + "nosec": 0, + "skipped_tests": 0, + "SEVERITY.UNDEFINED": 0, + "SEVERITY.LOW": 0, + "SEVERITY.MEDIUM": 0, + "SEVERITY.HIGH": 0, + "CONFIDENCE.UNDEFINED": 0, + "CONFIDENCE.LOW": 0, + "CONFIDENCE.MEDIUM": 0, + "CONFIDENCE.HIGH": 0 + }, + "./pygoat/introduction/views.py": { + "loc": 993, + "nosec": 0, + "skipped_tests": 0, + "SEVERITY.UNDEFINED": 0, + "SEVERITY.LOW": 17, + "SEVERITY.MEDIUM": 8, + "SEVERITY.HIGH": 2, + "CONFIDENCE.UNDEFINED": 0, + "CONFIDENCE.LOW": 3, + "CONFIDENCE.MEDIUM": 9, + "CONFIDENCE.HIGH": 15 + }, + "./pygoat/manage.py": { + "loc": 16, + "nosec": 0, + "skipped_tests": 0, + "SEVERITY.UNDEFINED": 0, + "SEVERITY.LOW": 0, + "SEVERITY.MEDIUM": 0, + "SEVERITY.HIGH": 0, + "CONFIDENCE.UNDEFINED": 0, + "CONFIDENCE.LOW": 0, + "CONFIDENCE.MEDIUM": 0, + "CONFIDENCE.HIGH": 0 + }, + "./pygoat/pygoat/__init__.py": { + "loc": 0, + "nosec": 0, + "skipped_tests": 0, + "SEVERITY.UNDEFINED": 0, + "SEVERITY.LOW": 0, + "SEVERITY.MEDIUM": 0, + "SEVERITY.HIGH": 0, + "CONFIDENCE.UNDEFINED": 0, + "CONFIDENCE.LOW": 0, + "CONFIDENCE.MEDIUM": 0, + "CONFIDENCE.HIGH": 0 + }, + "./pygoat/pygoat/asgi.py": { + "loc": 10, + "nosec": 0, + "skipped_tests": 0, + "SEVERITY.UNDEFINED": 0, + "SEVERITY.LOW": 0, + "SEVERITY.MEDIUM": 0, + "SEVERITY.HIGH": 0, + "CONFIDENCE.UNDEFINED": 0, + "CONFIDENCE.LOW": 0, + "CONFIDENCE.MEDIUM": 0, + "CONFIDENCE.HIGH": 0 + }, + "./pygoat/pygoat/settings.py": { + "loc": 108, + "nosec": 0, + "skipped_tests": 0, + "SEVERITY.UNDEFINED": 0, + "SEVERITY.LOW": 2, + "SEVERITY.MEDIUM": 0, + "SEVERITY.HIGH": 0, + "CONFIDENCE.UNDEFINED": 0, + "CONFIDENCE.LOW": 0, + "CONFIDENCE.MEDIUM": 2, + "CONFIDENCE.HIGH": 0 + }, + "./pygoat/pygoat/urls.py": { + "loc": 26, + "nosec": 0, + "skipped_tests": 0, + "SEVERITY.UNDEFINED": 0, + "SEVERITY.LOW": 0, + "SEVERITY.MEDIUM": 0, + "SEVERITY.HIGH": 0, + "CONFIDENCE.UNDEFINED": 0, + "CONFIDENCE.LOW": 0, + "CONFIDENCE.MEDIUM": 0, + "CONFIDENCE.HIGH": 0 + }, + "./pygoat/pygoat/wsgi.py": { + "loc": 10, + "nosec": 0, + "skipped_tests": 0, + "SEVERITY.UNDEFINED": 0, + "SEVERITY.LOW": 0, + "SEVERITY.MEDIUM": 0, + "SEVERITY.HIGH": 0, + "CONFIDENCE.UNDEFINED": 0, + "CONFIDENCE.LOW": 0, + "CONFIDENCE.MEDIUM": 0, + "CONFIDENCE.HIGH": 0 + }, + "./pygoat/setup.py": { + "loc": 19, + "nosec": 0, + "skipped_tests": 0, + "SEVERITY.UNDEFINED": 0, + "SEVERITY.LOW": 0, + "SEVERITY.MEDIUM": 0, + "SEVERITY.HIGH": 0, + "CONFIDENCE.UNDEFINED": 0, + "CONFIDENCE.LOW": 0, + "CONFIDENCE.MEDIUM": 0, + "CONFIDENCE.HIGH": 0 + }, + "./pygoat/temp.py": { + "loc": 0, + "nosec": 0, + "skipped_tests": 0, + "SEVERITY.UNDEFINED": 0, + "SEVERITY.LOW": 0, + "SEVERITY.MEDIUM": 0, + "SEVERITY.HIGH": 0, + "CONFIDENCE.UNDEFINED": 0, + "CONFIDENCE.LOW": 0, + "CONFIDENCE.MEDIUM": 0, + "CONFIDENCE.HIGH": 0 + }, + "./pygoat/uninstaller.py": { + "loc": 102, + "nosec": 0, + "skipped_tests": 0, + "SEVERITY.UNDEFINED": 0, + "SEVERITY.LOW": 4, + "SEVERITY.MEDIUM": 0, + "SEVERITY.HIGH": 0, + "CONFIDENCE.UNDEFINED": 0, + "CONFIDENCE.LOW": 0, + "CONFIDENCE.MEDIUM": 0, + "CONFIDENCE.HIGH": 4 + } + } + }, + "results": [ + { + "message": { + "text": "Consider possible security implications associated with the subprocess module." + }, + "level": "note", + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": "import yaml, subprocess\n" + }, + "startLine": 18 + }, + "artifactLocation": { + "uri": "pygoat/introduction/lab_code/test.py" + }, + "contextRegion": { + "snippet": { + "text": "'''\nimport yaml, subprocess\nstream = open('/home/fox/test.yaml', 'r')\n" + }, + "endLine": 19, + "startLine": 17 + } + } + } + ], + "properties": { + "issue_confidence": "HIGH", + "issue_severity": "LOW" + }, + "taxa": [ + { + "id": "78", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "2a313714-e7ec-4d06-891c-883519aac8c1" + } + } + ], + "ruleId": "B404", + "ruleIndex": 0 + }, + { + "message": { + "text": "Use of unsafe yaml load. Allows instantiation of arbitrary objects. Consider yaml.safe_load()." + }, + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": "data = yaml.load(stream)\n" + }, + "startLine": 20 + }, + "artifactLocation": { + "uri": "pygoat/introduction/lab_code/test.py" + }, + "contextRegion": { + "snippet": { + "text": "stream = open('/home/fox/test.yaml', 'r')\ndata = yaml.load(stream)\n\n" + }, + "endLine": 21, + "startLine": 19 + } + } + } + ], + "properties": { + "issue_confidence": "HIGH", + "issue_severity": "MEDIUM" + }, + "taxa": [ + { + "id": "20", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "4df5d1f6-f142-44ad-938c-bc0073aacb9c" + } + } + ], + "ruleId": "B506", + "ruleIndex": 1 + }, + { + "message": { + "text": "Consider possible security implications associated with the subprocess module." + }, + "level": "note", + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": "import subprocess\n" + }, + "startLine": 8 + }, + "artifactLocation": { + "uri": "pygoat/introduction/mitre.py" + }, + "contextRegion": { + "snippet": { + "text": "import re\nimport subprocess\nfrom .models import CSRF_user_tbl\n" + }, + "endLine": 9, + "startLine": 7 + } + } + } + ], + "properties": { + "issue_confidence": "HIGH", + "issue_severity": "LOW" + }, + "taxa": [ + { + "id": "78", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "d31d38f6-e89e-4388-9ff5-65a875065568" + } + } + ], + "ruleId": "B404", + "ruleIndex": 0 + }, + { + "message": { + "text": "Use of weak MD5 hash for security. Consider usedforsecurity=False" + }, + "level": "error", + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": " password = md5(password.encode()).hexdigest()\n" + }, + "startLine": 158 + }, + "artifactLocation": { + "uri": "pygoat/introduction/mitre.py" + }, + "contextRegion": { + "snippet": { + "text": " username = request.POST.get('username')\n password = md5(password.encode()).hexdigest()\n User = CSRF_user_tbl.objects.filter(username=username, password=password)\n" + }, + "endLine": 159, + "startLine": 157 + } + } + } + ], + "properties": { + "issue_confidence": "HIGH", + "issue_severity": "HIGH" + }, + "taxa": [ + { + "id": "327", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "1e380f40-6a0e-4675-8c4f-aaf11529cf23" + } + } + ], + "ruleId": "B324", + "ruleIndex": 2 + }, + { + "message": { + "text": "Use of possibly insecure function - consider using safer ast.literal_eval." + }, + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": " result = eval(expression)\n" + }, + "startLine": 215 + }, + "artifactLocation": { + "uri": "pygoat/introduction/mitre.py" + }, + "contextRegion": { + "snippet": { + "text": " expression = request.POST.get('expression')\n result = eval(expression)\n return JsonResponse({'result': result})\n" + }, + "endLine": 216, + "startLine": 214 + } + } + } + ], + "properties": { + "issue_confidence": "HIGH", + "issue_severity": "MEDIUM" + }, + "taxa": [ + { + "id": "78", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "1ed4f2e5-bf4f-429a-8c49-1e48e7f50dd1" + } + } + ], + "ruleId": "B307", + "ruleIndex": 3 + }, + { + "message": { + "text": "subprocess call with shell=True identified, security issue." + }, + "level": "error", + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": " process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n" + }, + "startLine": 230 + }, + "artifactLocation": { + "uri": "pygoat/introduction/mitre.py" + }, + "contextRegion": { + "snippet": { + "text": "def command_out(command):\n process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n return process.communicate()\n" + }, + "endLine": 231, + "startLine": 229 + } + } + } + ], + "properties": { + "issue_confidence": "HIGH", + "issue_severity": "HIGH" + }, + "taxa": [ + { + "id": "78", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "c9e65fc7-681b-4a5b-9c31-ea7b83fd7863" + } + } + ], + "ruleId": "B602", + "ruleIndex": 4 + }, + { + "message": { + "text": "Requests call without timeout" + }, + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": " response = requests.get(url)\n" + }, + "startLine": 8 + }, + "artifactLocation": { + "uri": "pygoat/introduction/playground/A6/soln.py" + }, + "contextRegion": { + "snippet": { + "text": " url = f\"https://pypi.org/pypi/{k[0]}/{k[1]}/json\"\n response = requests.get(url)\n response.raise_for_status()\n" + }, + "endLine": 9, + "startLine": 7 + } + } + } + ], + "properties": { + "issue_confidence": "LOW", + "issue_severity": "MEDIUM" + }, + "taxa": [ + { + "id": "400", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "2218034d-c7b3-4ffc-b80f-86ccdc3dff87" + } + } + ], + "ruleId": "B113", + "ruleIndex": 5 + }, + { + "message": { + "text": "Requests call without timeout" + }, + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": " response = requests.get(url)\n" + }, + "startLine": 8 + }, + "artifactLocation": { + "uri": "pygoat/introduction/playground/A6/utility.py" + }, + "contextRegion": { + "snippet": { + "text": " url = f\"https://pypi.org/pypi/{k[0]}/{k[1]}/json\"\n response = requests.get(url)\n response.raise_for_status()\n" + }, + "endLine": 9, + "startLine": 7 + } + } + } + ], + "properties": { + "issue_confidence": "LOW", + "issue_severity": "MEDIUM" + }, + "taxa": [ + { + "id": "400", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "03572b95-5d4f-4407-b026-0c3485bf8a10" + } + } + ], + "ruleId": "B113", + "ruleIndex": 5 + }, + { + "message": { + "text": "Possible hardcoded password: 'admin'" + }, + "level": "note", + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": " if username == \"admin\" and password == \"admin\":\n" + }, + "startLine": 15 + }, + "artifactLocation": { + "uri": "pygoat/introduction/playground/A9/api.py" + }, + "contextRegion": { + "snippet": { + "text": " L.info(f\"POST request with username {username} and password {password}\")\n if username == \"admin\" and password == \"admin\":\n return JsonResponse({\"message\":\"Loged in successfully\", \"method\":\"post\"},status = 200)\n" + }, + "endLine": 16, + "startLine": 14 + } + } + } + ], + "properties": { + "issue_confidence": "MEDIUM", + "issue_severity": "LOW" + }, + "taxa": [ + { + "id": "259", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "f224cd16-a426-485e-b0bd-a6d206923535" + } + } + ], + "ruleId": "B105", + "ruleIndex": 6 + }, + { + "message": { + "text": "Possible hardcoded password: 'admin'" + }, + "level": "note", + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": " if username == \"admin\" and password == \"admin\":\n" + }, + "startLine": 15 + }, + "artifactLocation": { + "uri": "pygoat/introduction/playground/A9/archive.py" + }, + "contextRegion": { + "snippet": { + "text": " L.info(f\"POST request with username {username} and password {password}\")\n if username == \"admin\" and password == \"admin\":\n return JsonResponse({\"message\":\"Loged in successfully\", \"method\":\"post\"},status = 200)\n" + }, + "endLine": 16, + "startLine": 14 + } + } + } + ], + "properties": { + "issue_confidence": "MEDIUM", + "issue_severity": "LOW" + }, + "taxa": [ + { + "id": "259", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "3dbf0cc8-8e7d-415c-9c13-bb5e74f1350b" + } + } + ], + "ruleId": "B105", + "ruleIndex": 6 + }, + { + "message": { + "text": "Using parseString to parse untrusted XML data is known to be vulnerable to XML attacks. Replace parseString with the equivalent defusedxml package, or make sure defusedxml.defuse_stdlib() is called." + }, + "level": "note", + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": "from xml.dom.pulldom import parseString, START_ELEMENT\n" + }, + "startLine": 20 + }, + "artifactLocation": { + "uri": "pygoat/introduction/views.py" + }, + "contextRegion": { + "snippet": { + "text": "from random import randint\nfrom xml.dom.pulldom import parseString, START_ELEMENT\nfrom xml.sax.handler import feature_external_ges\n" + }, + "endLine": 21, + "startLine": 19 + } + } + } + ], + "properties": { + "issue_confidence": "HIGH", + "issue_severity": "LOW" + }, + "taxa": [ + { + "id": "20", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "78279d40-f51b-43cb-86b4-c12ba4a5b470" + } + } + ], + "ruleId": "B409", + "ruleIndex": 7 + }, + { + "message": { + "text": "Using feature_external_ges to parse untrusted XML data is known to be vulnerable to XML attacks. Replace feature_external_ges with the equivalent defusedxml package, or make sure defusedxml.defuse_stdlib() is called." + }, + "level": "note", + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": "from xml.sax.handler import feature_external_ges\n" + }, + "startLine": 21 + }, + "artifactLocation": { + "uri": "pygoat/introduction/views.py" + }, + "contextRegion": { + "snippet": { + "text": "from xml.dom.pulldom import parseString, START_ELEMENT\nfrom xml.sax.handler import feature_external_ges\nfrom xml.sax import make_parser\n" + }, + "endLine": 22, + "startLine": 20 + } + } + } + ], + "properties": { + "issue_confidence": "HIGH", + "issue_severity": "LOW" + }, + "taxa": [ + { + "id": "20", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "b7925bca-8012-4cc9-a344-38640e87d187" + } + } + ], + "ruleId": "B406", + "ruleIndex": 8 + }, + { + "message": { + "text": "Using make_parser to parse untrusted XML data is known to be vulnerable to XML attacks. Replace make_parser with the equivalent defusedxml package, or make sure defusedxml.defuse_stdlib() is called." + }, + "level": "note", + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": "from xml.sax import make_parser\n" + }, + "startLine": 22 + }, + "artifactLocation": { + "uri": "pygoat/introduction/views.py" + }, + "contextRegion": { + "snippet": { + "text": "from xml.sax.handler import feature_external_ges\nfrom xml.sax import make_parser\nfrom django.views.decorators.csrf import csrf_exempt\n" + }, + "endLine": 23, + "startLine": 21 + } + } + } + ], + "properties": { + "issue_confidence": "HIGH", + "issue_severity": "LOW" + }, + "taxa": [ + { + "id": "20", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "74563cb3-89b9-4a66-a606-83fe898cb16b" + } + } + ], + "ruleId": "B406", + "ruleIndex": 8 + }, + { + "message": { + "text": "Consider possible security implications associated with the subprocess module." + }, + "level": "note", + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": "import subprocess\n" + }, + "startLine": 26 + }, + "artifactLocation": { + "uri": "pygoat/introduction/views.py" + }, + "contextRegion": { + "snippet": { + "text": "from django.template.loader import render_to_string\nimport subprocess\nimport pickle\n" + }, + "endLine": 27, + "startLine": 25 + } + } + } + ], + "properties": { + "issue_confidence": "HIGH", + "issue_severity": "LOW" + }, + "taxa": [ + { + "id": "78", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "f74f08c3-dbf9-4920-8796-4058e9432151" + } + } + ], + "ruleId": "B404", + "ruleIndex": 0 + }, + { + "message": { + "text": "Consider possible security implications associated with pickle module." + }, + "level": "note", + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": "import pickle\n" + }, + "startLine": 27 + }, + "artifactLocation": { + "uri": "pygoat/introduction/views.py" + }, + "contextRegion": { + "snippet": { + "text": "import subprocess\nimport pickle\nimport base64\n" + }, + "endLine": 28, + "startLine": 26 + } + } + } + ], + "properties": { + "issue_confidence": "HIGH", + "issue_severity": "LOW" + }, + "taxa": [ + { + "id": "502", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "cce2454a-4686-4184-8c41-1cb5b7a94fb3" + } + } + ], + "ruleId": "B403", + "ruleIndex": 9 + }, + { + "message": { + "text": "Possible SQL injection vector through string-based query construction." + }, + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": " sql_query = \"SELECT * FROM introduction_login WHERE user='\"+name+\"'AND password='\"+password+\"'\"\n" + }, + "startLine": 155 + }, + "artifactLocation": { + "uri": "pygoat/introduction/views.py" + }, + "contextRegion": { + "snippet": { + "text": "\n sql_query = \"SELECT * FROM introduction_login WHERE user='\"+name+\"'AND password='\"+password+\"'\"\n print(sql_query)\n" + }, + "endLine": 156, + "startLine": 154 + } + } + } + ], + "properties": { + "issue_confidence": "LOW", + "issue_severity": "MEDIUM" + }, + "taxa": [ + { + "id": "89", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "278114e0-91aa-4d74-981c-b3a2385de92d" + } + } + ], + "ruleId": "B608", + "ruleIndex": 10 + }, + { + "message": { + "text": "Pickle and modules that wrap it can be unsafe when used to deserialize untrusted data, possible security issue." + }, + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": " admin = pickle.loads(token)\n" + }, + "startLine": 211 + }, + "artifactLocation": { + "uri": "pygoat/introduction/views.py" + }, + "contextRegion": { + "snippet": { + "text": " token = base64.b64decode(token)\n admin = pickle.loads(token)\n if admin.admin == 1:\n" + }, + "endLine": 212, + "startLine": 210 + } + } + } + ], + "properties": { + "issue_confidence": "HIGH", + "issue_severity": "MEDIUM" + }, + "taxa": [ + { + "id": "502", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "74118bc0-608d-46ce-b7e5-3381cb6091bf" + } + } + ], + "ruleId": "B301", + "ruleIndex": 11 + }, + { + "message": { + "text": "Using xml.sax.make_parser to parse untrusted XML data is known to be vulnerable to XML attacks. Replace xml.sax.make_parser with its defusedxml equivalent function or make sure defusedxml.defuse_stdlib() is called" + }, + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": " parser = make_parser()\n" + }, + "startLine": 250 + }, + "artifactLocation": { + "uri": "pygoat/introduction/views.py" + }, + "contextRegion": { + "snippet": { + "text": "\n parser = make_parser()\n parser.setFeature(feature_external_ges, True)\n" + }, + "endLine": 251, + "startLine": 249 + } + } + } + ], + "properties": { + "issue_confidence": "HIGH", + "issue_severity": "MEDIUM" + }, + "taxa": [ + { + "id": "20", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "d7a4edbb-0e8f-4308-b09a-bc6066a7b737" + } + } + ], + "ruleId": "B317", + "ruleIndex": 12 + }, + { + "message": { + "text": "Using xml.dom.pulldom.parseString to parse untrusted XML data is known to be vulnerable to XML attacks. Replace xml.dom.pulldom.parseString with its defusedxml equivalent function or make sure defusedxml.defuse_stdlib() is called" + }, + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": " doc = parseString(request.body.decode('utf-8'), parser=parser)\n" + }, + "startLine": 252 + }, + "artifactLocation": { + "uri": "pygoat/introduction/views.py" + }, + "contextRegion": { + "snippet": { + "text": " parser.setFeature(feature_external_ges, True)\n doc = parseString(request.body.decode('utf-8'), parser=parser)\n for event, node in doc:\n" + }, + "endLine": 253, + "startLine": 251 + } + } + } + ], + "properties": { + "issue_confidence": "HIGH", + "issue_severity": "MEDIUM" + }, + "taxa": [ + { + "id": "20", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "13976a35-127a-45a7-980e-305071492d5c" + } + } + ], + "ruleId": "B319", + "ruleIndex": 13 + }, + { + "message": { + "text": "subprocess call with shell=True identified, security issue." + }, + "level": "error", + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": " shell=True,\n" + }, + "startLine": 423 + }, + "artifactLocation": { + "uri": "pygoat/introduction/views.py" + }, + "contextRegion": { + "snippet": { + "text": " command,\n shell=True,\n stdout=subprocess.PIPE, \n stderr=subprocess.PIPE)\n stdout, stderr = process.communicate()\n data = stdout.decode('utf-8')\n stderr = stderr.decode('utf-8')\n" + }, + "endLine": 428, + "startLine": 422 + } + } + } + ], + "properties": { + "issue_confidence": "HIGH", + "issue_severity": "HIGH" + }, + "taxa": [ + { + "id": "78", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "5726be7c-fb62-402b-a00c-b97086331f26" + } + } + ], + "ruleId": "B602", + "ruleIndex": 4 + }, + { + "message": { + "text": "Use of possibly insecure function - consider using safer ast.literal_eval." + }, + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": " output = eval(val)\n" + }, + "startLine": 451 + }, + "artifactLocation": { + "uri": "pygoat/introduction/views.py" + }, + "contextRegion": { + "snippet": { + "text": " try:\n output = eval(val)\n except:\n" + }, + "endLine": 452, + "startLine": 450 + } + } + } + ], + "properties": { + "issue_confidence": "HIGH", + "issue_severity": "MEDIUM" + }, + "taxa": [ + { + "id": "78", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "eb984ec0-8af6-41ab-b82b-fc812a602d29" + } + } + ], + "ruleId": "B307", + "ruleIndex": 3 + }, + { + "message": { + "text": "Standard pseudo-random generators are not suitable for security/cryptographic purposes." + }, + "level": "note", + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": " otpN=randint(100,999)\n" + }, + "startLine": 487 + }, + "artifactLocation": { + "uri": "pygoat/introduction/views.py" + }, + "contextRegion": { + "snippet": { + "text": " email=request.GET.get('email')\n otpN=randint(100,999)\n if email and otpN:\n" + }, + "endLine": 488, + "startLine": 486 + } + } + } + ], + "properties": { + "issue_confidence": "HIGH", + "issue_severity": "LOW" + }, + "taxa": [ + { + "id": "330", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "5e36e82c-de00-4bf3-b299-9995df5db58d" + } + } + ], + "ruleId": "B311", + "ruleIndex": 14 + }, + { + "message": { + "text": "Use of unsafe yaml load. Allows instantiation of arbitrary objects. Consider yaml.safe_load()." + }, + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": " data = yaml.load(file,yaml.Loader)\n" + }, + "startLine": 551 + }, + "artifactLocation": { + "uri": "pygoat/introduction/views.py" + }, + "contextRegion": { + "snippet": { + "text": " try :\n data = yaml.load(file,yaml.Loader)\n \n" + }, + "endLine": 552, + "startLine": 550 + } + } + } + ], + "properties": { + "issue_confidence": "HIGH", + "issue_severity": "MEDIUM" + }, + "taxa": [ + { + "id": "20", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "7d8fec63-d2d4-420f-bcfe-3ddeb2e1c6cb" + } + } + ], + "ruleId": "B506", + "ruleIndex": 1 + }, + { + "message": { + "text": "Standard pseudo-random generators are not suitable for security/cryptographic purposes." + }, + "level": "note", + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": " return (''.join(random.choices(string.ascii_uppercase + string.ascii_lowercase, k=10)))\n" + }, + "startLine": 671 + }, + "artifactLocation": { + "uri": "pygoat/introduction/views.py" + }, + "contextRegion": { + "snippet": { + "text": "def gentckt():\n return (''.join(random.choices(string.ascii_uppercase + string.ascii_lowercase, k=10)))\n\n" + }, + "endLine": 672, + "startLine": 670 + } + } + } + ], + "properties": { + "issue_confidence": "HIGH", + "issue_severity": "LOW" + }, + "taxa": [ + { + "id": "330", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "914e4d43-f404-43fa-bc49-c54d6de0374a" + } + } + ], + "ruleId": "B311", + "ruleIndex": 14 + }, + { + "message": { + "text": "Possible hardcoded password: 'jacktheripper'" + }, + "level": "note", + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": " elif (name=='jack' and password=='jacktheripper'): # Will implement hashing here\n" + }, + "startLine": 757 + }, + "artifactLocation": { + "uri": "pygoat/introduction/views.py" + }, + "contextRegion": { + "snippet": { + "text": " })\n elif (name=='jack' and password=='jacktheripper'): # Will implement hashing here\n html = render(\n" + }, + "endLine": 758, + "startLine": 756 + } + } + } + ], + "properties": { + "issue_confidence": "MEDIUM", + "issue_severity": "LOW" + }, + "taxa": [ + { + "id": "259", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "8857a324-2fce-4d64-afe1-09bd0da215f6" + } + } + ], + "ruleId": "B105", + "ruleIndex": 6 + }, + { + "message": { + "text": "Possible hardcoded password: 'jacktheripper'" + }, + "level": "note", + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": " elif ( name=='jack' and password=='jacktheripper'): # Will implement hashing here\n" + }, + "startLine": 797 + }, + "artifactLocation": { + "uri": "pygoat/introduction/views.py" + }, + "contextRegion": { + "snippet": { + "text": " })\n elif ( name=='jack' and password=='jacktheripper'): # Will implement hashing here\n html = render(\n" + }, + "endLine": 798, + "startLine": 796 + } + } + } + ], + "properties": { + "issue_confidence": "MEDIUM", + "issue_severity": "LOW" + }, + "taxa": [ + { + "id": "259", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "1ddc12ea-6aac-4476-96cf-39922601f8f0" + } + } + ], + "ruleId": "B105", + "ruleIndex": 6 + }, + { + "message": { + "text": "Possible hardcoded password: 'reaper'" + }, + "level": "note", + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": " if username == 'John' and password == 'reaper':\n" + }, + "startLine": 822 + }, + "artifactLocation": { + "uri": "pygoat/introduction/views.py" + }, + "contextRegion": { + "snippet": { + "text": "\n if username == 'John' and password == 'reaper':\n return render(request,'Lab_2021/A1_BrokenAccessControl/broken_access_lab_3.html', {'loggedin':True, 'admin': False})\n" + }, + "endLine": 823, + "startLine": 821 + } + } + } + ], + "properties": { + "issue_confidence": "MEDIUM", + "issue_severity": "LOW" + }, + "taxa": [ + { + "id": "259", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "2f19ea0b-76bf-4405-b180-e4a8521196b1" + } + } + ], + "ruleId": "B105", + "ruleIndex": 6 + }, + { + "message": { + "text": "Possible hardcoded password: 'admin_pass'" + }, + "level": "note", + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": " elif username == 'admin' and password == 'admin_pass':\n" + }, + "startLine": 824 + }, + "artifactLocation": { + "uri": "pygoat/introduction/views.py" + }, + "contextRegion": { + "snippet": { + "text": " return render(request,'Lab_2021/A1_BrokenAccessControl/broken_access_lab_3.html', {'loggedin':True, 'admin': False})\n elif username == 'admin' and password == 'admin_pass':\n return render(request,'Lab_2021/A1_BrokenAccessControl/broken_access_lab_3.html', {'loggedin':True, 'admin': True})\n" + }, + "endLine": 825, + "startLine": 823 + } + } + } + ], + "properties": { + "issue_confidence": "MEDIUM", + "issue_severity": "LOW" + }, + "taxa": [ + { + "id": "259", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "e5d9a878-8783-4674-827e-5a06b58be463" + } + } + ], + "ruleId": "B105", + "ruleIndex": 6 + }, + { + "message": { + "text": "Possible SQL injection vector through string-based query construction." + }, + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": " sql_query = \"SELECT * FROM introduction_sql_lab_table WHERE id='\"+name+\"'AND password='\"+password+\"'\"\n" + }, + "startLine": 855 + }, + "artifactLocation": { + "uri": "pygoat/introduction/views.py" + }, + "contextRegion": { + "snippet": { + "text": " if name:\n sql_query = \"SELECT * FROM introduction_sql_lab_table WHERE id='\"+name+\"'AND password='\"+password+\"'\"\n\n" + }, + "endLine": 856, + "startLine": 854 + } + } + } + ], + "properties": { + "issue_confidence": "LOW", + "issue_severity": "MEDIUM" + }, + "taxa": [ + { + "id": "89", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "171744a8-98a4-47a0-9b27-ce23cecc0a9f" + } + } + ], + "ruleId": "B608", + "ruleIndex": 10 + }, + { + "message": { + "text": "Possible hardcoded password: '65079b006e85a7e798abecb99e47c154'" + }, + "level": "note", + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": " sql_instance = sql_lab_table(id=\"admin\", password=\"65079b006e85a7e798abecb99e47c154\")\n" + }, + "startLine": 857 + }, + "artifactLocation": { + "uri": "pygoat/introduction/views.py" + }, + "contextRegion": { + "snippet": { + "text": "\n sql_instance = sql_lab_table(id=\"admin\", password=\"65079b006e85a7e798abecb99e47c154\")\n sql_instance.save()\n" + }, + "endLine": 858, + "startLine": 856 + } + } + } ], "properties": { - "issue_confidence": "HIGH", + "issue_confidence": "MEDIUM", "issue_severity": "LOW" }, - "ruleId": "B406", - "ruleIndex": 1 + "taxa": [ + { + "id": "259", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "2ca4e647-bd20-4dd4-a8dc-68e266c36629" + } + } + ], + "ruleId": "B106", + "ruleIndex": 15 }, { "message": { - "text": "Consider possible security implications associated with the subprocess module." + "text": "Possible hardcoded password: 'jack'" }, "level": "note", "locations": [ @@ -516,33 +2408,43 @@ "physicalLocation": { "region": { "snippet": { - "text": "import subprocess\n" + "text": " sql_instance = sql_lab_table(id=\"jack\", password=\"jack\")\n" }, - "startLine": 19 + "startLine": 859 }, "artifactLocation": { - "uri": "pygoat/pygoat/introduction/views.py" + "uri": "pygoat/introduction/views.py" }, "contextRegion": { "snippet": { - "text": "from django.template.loader import render_to_string\nimport subprocess\nimport pickle\n" + "text": " sql_instance.save()\n sql_instance = sql_lab_table(id=\"jack\", password=\"jack\")\n sql_instance.save()\n" }, - "endLine": 20, - "startLine": 18 + "endLine": 860, + "startLine": 858 } } } ], "properties": { - "issue_confidence": "HIGH", + "issue_confidence": "MEDIUM", "issue_severity": "LOW" }, - "ruleId": "B404", - "ruleIndex": 2 + "taxa": [ + { + "id": "259", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "fc0b2b72-6acc-4e14-9f58-95315c8d8718" + } + } + ], + "ruleId": "B106", + "ruleIndex": 15 }, { "message": { - "text": "Consider possible security implications associated with pickle module." + "text": "Possible hardcoded password: 'b4f945433ea4c369c12741f62a23ccc0'" }, "level": "note", "locations": [ @@ -550,199 +2452,394 @@ "physicalLocation": { "region": { "snippet": { - "text": "import pickle\n" + "text": " sql_instance = sql_lab_table(id=\"slinky\", password=\"b4f945433ea4c369c12741f62a23ccc0\")\n" }, - "startLine": 20 + "startLine": 861 }, "artifactLocation": { - "uri": "pygoat/pygoat/introduction/views.py" + "uri": "pygoat/introduction/views.py" }, "contextRegion": { "snippet": { - "text": "import subprocess\nimport pickle\nimport base64\n" + "text": " sql_instance.save()\n sql_instance = sql_lab_table(id=\"slinky\", password=\"b4f945433ea4c369c12741f62a23ccc0\")\n sql_instance.save()\n" }, - "endLine": 21, - "startLine": 19 + "endLine": 862, + "startLine": 860 } } } ], "properties": { - "issue_confidence": "HIGH", + "issue_confidence": "MEDIUM", "issue_severity": "LOW" }, - "ruleId": "B403", - "ruleIndex": 3 + "taxa": [ + { + "id": "259", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "3551cf6b-7711-4614-8ad7-fee021ff5aec" + } + } + ], + "ruleId": "B106", + "ruleIndex": 15 }, { "message": { - "text": "Possible SQL injection vector through string-based query construction." + "text": "Possible hardcoded password: 'f8d1ce191319ea8f4d1d26e65e130dd5'" }, + "level": "note", "locations": [ { "physicalLocation": { "region": { "snippet": { - "text": " val=login.objects.raw(\"SELECT * FROM introduction_login WHERE user='\"+name+\"'AND password='\"+password+\"'\")\n" + "text": " sql_instance = sql_lab_table(id=\"bloke\", password=\"f8d1ce191319ea8f4d1d26e65e130dd5\")\n" }, - "startLine": 86 + "startLine": 863 }, "artifactLocation": { - "uri": "pygoat/pygoat/introduction/views.py" + "uri": "pygoat/introduction/views.py" }, "contextRegion": { "snippet": { - "text": "\n val=login.objects.raw(\"SELECT * FROM introduction_login WHERE user='\"+name+\"'AND password='\"+password+\"'\")\n\n" + "text": " sql_instance.save()\n sql_instance = sql_lab_table(id=\"bloke\", password=\"f8d1ce191319ea8f4d1d26e65e130dd5\")\n sql_instance.save()\n" }, - "endLine": 87, - "startLine": 85 + "endLine": 864, + "startLine": 862 } } } ], "properties": { - "issue_confidence": "LOW", - "issue_severity": "MEDIUM" + "issue_confidence": "MEDIUM", + "issue_severity": "LOW" }, - "ruleId": "B608", - "ruleIndex": 4 + "taxa": [ + { + "id": "259", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "aefc3def-f137-4bc5-811d-6967be8f9b24" + } + } + ], + "ruleId": "B106", + "ruleIndex": 15 }, { "message": { - "text": "Pickle and modules that wrap it can be unsafe when used to deserialize untrusted data, possible security issue." + "text": "Requests call without timeout" }, "locations": [ { "physicalLocation": { "region": { "snippet": { - "text": " admin = pickle.loads(token)\n" + "text": " response = requests.get(url)\n" }, - "startLine": 122 + "startLine": 954 }, "artifactLocation": { - "uri": "pygoat/pygoat/introduction/views.py" + "uri": "pygoat/introduction/views.py" }, "contextRegion": { "snippet": { - "text": " token = base64.b64decode(token)\n admin = pickle.loads(token)\n if admin.admin == 1:\n" + "text": " try:\n response = requests.get(url)\n return render(request, \"Lab/ssrf/ssrf_lab2.html\", {\"response\": response.content.decode()})\n" }, - "endLine": 123, - "startLine": 121 + "endLine": 955, + "startLine": 953 } } } ], "properties": { - "issue_confidence": "HIGH", + "issue_confidence": "LOW", "issue_severity": "MEDIUM" }, - "ruleId": "B301", + "taxa": [ + { + "id": "400", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "0dbf60bb-53ce-4be1-a522-c302ee563be2" + } + } + ], + "ruleId": "B113", "ruleIndex": 5 }, { "message": { - "text": "Using xml.sax.make_parser to parse untrusted XML data is known to be vulnerable to XML attacks. Replace xml.sax.make_parser with its defusedxml equivalent function or make sure defusedxml.defuse_stdlib() is called" + "text": "Use of weak MD5 hash for security. Consider usedforsecurity=False" }, + "level": "error", "locations": [ { "physicalLocation": { "region": { "snippet": { - "text": " parser = make_parser()\n" + "text": " password = md5(password.encode()).hexdigest()\n" }, - "startLine": 161 + "startLine": 1017 }, "artifactLocation": { - "uri": "pygoat/pygoat/introduction/views.py" + "uri": "pygoat/introduction/views.py" }, "contextRegion": { "snippet": { - "text": "\n parser = make_parser()\n parser.setFeature(feature_external_ges, True)\n" + "text": " try:\n password = md5(password.encode()).hexdigest()\n user = CF_user.objects.get(username=username,password=password)\n" }, - "endLine": 162, - "startLine": 160 + "endLine": 1018, + "startLine": 1016 } } } ], "properties": { "issue_confidence": "HIGH", - "issue_severity": "MEDIUM" + "issue_severity": "HIGH" }, - "ruleId": "B317", + "taxa": [ + { + "id": "327", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "333a4f8b-57a3-478a-9053-9682a122855e" + } + } + ], + "ruleId": "B324", + "ruleIndex": 2 + }, + { + "message": { + "text": "Possible hardcoded password: 'P@$$w0rd'" + }, + "level": "note", + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": " if username == \"User\" and password == \"P@$$w0rd\":\n" + }, + "startLine": 1063 + }, + "artifactLocation": { + "uri": "pygoat/introduction/views.py" + }, + "contextRegion": { + "snippet": { + "text": " try:\n if username == \"User\" and password == \"P@$$w0rd\":\n expire = datetime.datetime.now() + datetime.timedelta(minutes=60)\n" + }, + "endLine": 1064, + "startLine": 1062 + } + } + } + ], + "properties": { + "issue_confidence": "MEDIUM", + "issue_severity": "LOW" + }, + "taxa": [ + { + "id": "259", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "7b586ac9-090d-40c3-a323-156009d33bb5" + } + } + ], + "ruleId": "B105", "ruleIndex": 6 }, { "message": { - "text": "Using xml.dom.pulldom.parseString to parse untrusted XML data is known to be vulnerable to XML attacks. Replace xml.dom.pulldom.parseString with its defusedxml equivalent function or make sure defusedxml.defuse_stdlib() is called" + "text": "Try, Except, Pass detected." }, + "level": "note", "locations": [ { "physicalLocation": { "region": { "snippet": { - "text": " doc = parseString(request.body.decode('utf-8'), parser=parser)\n" + "text": " except:\n" }, - "startLine": 163 + "startLine": 1176 }, "artifactLocation": { - "uri": "pygoat/pygoat/introduction/views.py" + "uri": "pygoat/introduction/views.py" }, "contextRegion": { "snippet": { - "text": " parser.setFeature(feature_external_ges, True)\n doc = parseString(request.body.decode('utf-8'), parser=parser)\n for event, node in doc:\n" + "text": " return render(request,\"Lab_2021/A7_auth_failure/lab3.html\", {\"username\":session.user,\"success\":True})\n except:\n pass\n return render(request, \"Lab_2021/A7_auth_failure/lab3.html\")\n" }, - "endLine": 164, - "startLine": 162 + "endLine": 1178, + "startLine": 1175 } } } ], "properties": { "issue_confidence": "HIGH", - "issue_severity": "MEDIUM" + "issue_severity": "LOW" }, - "ruleId": "B319", - "ruleIndex": 7 + "taxa": [ + { + "id": "703", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "651d3ad2-fc6d-4507-8b3c-ed285251c503" + } + } + ], + "ruleId": "B110", + "ruleIndex": 16 }, { "message": { - "text": "subprocess call with shell=True identified, security issue." + "text": "Possible hardcoded password: 'lr66%-a!$km5ed@n5ug!tya5bv!0(yqwa1tn!q%0%3m2nh%oml'" }, - "level": "error", + "level": "note", + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": "SECRET_KEY = 'lr66%-a!$km5ed@n5ug!tya5bv!0(yqwa1tn!q%0%3m2nh%oml'\n" + }, + "startLine": 24 + }, + "artifactLocation": { + "uri": "pygoat/pygoat/settings.py" + }, + "contextRegion": { + "snippet": { + "text": "# SECURITY WARNING: keep the secret key used in production secret!\nSECRET_KEY = 'lr66%-a!$km5ed@n5ug!tya5bv!0(yqwa1tn!q%0%3m2nh%oml'\n\n" + }, + "endLine": 25, + "startLine": 23 + } + } + } + ], + "properties": { + "issue_confidence": "MEDIUM", + "issue_severity": "LOW" + }, + "taxa": [ + { + "id": "259", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "67c6d67a-5ea0-4866-bc01-8552aeeb8308" + } + } + ], + "ruleId": "B105", + "ruleIndex": 6 + }, + { + "message": { + "text": "Possible hardcoded password: 'PYGOAT'" + }, + "level": "note", + "locations": [ + { + "physicalLocation": { + "region": { + "snippet": { + "text": "SECRET_COOKIE_KEY = \"PYGOAT\"\n" + }, + "startLine": 169 + }, + "artifactLocation": { + "uri": "pygoat/pygoat/settings.py" + }, + "contextRegion": { + "snippet": { + "text": "\nSECRET_COOKIE_KEY = \"PYGOAT\"\nCSRF_TRUSTED_ORIGINS = [\"http://127.0.0.1:8000\",\"http://0.0.0.0:8000\",\"http://172.16.189.10\"]\n" + }, + "endLine": 170, + "startLine": 168 + } + } + } + ], + "properties": { + "issue_confidence": "MEDIUM", + "issue_severity": "LOW" + }, + "taxa": [ + { + "id": "259", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "84158e36-134f-4fe1-93fc-852512d8da80" + } + } + ], + "ruleId": "B105", + "ruleIndex": 6 + }, + { + "message": { + "text": "Consider possible security implications associated with the subprocess module." + }, + "level": "note", "locations": [ { "physicalLocation": { "region": { "snippet": { - "text": " output=subprocess.check_output(command,shell=True,encoding=\"UTF-8\");\n" + "text": "import subprocess\n" }, - "startLine": 312 + "startLine": 7 }, "artifactLocation": { - "uri": "pygoat/pygoat/introduction/views.py" + "uri": "pygoat/uninstaller.py" }, "contextRegion": { "snippet": { - "text": "\n output=subprocess.check_output(command,shell=True,encoding=\"UTF-8\");\n print(output)\n" + "text": "import colorama\nimport subprocess\nfrom shutil import rmtree, which\n" }, - "endLine": 313, - "startLine": 311 + "endLine": 8, + "startLine": 6 } } } ], "properties": { "issue_confidence": "HIGH", - "issue_severity": "HIGH" + "issue_severity": "LOW" }, - "ruleId": "B602", - "ruleIndex": 8 + "taxa": [ + { + "id": "78", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "fe63bd69-5389-414a-a237-62247271a272" + } + } + ], + "ruleId": "B404", + "ruleIndex": 0 }, { "message": { - "text": "Standard pseudo-random generators are not suitable for security/cryptographic purposes." + "text": "subprocess call - check for execution of untrusted input." }, "level": "note", "locations": [ @@ -750,19 +2847,19 @@ "physicalLocation": { "region": { "snippet": { - "text": " otpN=randint(100,999)\n" + "text": " subprocess.run([pip_v,\n" }, - "startLine": 344 + "startLine": 40 }, "artifactLocation": { - "uri": "pygoat/pygoat/introduction/views.py" + "uri": "pygoat/uninstaller.py" }, "contextRegion": { "snippet": { - "text": " email=request.GET.get('email');\n otpN=randint(100,999)\n if email and otpN:\n" + "text": " pip_v = \"pip3\" if (which('pip3') is not None) else \"pip\"\n subprocess.run([pip_v,\n \"install\",\n \"--upgrade\",\n \"pip\"],\n stdout=subprocess.DEVNULL,\n stderr=subprocess.DEVNULL)\n subprocess.check_call([sys.executable,\n" }, - "endLine": 345, - "startLine": 343 + "endLine": 46, + "startLine": 39 } } } @@ -771,45 +2868,66 @@ "issue_confidence": "HIGH", "issue_severity": "LOW" }, - "ruleId": "B311", - "ruleIndex": 9 + "taxa": [ + { + "id": "78", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "72da2294-7380-4984-b671-a955dc6811d9" + } + } + ], + "ruleId": "B603", + "ruleIndex": 17 }, { "message": { - "text": "Use of unsafe yaml load. Allows instantiation of arbitrary objects. Consider yaml.safe_load()." + "text": "subprocess call - check for execution of untrusted input." }, + "level": "note", "locations": [ { "physicalLocation": { "region": { "snippet": { - "text": " data = yaml.load(file)\n" + "text": " subprocess.check_call([sys.executable,\n" }, - "startLine": 407 + "startLine": 46 }, "artifactLocation": { - "uri": "pygoat/pygoat/introduction/views.py" + "uri": "pygoat/uninstaller.py" }, "contextRegion": { "snippet": { - "text": " try :\n data = yaml.load(file)\n return render(request,\"Lab/A9/a9_lab.html\",{\"data\":data})\n" + "text": " stderr=subprocess.DEVNULL)\n subprocess.check_call([sys.executable,\n \"-m\",\n \"pip\",\n \"uninstall\",\n \"-yr\",\n \"requirements.txt\"])\n except subprocess.CalledProcessError:\n" }, - "endLine": 408, - "startLine": 406 + "endLine": 52, + "startLine": 45 } } } ], "properties": { "issue_confidence": "HIGH", - "issue_severity": "MEDIUM" + "issue_severity": "LOW" }, - "ruleId": "B506", - "ruleIndex": 10 + "taxa": [ + { + "id": "78", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "9b4ce1ac-9788-42ba-aa20-cca1b922ec42" + } + } + ], + "ruleId": "B603", + "ruleIndex": 17 }, { "message": { - "text": "Possible hardcoded password: 'lr66%-a!$km5ed@n5ug!tya5bv!0(yqwa1tn!q%0%3m2nh%oml'" + "text": "subprocess call - check for execution of untrusted input." }, "level": "note", "locations": [ @@ -817,33 +2935,158 @@ "physicalLocation": { "region": { "snippet": { - "text": "SECRET_KEY = 'lr66%-a!$km5ed@n5ug!tya5bv!0(yqwa1tn!q%0%3m2nh%oml'\n" + "text": " subprocess.check_call([sys.executable,\n" }, - "startLine": 25 + "startLine": 61 }, "artifactLocation": { - "uri": "pygoat/pygoat/pygoat/settings.py" + "uri": "pygoat/uninstaller.py" }, "contextRegion": { "snippet": { - "text": "# SECURITY WARNING: keep the secret key used in production secret!\nSECRET_KEY = 'lr66%-a!$km5ed@n5ug!tya5bv!0(yqwa1tn!q%0%3m2nh%oml'\n\nSENSITIVE_DATA = 'FLAGTHATNEEDSTOBEFOUND'\n" + "text": " try:\n subprocess.check_call([sys.executable,\n \"-m\",\n \"pip\",\n \"uninstall\",\n \"-y\",\n \"pip\"])\n except subprocess.CalledProcessError:\n" }, - "endLine": 27, - "startLine": 24 + "endLine": 67, + "startLine": 60 } } } ], "properties": { - "issue_confidence": "MEDIUM", + "issue_confidence": "HIGH", "issue_severity": "LOW" }, - "ruleId": "B105", - "ruleIndex": 11 + "taxa": [ + { + "id": "78", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "toolComponent": { + "name": "CWE", + "guid": "29f46b80-7831-4384-b949-58617e176855" + } + } + ], + "ruleId": "B603", + "ruleIndex": 17 + } + ], + "taxonomies": [ + { + "name": "CWE", + "version": "4.12", + "releaseDateUtc": "2023-06-29", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "informationUri": "https://cwe.mitre.org/data/published/cwe_v4.12.pdf", + "downloadUri": "https://cwe.mitre.org/data/xml/cwec_v4.12.xml.zip", + "organization": "MITRE", + "shortDescription": { + "text": "The MITRE Common Weakness Enumeration" + }, + "taxa": [ + { + "id": "78", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "name": "Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')", + "shortDescription": { + "text": "The product constructs all or part of an OS command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended OS command when it is sent to a downstream component." + }, + "defaultConfiguration": { + "level": "error" + } + }, + { + "id": "20", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "name": "Improper Input Validation", + "shortDescription": { + "text": "The product receives input or data, but it does\n not validate or incorrectly validates that the input has the\n properties that are required to process the data safely and\n correctly." + }, + "defaultConfiguration": { + "level": "error" + } + }, + { + "id": "327", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "name": "Use of a Broken or Risky Cryptographic Algorithm", + "shortDescription": { + "text": "The product uses a broken or risky cryptographic algorithm or protocol." + }, + "defaultConfiguration": { + "level": "error" + } + }, + { + "id": "400", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "name": "Uncontrolled Resource Consumption", + "shortDescription": { + "text": "The product does not properly control the allocation and maintenance of a limited resource, thereby enabling an actor to influence the amount of resources consumed, eventually leading to the exhaustion of available resources." + }, + "defaultConfiguration": { + "level": "error" + } + }, + { + "id": "259", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "name": "Use of Hard-coded Password", + "shortDescription": { + "text": "The product contains a hard-coded password, which it uses for its own inbound authentication or for outbound communication to external components." + }, + "defaultConfiguration": { + "level": "error" + } + }, + { + "id": "502", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "name": "Deserialization of Untrusted Data", + "shortDescription": { + "text": "The product deserializes untrusted data without sufficiently verifying that the resulting data will be valid." + }, + "defaultConfiguration": { + "level": "warning" + } + }, + { + "id": "89", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "name": "Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')", + "shortDescription": { + "text": "The product constructs all or part of an SQL command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended SQL command when it is sent to a downstream component." + }, + "defaultConfiguration": { + "level": "error" + } + }, + { + "id": "330", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "name": "Use of Insufficiently Random Values", + "shortDescription": { + "text": "The product uses insufficiently random numbers or values in a security context that depends on unpredictable numbers." + }, + "defaultConfiguration": { + "level": "error" + } + }, + { + "id": "703", + "guid": "8dd83d5c-cbef-4d50-a5e5-22b24bb72190", + "name": "Improper Check or Handling of Exceptional Conditions", + "shortDescription": { + "text": "The product does not properly anticipate or handle exceptional conditions that rarely occur during normal operation of the product." + }, + "defaultConfiguration": { + "level": "none" + } + } + ] } ] } ], "version": "2.1.0", - "$schema": "https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0-rtm.4.json" -} + "$schema": "https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0.json" +} \ No newline at end of file diff --git a/sechub-pds-solutions/multi/docker/packages.txt b/sechub-pds-solutions/multi/docker/packages.txt index 902bb77cb6..e743e62f60 100644 --- a/sechub-pds-solutions/multi/docker/packages.txt +++ b/sechub-pds-solutions/multi/docker/packages.txt @@ -1,5 +1,4 @@ flawfinder==2.0.19 bandit==1.7.5 -bandit-sarif-formatter==1.1.1 njsscan==0.3.4 mobsfscan==0.2.0 diff --git a/sechub-pds-solutions/owaspzap/docker/Owasp-Zap-Debian.dockerfile b/sechub-pds-solutions/owaspzap/docker/Owasp-Zap-Debian.dockerfile index 8ed650d39b..82aa3f58a2 100644 --- a/sechub-pds-solutions/owaspzap/docker/Owasp-Zap-Debian.dockerfile +++ b/sechub-pds-solutions/owaspzap/docker/Owasp-Zap-Debian.dockerfile @@ -10,10 +10,10 @@ LABEL org.opencontainers.image.description="A container which combines OWASP ZAP LABEL maintainer="SecHub FOSS Team" # Build args -ARG OWASPZAP_VERSION="2.13.0" -ARG OWASPZAP_SHA256SUM="24dfba87278515e3dabe8d24c259981cd812a8f6e66808c956104c3283d91d9d" +ARG OWASPZAP_VERSION="2.14.0" +ARG OWASPZAP_SHA256SUM="219d7f25bbe25247713805ab02cc12279898c870743c1aae3c2b0b1882191960" -ARG OWASPZAP_WRAPPER_VERSION="1.2.0" +ARG OWASPZAP_WRAPPER_VERSION="1.3.1" # OWASP ZAP host and port ENV ZAP_HOST="127.0.0.1" @@ -37,16 +37,17 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get install --assume-yes openjdk-17-jre firefox-esr wget && \ apt-get clean -# Install OWASP ZAP +# Install ZAP RUN cd "$DOWNLOAD_FOLDER" && \ - # download latest release of owasp zap - wget --no-verbose https://github.com/zaproxy/zaproxy/releases/download/v${OWASPZAP_VERSION}/zaproxy_${OWASPZAP_VERSION}-1_all.deb && \ - # verify that the checksum and the checksum of the file are same - echo "${OWASPZAP_SHA256SUM} zaproxy_${OWASPZAP_VERSION}-1_all.deb" | sha256sum --check && \ - dpkg -i zaproxy_${OWASPZAP_VERSION}-1_all.deb && \ - # remove zaproxy deb package - rm zaproxy_${OWASPZAP_VERSION}-1_all.deb - + # download latest release of ZAP + wget --no-verbose https://github.com/zaproxy/zaproxy/releases/download/v${OWASPZAP_VERSION}/ZAP_${OWASPZAP_VERSION}_Linux.tar.gz && \ + # verify that the checksum and the checksum of the file are same + echo "${OWASPZAP_SHA256SUM} ZAP_${OWASPZAP_VERSION}_Linux.tar.gz" | sha256sum --check && \ + # install ZAP + tar xf ZAP_${OWASPZAP_VERSION}_Linux.tar.gz -C "$TOOL_FOLDER" && \ + ln -s "$TOOL_FOLDER/ZAP_${OWASPZAP_VERSION}/zap.sh" "/usr/local/bin/zap" && \ + # remove plugins installed on default + rm $TOOL_FOLDER/ZAP_${OWASPZAP_VERSION}/plugin/*.zap # Install SecHub OWASP ZAP wrapper RUN cd "$TOOL_FOLDER" && \ @@ -66,7 +67,7 @@ COPY zap-addons.txt "$TOOL_FOLDER/zap-addons.txt" # Install OWASP ZAP addons # see: https://www.zaproxy.org/addons/ -# via addon manager: owasp-zap -cmd -addoninstall webdriverlinux +# via addon manager: zap -cmd -addoninstall webdriverlinux RUN mkdir --parents "/home/$USER/.ZAP/plugin" && \ chown --recursive "$USER:$USER" "/home/$USER/" && \ cd "/home/$USER/.ZAP/plugin" && \ diff --git a/sechub-pds-solutions/owaspzap/docker/scripts/owasp-zap.sh b/sechub-pds-solutions/owaspzap/docker/scripts/owasp-zap.sh index 97e84c222d..05530d0e61 100755 --- a/sechub-pds-solutions/owaspzap/docker/scripts/owasp-zap.sh +++ b/sechub-pds-solutions/owaspzap/docker/scripts/owasp-zap.sh @@ -3,8 +3,7 @@ shutdownZAP() { # --full: to specify the process by looking at full command line including the parameters - pkill -9 --full "/usr/bin/owasp-zap" - pkill -9 --full "/usr/share/zaproxy/zap-" + pkill -9 --full "/pds/tools/ZAP_" } # Start OWASP-ZAP server @@ -13,7 +12,7 @@ echo "Starting up OWASP-ZAP server" # This addon is mandatory now but the telemetry calls can be deactivated. # This feature addtionally disables automated update calls, e.g. to update extensions. # Otherwise, if you want to use a specific versions of extensions e.g. for testing reasons, ZAP would automatically check for updates. -owasp-zap -daemon -silent -nostdout -host "$ZAP_HOST" -port "$ZAP_PORT" -config "api.key=$ZAP_API_KEY" & +zap -daemon -silent -nostdout -host "$ZAP_HOST" -port "$ZAP_PORT" -config "api.key=$ZAP_API_KEY" & echo "Waiting for OWASP-ZAP to start" RETRIES=20 diff --git a/sechub-pds-solutions/owaspzap/docker/zap-addons.txt b/sechub-pds-solutions/owaspzap/docker/zap-addons.txt index 0f6bcf84e8..6620aa1726 100644 --- a/sechub-pds-solutions/owaspzap/docker/zap-addons.txt +++ b/sechub-pds-solutions/owaspzap/docker/zap-addons.txt @@ -1,16 +1,16 @@ -https://github.com/zaproxy/zap-extensions/releases/download/commonlib-v1.14.0/commonlib-release-1.14.0.zap -https://github.com/zaproxy/zap-extensions/releases/download/ascanrules-v55/ascanrules-release-55.zap -https://github.com/zaproxy/zap-extensions/releases/download/selenium-v15.12.1/selenium-release-15.12.1.zap -https://github.com/zaproxy/zap-extensions/releases/download/spiderAjax-v23.14.1/spiderAjax-release-23.14.1.zap -https://github.com/zaproxy/zap-extensions/releases/download/pscanrules-v49/pscanrules-release-49.zap -https://github.com/zaproxy/zap-extensions/releases/download/retire-v0.23.0/retire-release-0.23.0.zap -https://github.com/zaproxy/zap-extensions/releases/download/domxss-v15/domxss-release-15.zap -https://github.com/zaproxy/zap-extensions/releases/download/webdriverlinux-v56/webdriverlinux-release-56.zap -https://github.com/zaproxy/zap-extensions/releases/download/network-v0.9.0/network-beta-0.9.0.zap -https://github.com/zaproxy/zap-extensions/releases/download/openapi-v34/openapi-beta-34.zap -https://github.com/zaproxy/zap-extensions/releases/download/callhome-v0.6.0/callhome-release-0.6.0.zap -https://github.com/zaproxy/zap-extensions/releases/download/spider-v0.4.0/spider-release-0.4.0.zap -https://github.com/zaproxy/zap-extensions/releases/download/database-v0.1.0/database-alpha-0.1.0.zap -https://github.com/zaproxy/zap-extensions/releases/download/oast-v0.15.0/oast-beta-0.15.0.zap -https://github.com/zaproxy/zap-extensions/releases/download/reports-v0.22.0/reports-release-0.22.0.zap -https://github.com/zaproxy/zap-extensions/releases/download/replacer-v12/replacer-release-12.zap +https://github.com/zaproxy/zap-extensions/releases/download/commonlib-v1.18.0/commonlib-release-1.18.0.zap +https://github.com/zaproxy/zap-extensions/releases/download/ascanrules-v58/ascanrules-release-58.zap +https://github.com/zaproxy/zap-extensions/releases/download/selenium-v15.15.0/selenium-release-15.15.0.zap +https://github.com/zaproxy/zap-extensions/releases/download/spiderAjax-v23.17.0/spiderAjax-release-23.17.0.zap +https://github.com/zaproxy/zap-extensions/releases/download/pscanrules-v52/pscanrules-release-52.zap +https://github.com/zaproxy/zap-extensions/releases/download/retire-v0.26.0/retire-release-0.26.0.zap +https://github.com/zaproxy/zap-extensions/releases/download/domxss-v18/domxss-release-18.zap +https://github.com/zaproxy/zap-extensions/releases/download/webdriverlinux-v64/webdriverlinux-release-64.zap +https://github.com/zaproxy/zap-extensions/releases/download/network-v0.12.0/network-beta-0.12.0.zap +https://github.com/zaproxy/zap-extensions/releases/download/openapi-v38/openapi-beta-38.zap +https://github.com/zaproxy/zap-extensions/releases/download/callhome-v0.8.0/callhome-release-0.8.0.zap +https://github.com/zaproxy/zap-extensions/releases/download/spider-v0.7.0/spider-release-0.7.0.zap +https://github.com/zaproxy/zap-extensions/releases/download/database-v0.3.0/database-alpha-0.3.0.zap +https://github.com/zaproxy/zap-extensions/releases/download/oast-v0.17.0/oast-beta-0.17.0.zap +https://github.com/zaproxy/zap-extensions/releases/download/reports-v0.26.0/reports-release-0.26.0.zap +https://github.com/zaproxy/zap-extensions/releases/download/replacer-v15/replacer-release-15.zap diff --git a/sechub-pds-solutions/owaspzap/env b/sechub-pds-solutions/owaspzap/env index 056622a353..791ed3de28 100644 --- a/sechub-pds-solutions/owaspzap/env +++ b/sechub-pds-solutions/owaspzap/env @@ -4,10 +4,10 @@ BASE_IMAGE="ghcr.io/mercedes-benz/sechub/pds-base" # See: https://github.com/mercedes-benz/sechub/releases/ -OWASPZAP_WRAPPER_VERSION="1.2.0" +OWASPZAP_WRAPPER_VERSION="1.3.1" # See: https://github.com/zaproxy/zaproxy/releases/latest -OWASPZAP_VERSION="2.13.0" -OWASPZAP_SHA256SUM="24dfba87278515e3dabe8d24c259981cd812a8f6e66808c956104c3283d91d9d" +OWASPZAP_VERSION="2.14.0" +OWASPZAP_SHA256SUM="219d7f25bbe25247713805ab02cc12279898c870743c1aae3c2b0b1882191960" PDS_START_MODE=localserver ADMIN_USERID=admin diff --git a/sechub-pds-tools/src/main/java/com/mercedesbenz/sechub/pds/tools/SystemTestCommand.java b/sechub-pds-tools/src/main/java/com/mercedesbenz/sechub/pds/tools/SystemTestCommand.java index 31657f9664..1a6bb39d2e 100644 --- a/sechub-pds-tools/src/main/java/com/mercedesbenz/sechub/pds/tools/SystemTestCommand.java +++ b/sechub-pds-tools/src/main/java/com/mercedesbenz/sechub/pds/tools/SystemTestCommand.java @@ -1,6 +1,9 @@ // SPDX-License-Identifier: MIT package com.mercedesbenz.sechub.pds.tools; +import java.util.ArrayList; +import java.util.List; + import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; @@ -61,6 +64,13 @@ public class SystemTestCommand { String pdsSolutionsRootFolder; + @Parameter( + names = { "--run-tests", "-rt" }, + description = "The (comma separated) name(s) of the tests to run. When defined, only those tests are run. When nothing defined, all system tests are executed.", + required=false + ) + List testsToRun = new ArrayList<>(); + /* @formatter:on */ public String getPdsSolutionsRootFolder() { @@ -91,4 +101,8 @@ public boolean isRemoteRun() { return remoteRun; } + public List getTestsToRun() { + return testsToRun; + } + } diff --git a/sechub-pds-tools/src/main/java/com/mercedesbenz/sechub/pds/tools/systemtest/SystemTestLauncher.java b/sechub-pds-tools/src/main/java/com/mercedesbenz/sechub/pds/tools/systemtest/SystemTestLauncher.java index e879dd1ba0..88d7b9b8cc 100644 --- a/sechub-pds-tools/src/main/java/com/mercedesbenz/sechub/pds/tools/systemtest/SystemTestLauncher.java +++ b/sechub-pds-tools/src/main/java/com/mercedesbenz/sechub/pds/tools/systemtest/SystemTestLauncher.java @@ -6,6 +6,7 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.util.List; import com.mercedesbenz.sechub.commons.TextFileReader; import com.mercedesbenz.sechub.commons.model.JSONConverter; @@ -31,6 +32,7 @@ public SystemTestResult launch(SystemTestCommand systemTestCommand) throws IOExc handleAdditionalResources(systemTestCommand, builder); handleRunModes(systemTestCommand, builder); + handleTestsToRun(systemTestCommand, builder); handleSolutions(systemTestCommand, builder); handleWorkspace(systemTestCommand, builder); @@ -74,6 +76,14 @@ private void handleRunModes(SystemTestCommand systemTestCommand, SystemTestParam } } + private void handleTestsToRun(SystemTestCommand systemTestCommand, SystemTestParametersBuilder builder) { + + List testsToRun = systemTestCommand.getTestsToRun(); + if (testsToRun != null) { + builder.testsToRun(testsToRun.toArray(new String[testsToRun.size()])); + } + } + private void handleAdditionalResources(SystemTestCommand systemTestCommand, SystemTestParametersBuilder builder) { String addtionalResourcesFolder = systemTestCommand.getAdditionalResourcesFolder(); if (isNotEmpty(addtionalResourcesFolder)) { diff --git a/sechub-pds-tools/src/test/java/com/mercedesbenz/sechub/pds/tools/PDSToolsCLITest.java b/sechub-pds-tools/src/test/java/com/mercedesbenz/sechub/pds/tools/PDSToolsCLITest.java index eacc103ea6..613d8fef84 100644 --- a/sechub-pds-tools/src/test/java/com/mercedesbenz/sechub/pds/tools/PDSToolsCLITest.java +++ b/sechub-pds-tools/src/test/java/com/mercedesbenz/sechub/pds/tools/PDSToolsCLITest.java @@ -168,6 +168,7 @@ void systemtest_does_call_systemtest_launcher_with_correct_paramters_1() throws assertEquals(true, systemTestCommand.isDryRun()); assertEquals(false, systemTestCommand.isRemoteRun()); assertEquals(null, systemTestCommand.getWorkspaceFolder()); + assertTrue(systemTestCommand.getTestsToRun().isEmpty()); } @@ -180,7 +181,7 @@ void systemtest_does_call_systemtest_launcher_with_correct_paramters_2() throws /* execute */ cliToTest.start(new String[] { "systemtest", "--workspace-rootfolder", "/path/to/workspace", "--file", "/absolute/testfile.json", "--additional-resources-folder", "./additionalResources/path", "--pds-solutions-rootfolder", "/path/to/pds-solution", - "--sechub-solution-rootfolder", "/path/to/sechub-solution" }); + "--sechub-solution-rootfolder", "/path/to/sechub-solution", "--run-tests", "test1,test2" }); /* test */ ArgumentCaptor captor = ArgumentCaptor.forClass(SystemTestCommand.class); @@ -194,6 +195,9 @@ void systemtest_does_call_systemtest_launcher_with_correct_paramters_2() throws assertEquals(false, systemTestCommand.isDryRun()); assertEquals(false, systemTestCommand.isRemoteRun()); assertEquals("/path/to/workspace", systemTestCommand.getWorkspaceFolder()); + assertEquals(2, systemTestCommand.getTestsToRun().size()); + assertTrue(systemTestCommand.getTestsToRun().contains("test1")); + assertTrue(systemTestCommand.getTestsToRun().contains("test2")); } diff --git a/sechub-pds-tools/src/test/java/com/mercedesbenz/sechub/pds/tools/systemtest/SystemTestLauncherTest.java b/sechub-pds-tools/src/test/java/com/mercedesbenz/sechub/pds/tools/systemtest/SystemTestLauncherTest.java index 920cd95947..3acb94e3e6 100644 --- a/sechub-pds-tools/src/test/java/com/mercedesbenz/sechub/pds/tools/systemtest/SystemTestLauncherTest.java +++ b/sechub-pds-tools/src/test/java/com/mercedesbenz/sechub/pds/tools/systemtest/SystemTestLauncherTest.java @@ -2,10 +2,13 @@ package com.mercedesbenz.sechub.pds.tools.systemtest; import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import java.io.File; import java.io.IOException; +import java.util.Arrays; +import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -73,7 +76,7 @@ void result_is_returned_from_sytemtestapi() throws Exception { } @Test - void system_test_parameters_are_build_from_command_as_expected() throws Exception { + void system_test_parameters_are_build_from_command_as_expected__tests_to_run_set() throws Exception { /* prepare */ String testAdditionaResourcesPath = "test-additional-resources-path"; @@ -83,12 +86,14 @@ void system_test_parameters_are_build_from_command_as_expected() throws Exceptio String path = "./src/test/resources/systemtest/systemtest_example1.json"; String expectedPrettyJson = loadJsonAsPrettyPrinted(path); + List testsToRun = Arrays.asList(new String[] { "testA", "testB" }); when(command.getPathToConfigFile()).thenReturn(path); when(command.getAdditionalResourcesFolder()).thenReturn(testAdditionaResourcesPath); when(command.getPdsSolutionsRootFolder()).thenReturn(testPathToPDSSolution); when(command.getSecHubSolutionRootFolder()).thenReturn(testPathToSecHub); when(command.getWorkspaceFolder()).thenReturn(testPathToWorkspace); + when(command.getTestsToRun()).thenReturn(testsToRun); /* execute */ launcherToTest.launch(command); @@ -98,6 +103,47 @@ void system_test_parameters_are_build_from_command_as_expected() throws Exceptio verify(systemTestApi).runSystemTests(paramCaptor.capture()); SystemTestParameters parameters = paramCaptor.getValue(); + assertTrue(parameters.getTestsToRun().contains("testA")); + assertTrue(parameters.getTestsToRun().contains("testB")); + + // test configuration is loaded and available + SystemTestConfiguration parametersConfiguration = parameters.getConfiguration(); + String prettyJson = JSONConverter.get().toJSON(parametersConfiguration, true); + assertEquals(expectedPrettyJson, prettyJson); + + assertEquals(testAdditionaResourcesPath, parameters.getPathToAdditionalResources()); + assertEquals(testPathToPDSSolution, parameters.getPathToPdsSolutionsRootFolder()); + assertEquals(testPathToSecHub, parameters.getPathToSechubSolutionRootFolder()); + assertEquals(testPathToWorkspace, parameters.getPathToWorkspace()); + + } + + @Test + void system_test_parameters_are_build_from_command_as_expected__no_tests_to_run_set() throws Exception { + + /* prepare */ + String testAdditionaResourcesPath = "test-additional-resources-path"; + String testPathToPDSSolution = "test-pds-solution-path"; + String testPathToSecHub = "test-sechub-path"; + String testPathToWorkspace = "test-workspace-path"; + + String path = "./src/test/resources/systemtest/systemtest_example1.json"; + String expectedPrettyJson = loadJsonAsPrettyPrinted(path); + + when(command.getPathToConfigFile()).thenReturn(path); + when(command.getAdditionalResourcesFolder()).thenReturn(testAdditionaResourcesPath); + when(command.getPdsSolutionsRootFolder()).thenReturn(testPathToPDSSolution); + when(command.getSecHubSolutionRootFolder()).thenReturn(testPathToSecHub); + when(command.getWorkspaceFolder()).thenReturn(testPathToWorkspace); + + /* execute */ + launcherToTest.launch(command); + + /* test */ + ArgumentCaptor paramCaptor = ArgumentCaptor.forClass(SystemTestParameters.class); + verify(systemTestApi).runSystemTests(paramCaptor.capture()); + SystemTestParameters parameters = paramCaptor.getValue(); + assertTrue(parameters.getTestsToRun().isEmpty()); // test configuration is loaded and available SystemTestConfiguration parametersConfiguration = parameters.getConfiguration(); diff --git a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/WebConfigBuilderStrategy.java b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/WebConfigBuilderStrategy.java index cb2a41cd6e..5d86b97e08 100644 --- a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/WebConfigBuilderStrategy.java +++ b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/WebConfigBuilderStrategy.java @@ -38,9 +38,6 @@ * @param configuration */ public class WebConfigBuilderStrategy implements AdapterConfigurationStrategy/* */ { - public static final int MAX_LIST_SIZE_INCLUDES = 500; - public static final int MAX_LIST_SIZE_EXCLUDES = 500; - public static final int MAX_LENGTH_PATH_SIZE = 2048; private SecHubExecutionContext context; @@ -117,8 +114,6 @@ private , C extends Abstract List includesList = optIncludes.get(); - checkExcludesOrIncludes(includesList, MAX_LIST_SIZE_INCLUDES, true); - Set includes = new HashSet<>(includesList); configBuilder.setIncludes(includes); @@ -133,36 +128,11 @@ private , C extends Abstract List excludeList = optExcludes.get(); - checkExcludesOrIncludes(excludeList, MAX_LIST_SIZE_EXCLUDES, false); - Set excludes = new HashSet<>(excludeList); configBuilder.setExcludes(excludes); } - private void checkExcludesOrIncludes(List urlList, int maxListSize, boolean include) { - String term = "excludes"; - - if (urlList.size() > maxListSize) { - if (include) { - term = "includes"; - } - throw new IllegalArgumentException("A maximum of " + maxListSize + " " + term + " are allowed."); - } - - for (String url : urlList) { - if (url.length() > MAX_LENGTH_PATH_SIZE) { - String excludeSubStr = url.substring(0, MAX_LENGTH_PATH_SIZE); - throw new IllegalArgumentException("Maximum URL length is " + MAX_LENGTH_PATH_SIZE + " characters. The first " + MAX_LENGTH_PATH_SIZE - + " characters of the URL in question: " + excludeSubStr); - } - if (!url.startsWith("/")) { - throw new IllegalArgumentException("The URL does not start with a slash '/'. URL: " + url); - } - - } - } - private > void handleMaxScanDuration(B configBuilder, SecHubWebScanConfiguration webscanConfig) { Optional optMaxScanDuration = webscanConfig.getMaxScanDuration(); if (!optMaxScanDuration.isPresent()) { diff --git a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/autocleanup/ScanAutoCleanupService.java b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/autocleanup/ScanAutoCleanupService.java index fe11c0fab2..fb5bdf730d 100644 --- a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/autocleanup/ScanAutoCleanupService.java +++ b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/autocleanup/ScanAutoCleanupService.java @@ -41,8 +41,6 @@ public class ScanAutoCleanupService { @Autowired AutoCleanupResultInspector inspector; - private static boolean statistic_feature_1010_implemented = false; - @UseCaseScanAutoCleanExecution(@Step(number = 2, name = "Delete old data", description = "deletes old job information")) public void cleanup() { /* calculate */ @@ -75,9 +73,6 @@ private void deleteScanLogs(long days, LocalDateTime cleanTimeStamp) { private void deleteScanResults(long days, LocalDateTime cleanTimeStamp) { /* @formatter:off */ - if (! statistic_feature_1010_implemented) { - return; - } int amount = scanReportRepository.deleteReportsOlderThan(cleanTimeStamp); inspector.inspect(AutoCleanupResult.builder(). autoCleanup("scan-reports",getClass()). diff --git a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/project/FalsePositiveJobDataConfigMerger.java b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/project/FalsePositiveJobDataConfigMerger.java index d1a3943831..68d26d895b 100644 --- a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/project/FalsePositiveJobDataConfigMerger.java +++ b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/project/FalsePositiveJobDataConfigMerger.java @@ -58,6 +58,10 @@ public void removeJobDataWithMetaDataFromConfig(FalsePositiveProjectConfiguratio config.getFalsePositives().remove(entry); } + public boolean isFalsePositiveEntryAlreadyExisting(FalsePositiveProjectConfiguration config, FalsePositiveJobData falsePositiveJobData) { + return findExistingFalsePositiveEntryInConfig(config, falsePositiveJobData) != null; + } + private FalsePositiveEntry findExistingFalsePositiveEntryInConfig(FalsePositiveProjectConfiguration config, FalsePositiveJobData falsePositiveJobData) { for (FalsePositiveEntry existingFPEntry : config.getFalsePositives()) { FalsePositiveJobData jobData = existingFPEntry.getJobData(); diff --git a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/project/FalsePositiveJobDataService.java b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/project/FalsePositiveJobDataService.java index fb518159a0..951da73c95 100644 --- a/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/project/FalsePositiveJobDataService.java +++ b/sechub-scan/src/main/java/com/mercedesbenz/sechub/domain/scan/project/FalsePositiveJobDataService.java @@ -109,25 +109,41 @@ private void validateProjectIdAndProjectAccess(String projectId) { private void addJobDataListToConfiguration(FalsePositiveProjectConfiguration config, FalsePositiveJobDataList jobDataList) { List list = jobDataList.getJobData(); - /* we want to load reports only one time, so sort by report job UUID... */ + /* + * Reason for sorting: we want to load reports only one time, so sort by report + * job UUID is necessary for method "fetchReportIfNotAlreadyLoaded" + */ list.sort(Comparator.comparing(FalsePositiveJobData::getJobUUID)); - ScanSecHubReport report = null; + ScanSecHubReport currentReport = null; + for (FalsePositiveJobData data : list) { + if (merger.isFalsePositiveEntryAlreadyExisting(config, data)) { + LOG.debug("Skip processing because FP already defined: {}", data); + continue; + } + UUID jobUUID = data.getJobUUID(); + currentReport = fetchReportIfNotAlreadyLoaded(jobUUID, currentReport); - if (report == null || !jobUUID.equals(report.getJobUUID())) { - ScanReport scanReport = scanReportRepository.findBySecHubJobUUID(jobUUID); - if (scanReport == null) { - throw new NotFoundException("No report found for job " + jobUUID); - } - report = new ScanSecHubReport(scanReport); - } - merger.addJobDataWithMetaDataToConfig(report, config, data, userContextService.getUserId()); + merger.addJobDataWithMetaDataToConfig(currentReport, config, data, userContextService.getUserId()); } } + private ScanSecHubReport fetchReportIfNotAlreadyLoaded(UUID jobUUID, ScanSecHubReport currentReport) { + + /* load report if it is not the current report */ + if (currentReport == null || !jobUUID.equals(currentReport.getJobUUID())) { + ScanReport scanReport = scanReportRepository.findBySecHubJobUUID(jobUUID); + if (scanReport == null) { + throw new NotFoundException("No report found for job " + jobUUID); + } + currentReport = new ScanSecHubReport(scanReport); + } + return currentReport; + } + private FalsePositiveProjectConfiguration fetchOrCreateConfiguration(String projectId) { ScanProjectConfig projectConfig = configService.getOrCreate(projectId, CONFIG_ID, false, "{}"); // access check unnecessary, already done diff --git a/sechub-scan/src/test/java/com/mercedesbenz/sechub/domain/scan/WebConfigBuilderStrategyTest.java b/sechub-scan/src/test/java/com/mercedesbenz/sechub/domain/scan/WebConfigBuilderStrategyTest.java index 7e11529555..a8de54c5c0 100644 --- a/sechub-scan/src/test/java/com/mercedesbenz/sechub/domain/scan/WebConfigBuilderStrategyTest.java +++ b/sechub-scan/src/test/java/com/mercedesbenz/sechub/domain/scan/WebConfigBuilderStrategyTest.java @@ -1,20 +1,16 @@ // SPDX-License-Identifier: MIT package com.mercedesbenz.sechub.domain.scan; -import static org.hamcrest.CoreMatchers.*; -import static org.hamcrest.MatcherAssert.*; import static org.junit.jupiter.api.Assertions.*; import java.net.URI; import java.net.URL; import java.util.HashSet; import java.util.Iterator; -import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.UUID; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import com.mercedesbenz.sechub.adapter.AbstractWebScanAdapterConfig; @@ -195,245 +191,10 @@ public void form_script_login_data_transfered() { assertEquals(null, action.getValue()); } - @Test - public void excludes_no_slash_infront_single() { - /* prepare */ - List excludes = new LinkedList<>(); - excludes.add("contact.html"); - - String json = createExcludesJson(excludes); - SecHubConfiguration configuration = SECHUB_CONFIG.fromJSON(json); - SecHubExecutionContext context = new SecHubExecutionContext(UUID.randomUUID(), configuration, "test", UUID.randomUUID()); - WebConfigBuilderStrategy strategyToTest = new WebConfigBuilderStrategy(context); - TestAbstractWebScanAdapterConfigBuilder configBuilder = new TestAbstractWebScanAdapterConfigBuilder(); - - /* execute */ - IllegalArgumentException exception = Assertions.assertThrows(IllegalArgumentException.class, () -> { - strategyToTest.configure(configBuilder); - }); - - /* test */ - assertEquals("The URL does not start with a slash '/'. URL: contact.html", exception.getMessage()); - } - - @Test - public void excludes_no_slash_infront_multiple() { - /* prepare */ - List excludes = new LinkedList<>(); - - excludes.add("/abc"); - excludes.add("contact.html"); - excludes.add("/bca"); - excludes.add("ccb/bca"); - - String json = createExcludesJson(excludes); - SecHubConfiguration configuration = SECHUB_CONFIG.fromJSON(json); - SecHubExecutionContext context = new SecHubExecutionContext(UUID.randomUUID(), configuration, "test", UUID.randomUUID()); - WebConfigBuilderStrategy strategyToTest = new WebConfigBuilderStrategy(context); - TestAbstractWebScanAdapterConfigBuilder configBuilder = new TestAbstractWebScanAdapterConfigBuilder(); - - /* execute */ - IllegalArgumentException exception = Assertions.assertThrows(IllegalArgumentException.class, () -> { - strategyToTest.configure(configBuilder); - }); - - /* test */ - assertEquals("The URL does not start with a slash '/'. URL: contact.html", exception.getMessage()); - } - - @Test - public void includes_no_slash_infront_single() { - /* prepare */ - List includes = new LinkedList<>(); - includes.add("contact.html"); - - String json = createIncludesJson(includes); - SecHubConfiguration configuration = SECHUB_CONFIG.fromJSON(json); - SecHubExecutionContext context = new SecHubExecutionContext(UUID.randomUUID(), configuration, "test", UUID.randomUUID()); - WebConfigBuilderStrategy strategyToTest = new WebConfigBuilderStrategy(context); - TestAbstractWebScanAdapterConfigBuilder configBuilder = new TestAbstractWebScanAdapterConfigBuilder(); - - /* execute */ - IllegalArgumentException exception = Assertions.assertThrows(IllegalArgumentException.class, () -> { - strategyToTest.configure(configBuilder); - }); - - /* test */ - assertEquals("The URL does not start with a slash '/'. URL: contact.html", exception.getMessage()); - } - - @Test - public void includes_no_slash_infront_multiple() { - /* prepare */ - List includes = new LinkedList<>(); - includes.add("/abc"); - includes.add("contact.html"); - includes.add("/hidden"); - includes.add("ccb/bca"); - - String json = createIncludesJson(includes); - SecHubConfiguration configuration = SECHUB_CONFIG.fromJSON(json); - SecHubExecutionContext context = new SecHubExecutionContext(UUID.randomUUID(), configuration, "test", UUID.randomUUID()); - WebConfigBuilderStrategy strategyToTest = new WebConfigBuilderStrategy(context); - TestAbstractWebScanAdapterConfigBuilder configBuilder = new TestAbstractWebScanAdapterConfigBuilder(); - - /* execute */ - IllegalArgumentException exception = Assertions.assertThrows(IllegalArgumentException.class, () -> { - strategyToTest.configure(configBuilder); - }); - - /* test */ - assertEquals("The URL does not start with a slash '/'. URL: contact.html", exception.getMessage()); - } - - @Test - public void too_many_excludes() { - /* prepare */ - List excludes = new LinkedList<>(); - for (int i = 1; i <= 501; i++) { - excludes.add("/myapp" + i); - } - - String json = createExcludesJson(excludes); - SecHubConfiguration configuration = SECHUB_CONFIG.fromJSON(json); - SecHubExecutionContext context = new SecHubExecutionContext(UUID.randomUUID(), configuration, "test", UUID.randomUUID()); - WebConfigBuilderStrategy strategyToTest = new WebConfigBuilderStrategy(context); - TestAbstractWebScanAdapterConfigBuilder configBuilder = new TestAbstractWebScanAdapterConfigBuilder(); - - /* execute */ - IllegalArgumentException exception = Assertions.assertThrows(IllegalArgumentException.class, () -> { - strategyToTest.configure(configBuilder); - }); - - /* test */ - assertEquals("A maximum of 500 excludes are allowed.", exception.getMessage()); - } - - @Test - public void too_many_includes() { - /* prepare */ - List includes = new LinkedList<>(); - for (int i = 1; i <= 501; i++) { - includes.add("/myapp" + i); - } - - String json = createIncludesJson(includes); - SecHubConfiguration configuration = SECHUB_CONFIG.fromJSON(json); - SecHubExecutionContext context = new SecHubExecutionContext(UUID.randomUUID(), configuration, "test", UUID.randomUUID()); - WebConfigBuilderStrategy strategyToTest = new WebConfigBuilderStrategy(context); - TestAbstractWebScanAdapterConfigBuilder configBuilder = new TestAbstractWebScanAdapterConfigBuilder(); - - /* execute */ - IllegalArgumentException exception = Assertions.assertThrows(IllegalArgumentException.class, () -> { - strategyToTest.configure(configBuilder); - }); - - /* test */ - assertEquals("A maximum of 500 includes are allowed.", exception.getMessage()); - } - - @Test - public void exclude_too_long() { - /* prepare */ - // create long string - StringBuilder sb = new StringBuilder(); - sb.append("/"); - - for (int i = 0; i < 64; i++) { - sb.append("abcdefghijklmnopqrstuvwxyz012345"); - } - - List excludes = new LinkedList<>(); - excludes.add(sb.toString()); - - String json = createExcludesJson(excludes); - SecHubConfiguration configuration = SECHUB_CONFIG.fromJSON(json); - SecHubExecutionContext context = new SecHubExecutionContext(UUID.randomUUID(), configuration, "test", UUID.randomUUID()); - WebConfigBuilderStrategy strategyToTest = new WebConfigBuilderStrategy(context); - TestAbstractWebScanAdapterConfigBuilder configBuilder = new TestAbstractWebScanAdapterConfigBuilder(); - - /* execute */ - IllegalArgumentException exception = Assertions.assertThrows(IllegalArgumentException.class, () -> { - strategyToTest.configure(configBuilder); - }); - - /* test */ - assertThat(exception.getMessage(), - startsWith("Maximum URL length is 2048 characters. The first 2048 characters of the URL in question: /abcdefghijklmnopqrst")); - } - - @Test - public void include_too_long() { - /* prepare */ - // create long string - StringBuilder sb = new StringBuilder(); - sb.append("/"); - - for (int i = 0; i < 64; i++) { - sb.append("abcdefghijklmnopqrstuvwxyz012345"); - } - - List includes = new LinkedList<>(); - includes.add(sb.toString()); - - String json = createIncludesJson(includes); - SecHubConfiguration configuration = SECHUB_CONFIG.fromJSON(json); - SecHubExecutionContext context = new SecHubExecutionContext(UUID.randomUUID(), configuration, "test", UUID.randomUUID()); - WebConfigBuilderStrategy strategyToTest = new WebConfigBuilderStrategy(context); - TestAbstractWebScanAdapterConfigBuilder configBuilder = new TestAbstractWebScanAdapterConfigBuilder(); - - /* execute */ - IllegalArgumentException exception = Assertions.assertThrows(IllegalArgumentException.class, () -> { - strategyToTest.configure(configBuilder); - }); - - /* test */ - assertThat(exception.getMessage(), - startsWith("Maximum URL length is 2048 characters. The first 2048 characters of the URL in question: /abcdefghijklmnopqrst")); - } - private WebConfigBuilderStrategy createStrategy(String path) { return new WebConfigBuilderStrategy(createContext(path)); } - private String createExcludesJson(List excludes) { - return createIncludeOrExcludeJson(excludes, false); - } - - private String createIncludesJson(List includes) { - return createIncludeOrExcludeJson(includes, true); - } - - private String createIncludeOrExcludeJson(List items, boolean include) { - StringBuilder sb = new StringBuilder(); - sb.append("{"); - sb.append("\"apiVersion\": \"1.0\", "); - sb.append("\"webScan\": {"); - sb.append("\"uri\": \"https://productfailure.demo.example.org\", "); - - if (include) { - sb.append("\"includes\": ["); - } else { - sb.append("\"excludes\": ["); - } - - int itemNumber = 1; - for (String item : items) { - sb.append("\"").append(item).append("\""); - - if (itemNumber < items.size()) { - sb.append(","); - } - itemNumber++; - } - - sb.append("]"); - sb.append("}"); - sb.append("}"); - - return sb.toString(); - } - private SecHubExecutionContext createContext(String pathToTestConfig) { String json = ScanDomainTestFileSupport.getTestfileSupport().loadTestFile(pathToTestConfig); SecHubConfiguration configuration = SECHUB_CONFIG.fromJSON(json); diff --git a/sechub-scan/src/test/java/com/mercedesbenz/sechub/domain/scan/autocleanup/ScanAutoCleanupServiceTest.java b/sechub-scan/src/test/java/com/mercedesbenz/sechub/domain/scan/autocleanup/ScanAutoCleanupServiceTest.java index 77e177555b..08959b6b60 100644 --- a/sechub-scan/src/test/java/com/mercedesbenz/sechub/domain/scan/autocleanup/ScanAutoCleanupServiceTest.java +++ b/sechub-scan/src/test/java/com/mercedesbenz/sechub/domain/scan/autocleanup/ScanAutoCleanupServiceTest.java @@ -90,13 +90,11 @@ void cleanup_executes_delete_job_information_for_30_days() { verify(timeCalculationService).calculateNowMinusDays(eq(days)); verify(productResultRepository, times(1)).deleteResultsOlderThan(cleanTime); verify(projectScanLogRepository, times(1)).deleteLogsOlderThan(cleanTime); - // as long as issue https://github.com/mercedes-benz/sechub/issues/1010 is not - // implemented we keep the old data for statistics, so never called: - verify(scanReportRepository, never()).deleteReportsOlderThan(cleanTime); + verify(scanReportRepository, times(1)).deleteReportsOlderThan(cleanTime); // check inspection as expected ArgumentCaptor captor = ArgumentCaptor.forClass(AutoCleanupResult.class); - verify(inspector, times(2)).inspect(captor.capture()); + verify(inspector, times(3)).inspect(captor.capture()); List values = captor.getAllValues(); for (AutoCleanupResult result : values) { @@ -112,6 +110,9 @@ void cleanup_executes_delete_job_information_for_30_days() { case "product-results": assertEquals(20, result.getDeletedEntries()); break; + case "scan-reports": + assertEquals(30, result.getDeletedEntries()); + break; default: fail("unexpected variant:" + variant); } diff --git a/sechub-server/src/main/resources/db/migration/U26__statistic_indices.sql b/sechub-server/src/main/resources/db/migration/U26__statistic_indices.sql new file mode 100644 index 0000000000..13b06495de --- /dev/null +++ b/sechub-server/src/main/resources/db/migration/U26__statistic_indices.sql @@ -0,0 +1,7 @@ +-- SPDX-License-Identifier: MIT + +-- indices on statistics tables to enhance reporting performance + +DROP INDEX IF EXISTS i01_statistic_job_run_project; + +DROP INDEX IF EXISTS i01_statistic_job_run_data_filter; diff --git a/sechub-server/src/main/resources/db/migration/V26__statistic_indices.sql b/sechub-server/src/main/resources/db/migration/V26__statistic_indices.sql new file mode 100644 index 0000000000..7b03280c94 --- /dev/null +++ b/sechub-server/src/main/resources/db/migration/V26__statistic_indices.sql @@ -0,0 +1,9 @@ +-- SPDX-License-Identifier: MIT + +-- indices on statistics tables to enhance reporting performance + +CREATE INDEX IF NOT EXISTS i01_statistic_job_run_project + ON statistic_job_run (project_id); + +CREATE INDEX IF NOT EXISTS i01_statistic_job_run_data_filter + ON statistic_job_run_data (type,id); diff --git a/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/SystemTestAPI.java b/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/SystemTestAPI.java index 54c9bfa48a..b48eb479cf 100644 --- a/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/SystemTestAPI.java +++ b/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/SystemTestAPI.java @@ -62,7 +62,7 @@ public SystemTestResult runSystemTests(SystemTestParameters parameters, Environm SystemTestRuntime runtime = new SystemTestRuntime(locationSupport, execSupport); - return runtime.run(parameters.getConfiguration(), parameters.isLocalRun(), parameters.isDryRun()); + return runtime.run(parameters.getConfiguration(), parameters); } private void cleanupOldRuntimeFolderIfExisting(LocationSupport locationSupport) { diff --git a/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/SystemTestParameters.java b/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/SystemTestParameters.java index 51cdf18f08..114333a4d2 100644 --- a/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/SystemTestParameters.java +++ b/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/SystemTestParameters.java @@ -1,6 +1,9 @@ // SPDX-License-Identifier: MIT package com.mercedesbenz.sechub.systemtest; +import java.util.LinkedHashSet; +import java.util.Set; + import com.mercedesbenz.sechub.systemtest.config.SystemTestConfiguration; public class SystemTestParameters { @@ -11,6 +14,7 @@ public class SystemTestParameters { private String pathToWorkspace; private String pathToAdditionalResources; private boolean localRun = true;// default always local + private Set testsToRun = new LinkedHashSet<>(); private boolean dryRun; @@ -47,6 +51,10 @@ public boolean isDryRun() { return dryRun; } + public Set getTestsToRun() { + return testsToRun; + } + public static SystemTestParametersBuilder builder() { return new SystemTestParametersBuilder(); } @@ -112,10 +120,22 @@ public SystemTestParametersBuilder remoteRun() { return this; } + public SystemTestParametersBuilder testsToRun(String... testsToRun) { + if (testsToRun == null) { + return this; + } + for (String testToRun : testsToRun) { + if (testToRun == null) { + continue; + } + parameter.testsToRun.add(testToRun); + } + return this; + } + public SystemTestParameters build() { return parameter; } - } } diff --git a/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/config/DefaultFallback.java b/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/config/DefaultFallback.java index 59ac46c7ee..47786d2491 100644 --- a/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/config/DefaultFallback.java +++ b/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/config/DefaultFallback.java @@ -17,9 +17,9 @@ */ public enum DefaultFallback { - FALLBACK_PROJECT_NAME("default-test-project", "Project id"), + FALLBACK_PROJECT_NAME(StringConstants.DEFAULT_PROJECT_ID, "Project id"), - FALLBACK_PROFILE_ID("default-test-profile", "Profile id"), + FALLBACK_PROFILE_ID(StringConstants.DEFAULT_PROFILE_ID, "Profile id"), /** Same like default in /sechub-solution/env-sechub */ FALLBACK_LOCAL_SECHUB_URL("https://localhost:8443", "Local", "SecHub url"), @@ -49,7 +49,7 @@ public enum DefaultFallback { FALLBACK_SECHUB_WAIT_FOR_AVAILABLE("true", "SecHub wait for available"), - FALLBACK_UPLOAD_REF_ID("default-ref", "Upload reference id"), + FALLBACK_UPLOAD_REF_ID(StringConstants.DEFAULT_REFERENCE_ID, "Upload reference id"), ; @@ -57,6 +57,14 @@ public enum DefaultFallback { private String value; private String description; + public class StringConstants { + + public static final String DEFAULT_PROJECT_ID = "default-test-project"; + public static final String DEFAULT_PROFILE_ID = "default-test-profile"; + public static final String DEFAULT_REFERENCE_ID = "default-ref"; + + } + DefaultFallback(String value, String description) { this(value, null, description); } diff --git a/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/config/ProjectDefinition.java b/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/config/ProjectDefinition.java index 73f4e75ad8..bc2c3a4f71 100644 --- a/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/config/ProjectDefinition.java +++ b/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/config/ProjectDefinition.java @@ -8,6 +8,7 @@ public class ProjectDefinition extends AbstractDefinition { private String name; private List profiles = new ArrayList<>(); + private List whitelistedURIs = new ArrayList<>(); public String getName() { return name; @@ -20,4 +21,8 @@ public void setName(String name) { public List getProfiles() { return profiles; } + + public List getWhitelistedURIs() { + return whitelistedURIs; + } } diff --git a/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/config/RunSecHubJobDefinitionTransformer.java b/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/config/RunSecHubJobDefinitionTransformer.java index 9ec3ee5eb3..803bd61aae 100644 --- a/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/config/RunSecHubJobDefinitionTransformer.java +++ b/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/config/RunSecHubJobDefinitionTransformer.java @@ -47,50 +47,74 @@ private SecHubDataConfiguration createDataConfiguration(RunSecHubJobDefinition d for (UploadDefinition upload : uploads) { String referenceId = resolveReferenceIdOrFail(upload); - assertFolderDefined(upload, referenceId); + assertFolderOrFileDefined(upload, referenceId); assertNoMixupBetweenSourceAndBinaryUpload(upload, referenceId); - handleSourceFolder(data, upload); - handleBinaryFolder(data, upload); + handleSources(data, upload); + handleBinaries(data, upload); } return data; } - private void handleSourceFolder(SecHubDataConfiguration data, UploadDefinition upload) { + private void handleSources(SecHubDataConfiguration data, UploadDefinition upload) { Optional sourceFolderOpt = upload.getSourceFolder(); - if (sourceFolderOpt.isEmpty()) { + Optional sourceFileOpt = upload.getSourceFile(); + if (sourceFolderOpt.isEmpty() && sourceFileOpt.isEmpty()) { return; } SecHubSourceDataConfiguration sourceConfig = new SecHubSourceDataConfiguration(); SecHubFileSystemConfiguration fileSystem = new SecHubFileSystemConfiguration(); - fileSystem.getFolders().add(sourceFolderOpt.get()); + if (sourceFolderOpt.isPresent()) { + fileSystem.getFolders().add(sourceFolderOpt.get()); + } + if (sourceFileOpt.isPresent()) { + fileSystem.getFiles().add(sourceFileOpt.get()); + } sourceConfig.setFileSystem(fileSystem); sourceConfig.setUniqueName(upload.getReferenceId().orElse(null)); data.getSources().add(sourceConfig); } - private void handleBinaryFolder(SecHubDataConfiguration data, UploadDefinition upload) { + private void handleBinaries(SecHubDataConfiguration data, UploadDefinition upload) { Optional binaryFolderOpt = upload.getBinariesFolder(); - if (binaryFolderOpt.isEmpty()) { + Optional binaryFileOpt = upload.getBinaryFile(); + if (binaryFolderOpt.isEmpty() && binaryFileOpt.isEmpty()) { return; } SecHubBinaryDataConfiguration binaryConfig = new SecHubBinaryDataConfiguration(); SecHubFileSystemConfiguration fileSystem = new SecHubFileSystemConfiguration(); - fileSystem.getFolders().add(binaryFolderOpt.get()); + if (binaryFolderOpt.isPresent()) { + fileSystem.getFolders().add(binaryFolderOpt.get()); + } + if (binaryFileOpt.isPresent()) { + fileSystem.getFiles().add(binaryFileOpt.get()); + } binaryConfig.setFileSystem(fileSystem); binaryConfig.setUniqueName(upload.getReferenceId().orElse(null)); data.getBinaries().add(binaryConfig); } private void assertNoMixupBetweenSourceAndBinaryUpload(UploadDefinition upload, String referenceId) { - if (upload.getSourceFolder().isPresent() && upload.getBinariesFolder().isPresent()) { - throw new IllegalStateException("Source folder and binary folder defined for same reference id:" + referenceId); + + boolean referenceIsForSourcey = upload.getSourceFolder().isPresent() || upload.getSourceFile().isPresent(); + boolean referenceIsForBinary = upload.getBinariesFolder().isPresent() || upload.getBinaryFile().isPresent(); + + if (referenceIsForSourcey && referenceIsForBinary) { + throw new IllegalStateException("Source folder/file and binary folder/file defined for same reference id:" + referenceId); } } - private void assertFolderDefined(UploadDefinition upload, String referenceId) { - if (upload.getSourceFolder().isEmpty() && upload.getBinariesFolder().isEmpty()) { - throw new IllegalStateException("Neither a source folder nor a binary folder is defined for reference id:" + referenceId); + private void assertFolderOrFileDefined(UploadDefinition upload, String referenceId) { + + boolean atleastOneDefined = false; + + atleastOneDefined = atleastOneDefined || upload.getSourceFolder().isPresent(); + atleastOneDefined = atleastOneDefined || upload.getSourceFile().isPresent(); + atleastOneDefined = atleastOneDefined || upload.getBinariesFolder().isPresent(); + atleastOneDefined = atleastOneDefined || upload.getBinaryFile().isPresent(); + + if (!atleastOneDefined) { + throw new IllegalStateException("Neither source folder, binary folder, source file or binary file is defined for reference id:" + referenceId); } } diff --git a/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/config/SystemTestConfigurationBuilder.java b/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/config/SystemTestConfigurationBuilder.java index d481e1267f..d56482f11d 100644 --- a/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/config/SystemTestConfigurationBuilder.java +++ b/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/config/SystemTestConfigurationBuilder.java @@ -3,6 +3,7 @@ import java.net.URI; import java.net.URL; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Optional; @@ -266,6 +267,25 @@ public SecHubSetupBuilder admin(String userId, String apiToken) { return this; } + /** + * Start definition for project using default project name: + * {@value com.mercedesbenz.sechub.systemtest.config.DefaultFallback.StringConstants#DEFAULT_PROJECT_ID} + * + * @return project setup builder + */ + public ProjectSetupBuilder project() { + return project(DefaultFallback.FALLBACK_PROJECT_NAME.getValue()); + } + + /** + * Start definition for project using given project project id. + * + * @return project setup builder + */ + public ProjectSetupBuilder project(String projectId) { + return new ProjectSetupBuilder(projectId); + } + public ConfigurationBuilder configure() { return new ConfigurationBuilder(); } @@ -346,6 +366,55 @@ public ExecutorConfigBuilder name(String name) { } } + + public class ProjectSetupBuilder extends AbstractDefinitionBuilder { + + private ProjectDefinition projectDefinition; + + public ProjectSetupBuilder(String projectId) { + + SecHubConfigurationDefinition configuration = SecHubSetupBuilder.this.getSechubDefinition().getConfigure(); + Optional> projectsOpt = configuration.getProjects(); + + List projects = null; + if (projectsOpt.isPresent()) { + projects = projectsOpt.get(); + } else { + projects = new ArrayList<>(); + configuration.setProjects(Optional.of(projects)); + } + + for (ProjectDefinition foundProjectDefinition : projects) { + if (foundProjectDefinition.getName().equalsIgnoreCase(projectId)) { + projectDefinition = foundProjectDefinition; + break; + } + } + + if (projectDefinition == null) { + projectDefinition = new ProjectDefinition(); + projectDefinition.setName(projectId); + + projects.add(projectDefinition); + } + } + + public SecHubSetupBuilder endProject() { + return SecHubSetupBuilder.this; + } + + @Override + protected AbstractDefinition resolveDefinition() { + return projectDefinition; + } + + public ProjectSetupBuilder addURItoWhiteList(String uri) { + projectDefinition.getWhitelistedURIs().add(uri); + return this; + } + + } + } public class SolutionSetupBuilder extends AbstractDefinitionBuilder { @@ -624,6 +693,16 @@ public UploadBuilder binariesFolder(String path) { return this; } + public UploadBuilder sourceFile(String path) { + uploadDefinition.setSourceFile(Optional.of(path)); + return this; + } + + public UploadBuilder binaryFile(String path) { + uploadDefinition.setBinaryFile(Optional.of(path)); + return this; + } + } } diff --git a/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/config/TestDefinition.java b/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/config/TestDefinition.java index 017423e62a..85c257e5ee 100644 --- a/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/config/TestDefinition.java +++ b/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/config/TestDefinition.java @@ -10,6 +10,7 @@ public class TestDefinition extends AbstractDefinition { private List prepare = new ArrayList<>(); private List _assert = new ArrayList<>(); private TestExecutionDefinition execute = new TestExecutionDefinition(); + private List cleanup = new ArrayList<>(); public void setName(String name) { this.name = name; @@ -23,6 +24,10 @@ public List getPrepare() { return prepare; } + public List getCleanup() { + return cleanup; + } + public TestExecutionDefinition getExecute() { return execute; } diff --git a/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/config/UploadDefinition.java b/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/config/UploadDefinition.java index d1cd833380..8deb39f249 100644 --- a/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/config/UploadDefinition.java +++ b/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/config/UploadDefinition.java @@ -9,6 +9,9 @@ public class UploadDefinition extends AbstractDefinition { private Optional sourceFolder = Optional.ofNullable(null); private Optional referenceId = Optional.ofNullable(null); + private Optional binaryFile = Optional.ofNullable(null); + private Optional sourceFile = Optional.ofNullable(null); + public Optional getBinariesFolder() { return binariesFolder; } @@ -32,4 +35,21 @@ public Optional getReferenceId() { public void setReferenceId(Optional referenceId) { this.referenceId = referenceId; } + + public Optional getBinaryFile() { + return binaryFile; + } + + public void setBinaryFile(Optional binaryFile) { + this.binaryFile = binaryFile; + } + + public Optional getSourceFile() { + return sourceFile; + } + + public void setSourceFile(Optional sourceFile) { + this.sourceFile = sourceFile; + } + } diff --git a/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/runtime/SystemTestRuntime.java b/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/runtime/SystemTestRuntime.java index c641d36da4..aadf0c0e71 100644 --- a/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/runtime/SystemTestRuntime.java +++ b/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/runtime/SystemTestRuntime.java @@ -2,17 +2,21 @@ package com.mercedesbenz.sechub.systemtest.runtime; import java.util.ArrayList; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.mercedesbenz.sechub.commons.core.RunOrFail; import com.mercedesbenz.sechub.commons.model.JSONConverter; +import com.mercedesbenz.sechub.systemtest.SystemTestParameters; import com.mercedesbenz.sechub.systemtest.config.SystemTestConfiguration; import com.mercedesbenz.sechub.systemtest.config.TestDefinition; import com.mercedesbenz.sechub.systemtest.runtime.config.SystemTestRuntimeLocalSecHubProductConfigurator; import com.mercedesbenz.sechub.systemtest.runtime.error.SystemTestErrorException; +import com.mercedesbenz.sechub.systemtest.runtime.error.SystemTestFailure; import com.mercedesbenz.sechub.systemtest.runtime.init.SystemTestRuntimeContextHealthCheck; import com.mercedesbenz.sechub.systemtest.runtime.init.SystemTestRuntimePreparator; import com.mercedesbenz.sechub.systemtest.runtime.launch.ExecutionSupport; @@ -66,9 +70,9 @@ public SystemTestRuntime(LocationSupport locationSupport, ExecutionSupport execS this.secondsToWaitForRemainingProcesses = getValue("SYSTEMTEST_WAIT_FOR_REMAINING_PROCESSES", DEFAULT_SECONDS_TO_WAIT_FOR_REMAINIG_PROCESSES); } - public SystemTestResult run(SystemTestConfiguration configuration, boolean localRun, boolean isDryRun) { + public SystemTestResult run(SystemTestConfiguration configuration, SystemTestParameters parameters) { - SystemTestRuntimeContext context = initialize(configuration, localRun, isDryRun); + SystemTestRuntimeContext context = initialize(configuration, parameters); return runAfterInitialization(context); @@ -112,15 +116,7 @@ private SystemTestResult runAfterInitialization(SystemTestRuntimeContext context /* execute tests */ switchToStage("Test", context); - - /* handle dynamic variables */ - List originTestList = context.getConfiguration().getTests(); - List workingList = new ArrayList<>(originTestList); - originTestList.clear(); - - for (TestDefinition test : workingList) { - testEngine.runTest(test, context); - } + handleTests(context); /* shutdown */ switchToStage("Shutdown", context); @@ -166,6 +162,43 @@ private SystemTestResult runAfterInitialization(SystemTestRuntimeContext context } } + private void handleTests(SystemTestRuntimeContext context) { + /* handle dynamic variables */ + List originTestList = context.getConfiguration().getTests(); + List workingList = new ArrayList<>(originTestList); + originTestList.clear(); + + boolean atLeastOneTestExecuted = false; + Set allExistingTestNames = new LinkedHashSet<>(); + for (TestDefinition test : workingList) { + String testName = test.getName(); + + allExistingTestNames.add(testName); + + if (context.isRunningTest(testName)) { + atLeastOneTestExecuted = true; + testEngine.runTest(test, context); + } + } + if (!atLeastOneTestExecuted) { + SystemTestRunResult missingTestsResult = new SystemTestRunResult(""); + missingTestsResult.setFailure(new SystemTestFailure("No tests were executed", "From " + workingList.size() + " defined tests none was executed")); + context.getResults().add(missingTestsResult); + } + + if (!context.isRunningAllTests()) { + /* check if the user wanted a test to run which does not exist */ + for (String testToRun : context.getTestsToRun()) { + if (!allExistingTestNames.contains(testToRun)) { + SystemTestRunResult missingTestsResult = new SystemTestRunResult(testToRun); + missingTestsResult.setFailure( + new SystemTestFailure("Test '" + testToRun + "' is not defined!", "Following tests are defined: " + allExistingTestNames)); + context.getResults().add(missingTestsResult); + } + } + } + } + private void logResult(SystemTestRuntimeContext context, SystemTestResult result) { String resultStatus = "FAILED"; int testsFailed = 0; @@ -208,14 +241,15 @@ private void logResult(SystemTestRuntimeContext context, SystemTestResult result LOG.info(info); } - private SystemTestRuntimeContext initialize(SystemTestConfiguration configuration, boolean localRun, boolean isDryRun) { + private SystemTestRuntimeContext initialize(SystemTestConfiguration configuration, SystemTestParameters parameters) { SystemTestRuntimeContext context = new SystemTestRuntimeContext(configuration, locationSupport.getWorkspaceRoot(), locationSupport.getAdditionalResourcesRoot()); - context.localRun = localRun; - context.dryRun = isDryRun; + context.localRun = parameters.isLocalRun(); + context.dryRun = parameters.isDryRun(); + context.addTestsToRun(parameters.getTestsToRun()); - LOG.info("Starting - run {}{}\nWorking directory: {}", isDryRun ? "DRY " : "", localRun ? "LOCAL" : "REMOTE"); + LOG.info("Starting - run {}{}\nWorking directory: {}", context.dryRun ? "DRY " : "", context.localRun ? "LOCAL" : "REMOTE"); context.locationSupport = locationSupport; context.environmentProvider = environmentSupport; diff --git a/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/runtime/SystemTestRuntimeContext.java b/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/runtime/SystemTestRuntimeContext.java index 977ce8d8a9..f9e90973e5 100644 --- a/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/runtime/SystemTestRuntimeContext.java +++ b/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/runtime/SystemTestRuntimeContext.java @@ -56,7 +56,7 @@ public class SystemTestRuntimeContext { Path additionalResourcesRoot; private SystemTestConfiguration configuration; - private SystemTestRunResult currentResult; + private SystemTestRunResult currentResult = new SystemTestRunResult("no-test-fallback-" + System.nanoTime()); private Set results = new LinkedHashSet<>(); private SystemTestRuntimeMetaData runtimeMetaData = new SystemTestRuntimeMetaData(); private Map pdsSolutionRuntimeDataMap = new LinkedHashMap<>(); @@ -65,9 +65,17 @@ public class SystemTestRuntimeContext { private SecHubClient remoteUserSecHubClient; private SecHubClient localAdminSecHubClient; private SystemTestTemplateEngine templateEngine = new SystemTestTemplateEngine(); + private Set testsToRun = new LinkedHashSet<>(); private Map localTechUserPdsClientMap = new TreeMap<>(); + public void addTestsToRun(Collection testNames) { + if (testNames == null) { + return; + } + testsToRun.addAll(testNames); + } + public void alterConfguration(SystemTestConfiguration configuration) { this.configuration = configuration; } @@ -92,6 +100,18 @@ public boolean isDryRun() { return dryRun; } + public boolean isRunningAllTests() { + return testsToRun.isEmpty(); + } + + public boolean isRunningTest(String name) { + return isRunningAllTests() || testsToRun.contains(name); + } + + public Set getTestsToRun() { + return Collections.unmodifiableSet(testsToRun); + } + /* only for tests */ SystemTestRuntimeContext() { } diff --git a/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/runtime/SystemTestRuntimeMetaData.java b/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/runtime/SystemTestRuntimeMetaData.java index 759084cc7e..6e0464d9bd 100644 --- a/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/runtime/SystemTestRuntimeMetaData.java +++ b/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/runtime/SystemTestRuntimeMetaData.java @@ -24,11 +24,28 @@ public class SystemTestRuntimeMetaData { private Map pdsSolutionConfigurations = new LinkedHashMap<>(); private TextFileReader textFileReader; - private class PdsSolutionData { + class PdsSolutionData { private PDSServerConfiguration serverConfiguration; private Path pathToServerConfiguration; private PDSSolutionDefinition solutionDefinition; - public String solutionPathToConfigFile; + private String solutionPathToConfigFile; + + public PDSServerConfiguration getServerConfiguration() { + return serverConfiguration; + } + + public Path getPathToServerConfiguration() { + return pathToServerConfiguration; + } + + public PDSSolutionDefinition getSolutionDefinition() { + return solutionDefinition; + } + + public String getSolutionPathToConfigFile() { + return solutionPathToConfigFile; + } + } public SystemTestRuntimeMetaData() { diff --git a/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/runtime/config/SystemTestRuntimeLocalSecHubProductConfigurator.java b/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/runtime/config/SystemTestRuntimeLocalSecHubProductConfigurator.java index c58db453f0..40600e4279 100644 --- a/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/runtime/config/SystemTestRuntimeLocalSecHubProductConfigurator.java +++ b/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/runtime/config/SystemTestRuntimeLocalSecHubProductConfigurator.java @@ -22,6 +22,7 @@ import com.mercedesbenz.sechub.api.ExecutorConfigurationSetup; import com.mercedesbenz.sechub.api.ExecutorConfigurationSetupCredentials; import com.mercedesbenz.sechub.api.Project; +import com.mercedesbenz.sechub.api.ProjectWhiteList; import com.mercedesbenz.sechub.api.SecHubClient; import com.mercedesbenz.sechub.api.SecHubClientException; import com.mercedesbenz.sechub.commons.model.ScanType; @@ -111,7 +112,12 @@ private void createProjects(SystemTestRuntimeContext context) throws SecHubClien project.setName(projectName); project.setOwner(client.getUsername());// we use the administrator as owner of the project + ProjectWhiteList whiteList = project.getWhiteList(); + for (String whiteListEntry : projectDefinition.getWhitelistedURIs()) { + whiteList.getUris().add(whiteListEntry); + } client.createProject(project); + } } diff --git a/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/runtime/error/SystemTestFailure.java b/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/runtime/error/SystemTestFailure.java index 87b0a44c84..7538b41961 100644 --- a/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/runtime/error/SystemTestFailure.java +++ b/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/runtime/error/SystemTestFailure.java @@ -6,6 +6,14 @@ public class SystemTestFailure { private String message; private String details; + public SystemTestFailure() { + } + + public SystemTestFailure(String message, String details) { + this.message = message; + this.details = details; + } + public void setMessage(String message) { this.message = message; } diff --git a/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/runtime/init/SystemTestRuntimePreparator.java b/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/runtime/init/SystemTestRuntimePreparator.java index 4a669fcacc..b5a9684f84 100644 --- a/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/runtime/init/SystemTestRuntimePreparator.java +++ b/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/runtime/init/SystemTestRuntimePreparator.java @@ -72,11 +72,8 @@ public void prepare(SystemTestRuntimeContext context) { initializeAlteredConfiguration(context); - if (!context.isLocalRun()) { - LOG.debug("Skip preparation - run is not local"); - return; - } prepareLocal(context); + prepareRemote(context); prepareTests(context); } @@ -131,7 +128,10 @@ private void handleWebScan(TestDefinition test, SystemTestRuntimeContext context } SecHubWebScanConfiguration webScan = webScanOpt.get(); LOG.warn("Web scan found, but no special preparation done for url: {}", webScan.getUrl()); - + if (webScan.getApi().isEmpty()) { + return; + } + handleUsedDataConfigurationObjects(webScan.getApi().get(), test, context); } private void handleInfraScan(TestDefinition test, SystemTestRuntimeContext context, RunSecHubJobDefinition runSecHubJob) { @@ -244,6 +244,10 @@ private Map createRuntimeVariables(SystemTestRuntimeContext cont } private void prepareLocal(SystemTestRuntimeContext context) { + if (!context.isLocalRun()) { + LOG.debug("Skip local preparation - run is not local"); + return; + } createDefaultsWhereNothingDefined(context); prepareScripts(context); @@ -256,6 +260,14 @@ private void prepareLocal(SystemTestRuntimeContext context) { } + private void prepareRemote(SystemTestRuntimeContext context) { + if (context.isLocalRun()) { + LOG.debug("Skip remote preparation - run is not remote"); + return; + } + /* currently no special remote preparation at all */ + } + private void addFallbackDefaultProfileToExecutorsWithoutProfile(SystemTestRuntimeContext context) { SecHubConfigurationDefinition sechubConfig = context.getLocalSecHubConfigurationOrFail(); List executors = sechubConfig.getExecutors(); @@ -267,29 +279,38 @@ private void addFallbackDefaultProfileToExecutorsWithoutProfile(SystemTestRuntim } } - private void createFallbackDefaultProjectWhenNoProjectsDefined(SystemTestRuntimeContext context) { + private void addFallbackDefaultProjectAndProfilesWhenNotDefined(SystemTestRuntimeContext context) { SecHubConfigurationDefinition sechubConfig = context.getLocalSecHubConfigurationOrFail(); - Optional> projects = sechubConfig.getProjects(); - if (!projects.isPresent()) { + Optional> projectsOpt = sechubConfig.getProjects(); + if (!projectsOpt.isPresent()) { sechubConfig.setProjects(Optional.of(new ArrayList<>())); } - List projectDefinitions = sechubConfig.getProjects().get(); - if (projectDefinitions.isEmpty()) { + List projects = sechubConfig.getProjects().get(); + + /* handle missing project definitions */ + if (projects.isEmpty()) { + ProjectDefinition fallbackProject = new ProjectDefinition(); + + fallbackProject.setName(FALLBACK_PROJECT_NAME.getValue()); + fallbackProject.setComment("Auto created fallback default project"); - ProjectDefinition fallback = new ProjectDefinition(); + projects.add(fallbackProject); + } - fallback.setName(FALLBACK_PROJECT_NAME.getValue()); - fallback.setComment("Auto created fallback default project"); - fallback.getProfiles().add(FALLBACK_PROFILE_ID.getValue()); - projectDefinitions.add(fallback); + /* handle missing profile definitions for projects */ + for (ProjectDefinition projectDefinition : projects) { + List profiles = projectDefinition.getProfiles(); + if (profiles.isEmpty()) { + profiles.add(FALLBACK_PROFILE_ID.getValue()); + } } } private void createDefaultsWhereNothingDefined(SystemTestRuntimeContext context) { createFallbackSecHubSetupParts(context); - createFallbackDefaultProjectWhenNoProjectsDefined(context); + addFallbackDefaultProjectAndProfilesWhenNotDefined(context); createFallbackNamesForExecutorsWithoutName(context); addFallbackDefaultProfileToExecutorsWithoutProfile(context); @@ -311,7 +332,6 @@ private void createFallbackNamesForExecutorsWithoutName(SystemTestRuntimeContext executor.setName(newName); } } - } private void createFallbacksForPDSSolutions(SystemTestRuntimeContext context) { diff --git a/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/runtime/launch/SystemTestRuntimeProductLauncher.java b/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/runtime/launch/SystemTestRuntimeProductLauncher.java index 2c5c7e09da..5bbbd9064a 100644 --- a/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/runtime/launch/SystemTestRuntimeProductLauncher.java +++ b/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/runtime/launch/SystemTestRuntimeProductLauncher.java @@ -202,6 +202,10 @@ public void waitUntilSecHubAdminAvailable(SystemTestRuntimeContext context) { if (!context.isLocalRun()) { return; } + if (context.isDryRun()) { + LOG.debug("Skip sechub admin available check - becaus dry run"); + return; + } SecHubClient sechubClient = context.getLocalAdminSecHubClient(); boolean adminAccountAvailable = false; diff --git a/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/runtime/testengine/CurrentTestVariableCalculatorFactory.java b/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/runtime/testengine/CurrentTestVariableCalculatorFactory.java new file mode 100644 index 0000000000..9fd445561c --- /dev/null +++ b/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/runtime/testengine/CurrentTestVariableCalculatorFactory.java @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +package com.mercedesbenz.sechub.systemtest.runtime.testengine; + +import com.mercedesbenz.sechub.systemtest.config.TestDefinition; +import com.mercedesbenz.sechub.systemtest.runtime.SystemTestRuntimeContext; + +interface CurrentTestVariableCalculatorFactory { + + CurrentTestVariableCalculator create(TestDefinition test, SystemTestRuntimeContext context); +} \ No newline at end of file diff --git a/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/runtime/testengine/SystemTestRuntimeTestAssertion.java b/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/runtime/testengine/SystemTestRuntimeTestAssertion.java index ba8760b9a4..c77f6a4670 100644 --- a/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/runtime/testengine/SystemTestRuntimeTestAssertion.java +++ b/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/runtime/testengine/SystemTestRuntimeTestAssertion.java @@ -5,6 +5,7 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.util.List; +import java.util.Optional; import java.util.UUID; import com.mercedesbenz.sechub.api.SecHubReport; @@ -24,17 +25,30 @@ public void assertTest(TestAssertDefinition assertDefinition, TestEngineTestCont } private void handleSecHubAsserts(TestAssertDefinition assertDefinition, TestEngineTestContext testContext) { + if (testContext.hasFailed()) { + // already marked as failed - e.g. by other test asserts + return; + } + if (!testContext.isSecHubTest()) { return; } - if (assertDefinition.getSechubResult().isEmpty()) { + + Optional sechubResult = assertDefinition.getSechubResult(); + if (sechubResult.isEmpty()) { return; } - handleSecHubAssert(assertDefinition.getSechubResult().get(), testContext); + + handleSecHubAssert(sechubResult.get(), testContext); } private void handleSecHubAssert(AssertSechubResultDefinition sechubResultAssert, TestEngineTestContext testContext) { SecHubReport report = testContext.getSecHubRunData().getReport(); + if (report == null) { + testContext.markAsFailed("Sechub report not available", + "This should not happen. Please look at the unit test console log and inside SecHub server log for details"); + return; + } String reportAsJson = JSONConverter.get().toJSON(report, true); if (sechubResultAssert.getHasTrafficLight().isPresent()) { diff --git a/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/runtime/testengine/SystemTestRuntimeTestEngine.java b/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/runtime/testengine/SystemTestRuntimeTestEngine.java index ef600972ff..f64ade09de 100644 --- a/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/runtime/testengine/SystemTestRuntimeTestEngine.java +++ b/sechub-systemtest/src/main/java/com/mercedesbenz/sechub/systemtest/runtime/testengine/SystemTestRuntimeTestEngine.java @@ -15,8 +15,8 @@ import org.slf4j.LoggerFactory; import com.mercedesbenz.sechub.api.SecHubClient; -import com.mercedesbenz.sechub.api.SecHubClientException; import com.mercedesbenz.sechub.api.SecHubReport; +import com.mercedesbenz.sechub.commons.core.util.SimpleStringUtils; import com.mercedesbenz.sechub.commons.model.JSONConverter; import com.mercedesbenz.sechub.commons.model.SecHubConfigurationModel; import com.mercedesbenz.sechub.systemtest.config.CopyDefinition; @@ -55,6 +55,7 @@ public class SystemTestRuntimeTestEngine { RunSecHubJobDefinitionTransformer runSecHubJobTransformer = new RunSecHubJobDefinitionTransformer(); SystemTestRuntimeTestAssertion testAssertion = new SystemTestRuntimeTestAssertion(); + CurrentTestVariableCalculatorFactory currentTestVariableCalculatorFactory = new DefaultCurrentTestVariableCalculatorFactory(); public SystemTestRuntimeTestEngine(ExecutionSupport execSupport) { this.execSupport = execSupport; @@ -62,15 +63,17 @@ public SystemTestRuntimeTestEngine(ExecutionSupport execSupport) { public void runTest(TestDefinition test, SystemTestRuntimeContext runtimeContext) { TestEngineTestContext testContext = createTestContext(test, runtimeContext); - - if (!prepareTest(testContext)) { - /* preparation failed */ + if (testContext.hasFailed()) { return; } + prepareTest(testContext); + try { + executeTest(testContext); - executeTest(testContext); - - assertTest(testContext); + assertTest(testContext); + } finally { + cleanupTest(testContext); + } } @@ -81,30 +84,53 @@ private TestEngineTestContext createTestContext(TestDefinition test, SystemTestR } private void assertTest(TestEngineTestContext testContext) { + if (testContext.hasFailed()) { + // already marked as failed + return; + } List asserts = testContext.getTest().getAssert(); for (TestAssertDefinition assertDef : asserts) { testAssertion.assertTest(assertDef, testContext); } } - private boolean prepareTest(TestEngineTestContext testContext) { - boolean prepared = false; + private void prepareTest(TestEngineTestContext testContext) { try { executePreparationSteps("Prepare", testContext); - prepared = true; - } catch (SystemTestScriptExecutionException e) { + } catch (Exception e) { + LOG.error("Preparation for test '{}' failed!", testContext.getTestName(), e); testContext.markAsFailed("Was not able to prepare test", e); } - return prepared; + } + + private void cleanupTest(TestEngineTestContext testContext) { + try { + executeCleanupSteps("Cleanup", testContext); + } catch (Exception e) { + LOG.error("Cleanup for test '{}' failed!", testContext.getTestName(), e); + if (!testContext.hasFailed()) { + /* only when not already failed we add the cleanup step failure */ + testContext.markAsFailed("Was not able to cleanup test", e); + } + } + } private void executeTest(TestEngineTestContext testContext) { + if (testContext.hasFailed()) { + // already marked as failed + return; + } /* mark current test - fail if not supported */ if (testContext.isSecHubTest()) { try { launchSecHubJob(testContext); - } catch (SecHubClientException e) { - testContext.markAsFailed("Was not able to launch SecHub job", e); + } catch (Exception e) { + String reason = e.getMessage(); + if (reason == null) { + reason = e.getClass().getSimpleName(); + } + testContext.markAsFailed("Was not able to launch SecHub job. Reason: " + SimpleStringUtils.truncateWhenTooLong(reason, 150), e); } } else { // currently we do only support SecHub runs @@ -113,7 +139,7 @@ private void executeTest(TestEngineTestContext testContext) { } } - private void launchSecHubJob(TestEngineTestContext testContext) throws SecHubClientException { + private void launchSecHubJob(TestEngineTestContext testContext) throws Exception { SecHubClient clientForScheduling = null; SystemTestRuntimeContext runtimeContext = testContext.getRuntimeContext(); @@ -207,8 +233,15 @@ private Path resolveWorkingDirectoryRealPathForCurrentTest(TestEngineTestContext private void executePreparationSteps(String name, TestEngineTestContext testContext) throws SystemTestScriptExecutionException { TestDefinition test = testContext.getTest(); + executeSteps(name, testContext, test.getPrepare()); + } + + private void executeCleanupSteps(String name, TestEngineTestContext testContext) throws SystemTestScriptExecutionException { + TestDefinition test = testContext.getTest(); + executeSteps(name, testContext, test.getCleanup()); + } - List steps = test.getPrepare(); + private void executeSteps(String name, TestEngineTestContext testContext, List steps) throws SystemTestScriptExecutionException { if (steps.isEmpty()) { return; } @@ -216,20 +249,20 @@ private void executePreparationSteps(String name, TestEngineTestContext testCont for (ExecutionStepDefinition step : steps) { LOG.trace("Enter: {} - step: {}", name, step.getComment()); if (step.getCopy().isPresent()) { - executePreparationCopy(testContext, step.getCopy().get()); + executeCopyStep(testContext, step.getCopy().get()); } if (step.getScript().isPresent()) { - executePreparationScript(testContext, step.getScript().get()); + executeScript(testContext, step.getScript().get()); } } } - private void executePreparationCopy(TestEngineTestContext testContext, CopyDefinition copyDirectoriesDefinition) { + private void executeCopyStep(TestEngineTestContext testContext, CopyDefinition copyDirectoriesDefinition) { String from = copyDirectoriesDefinition.getFrom(); String to = copyDirectoriesDefinition.getTo(); - from = testContext.dynamicVariableGenerator.calculateValue(from); - to = testContext.dynamicVariableGenerator.calculateValue(to); + from = testContext.currentTestVariableCalculator.calculateValue(from); + to = testContext.currentTestVariableCalculator.calculateValue(to); try { Path source = Paths.get(from).toRealPath(); @@ -249,9 +282,10 @@ private void executePreparationCopy(TestEngineTestContext testContext, CopyDefin } } - private void executePreparationScript(TestEngineTestContext testContext, ScriptDefinition scriptDefinition) throws SystemTestScriptExecutionException { + private void executeScript(TestEngineTestContext testContext, ScriptDefinition scriptDefinition) throws SystemTestScriptExecutionException { - ProcessContainer processContainer = execSupport.execute(scriptDefinition, testContext.getDynamicVariableGenerator(), SystemTestExecutionState.PREPARE); + ProcessContainer processContainer = execSupport.execute(scriptDefinition, testContext.getCurrentTestVariableCalculator(), + SystemTestExecutionState.PREPARE); long startTime = System.currentTimeMillis(); long diffTime = startTime; @@ -264,7 +298,7 @@ private void executePreparationScript(TestEngineTestContext testContext, ScriptD diffTime = System.currentTimeMillis(); long secondsWaited = (System.currentTimeMillis() - startTime) / 1000; - LOG.info("Waiting now for test prepare script: {} - {} seconds waited at all", scriptDefinition.getPath(), secondsWaited); + LOG.info("Waiting now for test script: {} - {} seconds waited at all", scriptDefinition.getPath(), secondsWaited); } Thread.sleep(300); } catch (InterruptedException e) { @@ -307,25 +341,41 @@ public UUID getSecHubJobUUID() { } + class DefaultCurrentTestVariableCalculatorFactory implements CurrentTestVariableCalculatorFactory { + + @Override + public CurrentTestVariableCalculator create(TestDefinition test, SystemTestRuntimeContext runtimeContext) { + return new CurrentTestVariableCalculator(test, runtimeContext); + } + + } + class TestEngineTestContext { private final SystemTestRuntimeTestEngine systemTestRuntimeTestEngine; private SecHubRunData secHubRunData; SystemTestRuntimeContext runtimeContext; TestDefinition test; - private CurrentTestVariableCalculator dynamicVariableGenerator; + private CurrentTestVariableCalculator currentTestVariableCalculator; TestEngineTestContext(SystemTestRuntimeTestEngine systemTestRuntimeTestEngine, TestDefinition test, SystemTestRuntimeContext runtimeContext) { this.systemTestRuntimeTestEngine = systemTestRuntimeTestEngine; this.test = test; this.runtimeContext = runtimeContext; - this.dynamicVariableGenerator = new CurrentTestVariableCalculator(test, runtimeContext); + this.currentTestVariableCalculator = currentTestVariableCalculatorFactory.create(test, runtimeContext); appendSecHubRunData(); } - public VariableCalculator getDynamicVariableGenerator() { - return dynamicVariableGenerator; + public String getTestName() { + if (test == null) { + return ""; + } + return test.getName(); + } + + public VariableCalculator getCurrentTestVariableCalculator() { + return currentTestVariableCalculator; } public TestDefinition getTest() { @@ -361,7 +411,7 @@ private void appendSecHubRunData() { String configurationAsJson = converter.toJSON(secHubConfiguration); - String changedConfigurationAsJson = dynamicVariableGenerator.replace(configurationAsJson); + String changedConfigurationAsJson = currentTestVariableCalculator.replace(configurationAsJson); secHubRunData.secHubConfiguration = converter.fromJSON(SecHubConfigurationModel.class, changedConfigurationAsJson); } @@ -385,6 +435,10 @@ public void markAsFailed(String message, String hint, Exception e) { runtimeContext.getCurrentResult().setFailure(failure); } + public boolean hasFailed() { + return runtimeContext.getCurrentResult().hasFailed(); + } + private String createDetails(String hint, Exception e) { StringBuilder sb = new StringBuilder(); sb.append("\n- SecHubJob UUID: "); diff --git a/sechub-systemtest/src/test/java/com/mercedesbenz/sechub/systemtest/SystemTestDryRunTest.java b/sechub-systemtest/src/test/java/com/mercedesbenz/sechub/systemtest/SystemTestDryRunTest.java index 7d2d1b7345..0fd4d4db92 100644 --- a/sechub-systemtest/src/test/java/com/mercedesbenz/sechub/systemtest/SystemTestDryRunTest.java +++ b/sechub-systemtest/src/test/java/com/mercedesbenz/sechub/systemtest/SystemTestDryRunTest.java @@ -7,6 +7,10 @@ import java.io.IOException; import java.nio.file.Path; +import java.util.Iterator; +import java.util.List; +import java.util.Optional; +import java.util.Set; import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.BeforeEach; @@ -16,9 +20,13 @@ import org.slf4j.LoggerFactory; import com.mercedesbenz.sechub.commons.model.JSONConverter; +import com.mercedesbenz.sechub.systemtest.config.LocalSetupDefinition; +import com.mercedesbenz.sechub.systemtest.config.ProjectDefinition; import com.mercedesbenz.sechub.systemtest.config.RuntimeVariable; +import com.mercedesbenz.sechub.systemtest.config.SecHubConfigurationDefinition; import com.mercedesbenz.sechub.systemtest.config.SystemTestConfiguration; import com.mercedesbenz.sechub.systemtest.runtime.SystemTestResult; +import com.mercedesbenz.sechub.systemtest.runtime.SystemTestRunResult; import com.mercedesbenz.sechub.systemtest.runtime.SystemTestRuntimeException; import com.mercedesbenz.sechub.systemtest.runtime.launch.ProcessContainerFailedException; import com.mercedesbenz.sechub.test.TestFileReader; @@ -47,6 +55,56 @@ void beforeEach(TestInfo info) { LOG.info("--------------------------------------------------------------------------------------------------------------------------------"); } + @Test + void faked_webscan_can_be_executed_without_errors_and_contains_expected_data_in_configuration() throws IOException { + + /* @formatter:off */ + + /* prepare*/ + SystemTestConfiguration configuration = configure(). + localSetup(). + secHub(). + project(). + addURItoWhiteList("https://example.com/app-to-test"). + endProject(). + endSecHub(). + endLocalSetup(). + test("at-least-one-testentry"). + runSecHubJob(). + secretScan().endScan(). + uploads().addBinaryUploadWithDefaultRef("/path"). + endUploads(). + endRunSecHub(). + endTest(). + build(); + + /* execute */ + SystemTestResult result = systemTestApi. + runSystemTests(params(). + localRun(). + dryRun(). + testConfiguration(configuration). + additionalResourcesPath(ADDITIONAL_RESOURCES_PATH). + pdsSolutionPath(FAKED_PDS_SOLUTIONS_PATH). + build()); + /* @formatter:on */ + + /* test */ + if (result.hasFailedTests()) { + fail("The execution failed:" + result.toString()); + } + + Optional localSetup = configuration.getSetup().getLocal(); + SecHubConfigurationDefinition configure = localSetup.get().getSecHub().getConfigure(); + Optional> projectsOpt = configure.getProjects(); + + List projects = projectsOpt.get(); + assertEquals(1, projects.size()); + ProjectDefinition project = projects.iterator().next(); + assertTrue(project.getWhitelistedURIs().contains("https://example.com/app-to-test")); + + } + @Test void faked_gosec_can_be_executed_without_errors() throws IOException { /* @formatter:off */ @@ -199,6 +257,191 @@ void faked_gosec_can_be_executed_without_errors() throws IOException { /* @formatter:on */ } + @Test + void faked_test_can_be_executed_when_testsToRun_not_defined() throws IOException { + /* @formatter:off */ + + /* prepare */ + SystemTestConfiguration configuration = configure(). + + localSetup(). + endLocalSetup(). + + test("correct-testname"). + runSecHubJob(). + webScan(). + url("https://example.com"). + endScan(). + uploads(). + + endUploads(). + endRunSecHub(). + endTest(). + + build(); + + + /* execute */ + SystemTestResult result = systemTestApi.runSystemTests( + params(). + localRun(). + dryRun(). + testConfiguration(configuration). + build() + ); + + /* test */ + if (result.hasFailedTests()) { + fail("The execution failed:"+result.toString()); + } + + /* @formatter:on */ + } + + @Test + void faked_test_can_be_executed_when_testsToRun_contains_correct_testname() throws IOException { + /* @formatter:off */ + + /* prepare */ + SystemTestConfiguration configuration = configure(). + + localSetup(). + endLocalSetup(). + + test("correct-testname"). + runSecHubJob(). + webScan(). + url("https://example.com"). + endScan(). + uploads(). + + endUploads(). + endRunSecHub(). + endTest(). + + build(); + + + /* execute */ + SystemTestResult result = systemTestApi.runSystemTests( + params(). + localRun(). + dryRun(). + testsToRun("correct-testname"). + testConfiguration(configuration). + build() + ); + + /* test */ + if (result.hasFailedTests()) { + fail("The execution failed:"+result.toString()); + } + + /* @formatter:on */ + } + + @Test + void faked_test_cannot_be_executed_when_wrong_test_name_used_for_runtests() throws IOException { + /* @formatter:off */ + + /* prepare */ + SystemTestConfiguration configuration = configure(). + + localSetup(). + endLocalSetup(). + + test("correct-testname"). + runSecHubJob(). + webScan(). + url("https://example.com"). + endScan(). + uploads(). + + endUploads(). + endRunSecHub(). + endTest(). + + build(); + + + /* execute */ + SystemTestResult result = systemTestApi.runSystemTests( + params(). + localRun(). + dryRun(). + testsToRun("wrong-testname"). + testConfiguration(configuration). + build() + ); + + /* test */ + if (!result.hasFailedTests()) { + fail("The execution has not failed:"+result.toString()); + } + Set runs = result.getRuns(); + assertEquals(2, runs.size()); + Iterator iterator = runs.iterator(); + SystemTestRunResult run1 = iterator.next(); + assertEquals("No tests were executed", run1.getFailure().getMessage()); + + SystemTestRunResult run2 = iterator.next(); + assertEquals("Test 'wrong-testname' is not defined!", run2.getFailure().getMessage()); + + /* @formatter:on */ + } + + @Test + void faked_test_cannot_be_executed_when_one_correct_and_one_wrong_test_name_used_for_runtests() throws IOException { + /* @formatter:off */ + + /* prepare */ + SystemTestConfiguration configuration = configure(). + + localSetup(). + endLocalSetup(). + + test("correct-testname"). + runSecHubJob(). + webScan(). + url("https://example.com"). + endScan(). + uploads(). + + endUploads(). + endRunSecHub(). + endTest(). + + build(); + + + /* execute */ + SystemTestResult result = systemTestApi.runSystemTests( + params(). + localRun(). + dryRun(). + testsToRun("correct-testname", "wrong-testname"). + testConfiguration(configuration). + build() + ); + + /* test */ + if (!result.hasFailedTests()) { + fail("The execution has not failed:"+result.toString()); + } + Set runs = result.getRuns(); + assertEquals(2, runs.size()); + Iterator iterator = runs.iterator(); + + SystemTestRunResult run1 = iterator.next(); + assertFalse(run1.hasFailed()); + + SystemTestRunResult run2 = iterator.next(); + assertTrue(run2.hasFailed()); + assertEquals("Test 'wrong-testname' is not defined!", run2.getFailure().getMessage()); + + /* @formatter:on */ + } + @Test void fail_because_unknown_runtime_variable() { /* @formatter:off */ diff --git a/sechub-systemtest/src/test/java/com/mercedesbenz/sechub/systemtest/SystemTestFrameworkIntTest.java b/sechub-systemtest/src/test/java/com/mercedesbenz/sechub/systemtest/SystemTestFrameworkIntTest.java index 1c67107e9a..a991621216 100644 --- a/sechub-systemtest/src/test/java/com/mercedesbenz/sechub/systemtest/SystemTestFrameworkIntTest.java +++ b/sechub-systemtest/src/test/java/com/mercedesbenz/sechub/systemtest/SystemTestFrameworkIntTest.java @@ -78,6 +78,9 @@ void even_integration_test_setup_can_be_tested_codescan_source_only_and_gen_exam comment("We do not define start/stop here, because reuse running local SecHub server"). url(new URL("https://localhost:"+SECHUB_PORT)). admin(toEnvVariable(ENV_TEST_INTTEST_ADMIN_USERID),toSecretEnvVariable(ENV_TEST_INTTEST_ADMIN_APITOKEN)). + project(). + addURItoWhiteList("https://example.org/testproject1"). + endProject(). /* * We do not define any steps here - developers must have started the * integration test SecHub server locally in IDE diff --git a/sechub-systemtest/src/test/java/com/mercedesbenz/sechub/systemtest/config/SystemTestConfigurationTest.java b/sechub-systemtest/src/test/java/com/mercedesbenz/sechub/systemtest/config/SystemTestConfigurationTest.java index 2917c9cd02..7301e97a19 100644 --- a/sechub-systemtest/src/test/java/com/mercedesbenz/sechub/systemtest/config/SystemTestConfigurationTest.java +++ b/sechub-systemtest/src/test/java/com/mercedesbenz/sechub/systemtest/config/SystemTestConfigurationTest.java @@ -15,6 +15,8 @@ import org.slf4j.LoggerFactory; import com.mercedesbenz.sechub.commons.model.JSONConverter; +import com.mercedesbenz.sechub.commons.model.SecHubCodeScanConfiguration; +import com.mercedesbenz.sechub.commons.model.SecHubSecretScanConfiguration; import com.mercedesbenz.sechub.commons.model.TrafficLight; class SystemTestConfigurationTest { @@ -25,6 +27,8 @@ class SystemTestConfigurationTest { private static final String DEFINED_PROFILE = "a-defined-profile-when-not-default-used"; + private static final String DEFINED_REFERENCE_ID = "an-upload-reference-id"; + @Test void a_full_blown_setup_can_be_serialized_and_deserialized() throws Exception { @@ -254,11 +258,21 @@ private void buildTest(SystemTestConfiguration configuration) { runSecHubJob1.setProject(DEFINED_PROJECT_NAME); UploadDefinition upload = new UploadDefinition(); - upload.setComment("Here we can define either binaries or sources to upload - we define the folders, framework will create tars/zips automatically"); + upload.setComment( + "Here we can define either binaries or sources to upload - we define the folders, framework will create tars/zips automatically. When no upload reference id is defined, " + + DefaultFallback.FALLBACK_UPLOAD_REF_ID.getValue() + " is used as default"); upload.setSourceFolder(Optional.of("./checkout/sources")); - upload.setReferenceId(Optional.of(DefaultFallback.FALLBACK_UPLOAD_REF_ID.getValue())); + upload.setReferenceId(Optional.of(DEFINED_REFERENCE_ID)); runSecHubJob1.getUploads().add(upload); + SecHubCodeScanConfiguration codeScan = new SecHubCodeScanConfiguration(); + codeScan.getNamesOfUsedDataConfigurationObjects().add(DEFINED_REFERENCE_ID); + runSecHubJob1.setCodeScan(Optional.of(codeScan)); + + SecHubSecretScanConfiguration secretScan = new SecHubSecretScanConfiguration(); + secretScan.getNamesOfUsedDataConfigurationObjects().add(DEFINED_REFERENCE_ID); + runSecHubJob1.setSecretScan(Optional.of(secretScan)); + test1execute1.setRunSecHubJob(Optional.of(runSecHubJob1)); // asserts diff --git a/sechub-systemtest/src/test/java/com/mercedesbenz/sechub/systemtest/runtime/RunSecHubJobDefinitionTransformerTest.java b/sechub-systemtest/src/test/java/com/mercedesbenz/sechub/systemtest/runtime/RunSecHubJobDefinitionTransformerTest.java index 37f1c9472b..053d6151f9 100644 --- a/sechub-systemtest/src/test/java/com/mercedesbenz/sechub/systemtest/runtime/RunSecHubJobDefinitionTransformerTest.java +++ b/sechub-systemtest/src/test/java/com/mercedesbenz/sechub/systemtest/runtime/RunSecHubJobDefinitionTransformerTest.java @@ -8,6 +8,9 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.ValueSource; import com.mercedesbenz.sechub.commons.model.SecHubBinaryDataConfiguration; import com.mercedesbenz.sechub.commons.model.SecHubCodeScanConfiguration; @@ -25,6 +28,11 @@ class RunSecHubJobDefinitionTransformerTest { + private static final String BINARY_FILE = "binaryFile"; + private static final String BINARIES_FOLDER = "binariesFolder"; + private static final String SOURCE_FOLDER = "sourceFolder"; + private static final String SOURCE_FILE = "sourceFile"; + private RunSecHubJobDefinitionTransformer transformerToTest; @BeforeEach @@ -137,12 +145,14 @@ void source_uploads_are_transformed_to_data_section() { /* prepare */ String referenceId = "ref1"; String folder = "somewhere"; + String file = "some-source-file"; RunSecHubJobDefinition definition = new RunSecHubJobDefinition(); UploadDefinition upload = new UploadDefinition(); upload.setReferenceId(Optional.of(referenceId)); upload.setSourceFolder(Optional.of(folder)); + upload.setSourceFile(Optional.of(file)); definition.getUploads().add(upload); @@ -165,6 +175,9 @@ void source_uploads_are_transformed_to_data_section() { List folders = fileSystemConfiguration.getFolders(); assertTrue(folders.contains(folder)); + List files = fileSystemConfiguration.getFiles(); + assertTrue(files.contains(file)); + } @Test @@ -172,13 +185,14 @@ void binary_uploads_are_transformed_to_data_section() { /* prepare */ String referenceId = "ref2"; String folder = "somewhere-bin"; + String file = "some-binary-file"; RunSecHubJobDefinition definition = new RunSecHubJobDefinition(); UploadDefinition upload = new UploadDefinition(); upload.setReferenceId(Optional.of(referenceId)); upload.setBinariesFolder(Optional.of(folder)); - + upload.setBinaryFile(Optional.of(file)); definition.getUploads().add(upload); /* execute */ @@ -199,6 +213,107 @@ void binary_uploads_are_transformed_to_data_section() { SecHubFileSystemConfiguration fileSystemConfiguration = fileSystemConfigurationOpt.get(); List folders = fileSystemConfiguration.getFolders(); assertTrue(folders.contains(folder)); + + List files = fileSystemConfiguration.getFiles(); + assertTrue(files.contains(file)); + + } + + @Test + void transformation_fails_when_no_files_and_no_folders_defined_in_upload_definition() { + /* prepare */ + String referenceId = "reference-id"; + + RunSecHubJobDefinition definition = new RunSecHubJobDefinition(); + + UploadDefinition upload = new UploadDefinition(); + upload.setReferenceId(Optional.of(referenceId)); + definition.getUploads().add(upload); + + /* execute + test */ + assertThrows(IllegalStateException.class, () -> transformerToTest.transformToSecHubConfiguration(definition)); + } + + /* @formatter:off */ + @ParameterizedTest + @CsvSource({ + SOURCE_FILE+","+BINARY_FILE, + SOURCE_FILE+","+BINARIES_FOLDER, + SOURCE_FOLDER+","+BINARY_FILE, + SOURCE_FOLDER+","+BINARIES_FOLDER + }) + /* @formatter:on */ + void transformation_fails_for_invalid_upload_combinations_on_same_reference_id(String type1, String type2) { + /* prepare */ + String referenceId = "reference-id"; + + RunSecHubJobDefinition definition = new RunSecHubJobDefinition(); + + UploadDefinition upload = new UploadDefinition(); + upload.setReferenceId(Optional.of(referenceId)); + + callUploadSetterForType(upload, type1); + callUploadSetterForType(upload, type2); + + definition.getUploads().add(upload); + + /* execute + test */ + assertThrows(IllegalStateException.class, () -> transformerToTest.transformToSecHubConfiguration(definition)); + } + + /* @formatter:off */ + @ParameterizedTest + @CsvSource({ + SOURCE_FILE+","+SOURCE_FOLDER, + BINARY_FILE+","+BINARIES_FOLDER + }) + /* @formatter:on */ + void transformation_does_not_fails_for_valid_upload_combinations_on_same_reference_id(String type1, String type2) { + /* prepare */ + String referenceId = "reference-id"; + + RunSecHubJobDefinition definition = new RunSecHubJobDefinition(); + + UploadDefinition upload = new UploadDefinition(); + upload.setReferenceId(Optional.of(referenceId)); + + callUploadSetterForType(upload, type1); + callUploadSetterForType(upload, type2); + + definition.getUploads().add(upload); + + /* execute + test */ + assertDoesNotThrow(() -> transformerToTest.transformToSecHubConfiguration(definition)); + } + + @ParameterizedTest + @ValueSource(strings = { SOURCE_FILE, SOURCE_FOLDER, BINARIES_FOLDER, BINARY_FILE }) + void transformation_does_not_fails_when_at_least_one_file_or_folder_in_upload_definition(String type) { + /* prepare */ + String referenceId = "reference-id"; + + RunSecHubJobDefinition definition = new RunSecHubJobDefinition(); + + UploadDefinition upload = new UploadDefinition(); + upload.setReferenceId(Optional.of(referenceId)); + + callUploadSetterForType(upload, type); + + definition.getUploads().add(upload); + + /* execute + test */ + assertDoesNotThrow(() -> transformerToTest.transformToSecHubConfiguration(definition)); + + } + + private void callUploadSetterForType(UploadDefinition upload, String type) { + switch (type) { + case SOURCE_FILE -> upload.setSourceFile(Optional.of("x")); + case SOURCE_FOLDER -> upload.setSourceFolder(Optional.of("x")); + case BINARIES_FOLDER -> upload.setBinariesFolder(Optional.of("y")); + case BINARY_FILE -> upload.setBinaryFile(Optional.of("y")); + default -> throw new IllegalStateException("Not tested"); + } } } diff --git a/sechub-systemtest/src/test/java/com/mercedesbenz/sechub/systemtest/runtime/SystemTestRuntimeContextTest.java b/sechub-systemtest/src/test/java/com/mercedesbenz/sechub/systemtest/runtime/SystemTestRuntimeContextTest.java new file mode 100644 index 0000000000..6b442ecf4b --- /dev/null +++ b/sechub-systemtest/src/test/java/com/mercedesbenz/sechub/systemtest/runtime/SystemTestRuntimeContextTest.java @@ -0,0 +1,73 @@ +package com.mercedesbenz.sechub.systemtest.runtime; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class SystemTestRuntimeContextTest { + private SystemTestRuntimeContext contextToTest; + + @BeforeEach + void beforeEach() { + contextToTest = new SystemTestRuntimeContext(); + } + + @Test + void initial_context_runs_all_tests() { + assertTrue(contextToTest.isRunningAllTests()); + assertTrue(contextToTest.isRunningTest("anything")); + } + + @Test + void initial_context_with_added_null_as_test_runs_all_tests() { + /* prepare */ + contextToTest.addTestsToRun(null); + + /* execute + test */ + assertTrue(contextToTest.isRunningAllTests()); + assertTrue(contextToTest.isRunningTest("anything")); + } + + @Test + void initial_context_with_empty_tests_to_run_runs_all_tests() { + /* prepare */ + contextToTest.addTestsToRun(new ArrayList<>()); + + /* execute + test */ + assertTrue(contextToTest.isRunningAllTests()); + assertTrue(contextToTest.isRunningTest("anything")); + } + + @Test + void initial_context_with_one_test_entry_to_run_runs_only_defined_one() { + /* prepare */ + List testNames = new ArrayList<>(); + testNames.add("defined-testname"); + contextToTest.addTestsToRun(testNames); + + /* execute + test */ + assertFalse(contextToTest.isRunningAllTests()); + assertTrue(contextToTest.isRunningTest("defined-testname")); + assertFalse(contextToTest.isRunningTest("anything")); + } + + @Test + void initial_context_with_two_test_entries_to_run_runs_only_defined_ones() { + /* prepare */ + List testNames = new ArrayList<>(); + testNames.add("defined-testname1"); + testNames.add("defined-testname2"); + contextToTest.addTestsToRun(testNames); + + /* execute + test */ + assertFalse(contextToTest.isRunningAllTests()); + assertTrue(contextToTest.isRunningTest("defined-testname1")); + assertTrue(contextToTest.isRunningTest("defined-testname2")); + assertFalse(contextToTest.isRunningTest("anything")); + } + +} diff --git a/sechub-systemtest/src/test/java/com/mercedesbenz/sechub/systemtest/runtime/SystemTestRuntimePreparatorTest.java b/sechub-systemtest/src/test/java/com/mercedesbenz/sechub/systemtest/runtime/SystemTestRuntimePreparatorTest.java index 0fa9f3b225..6b1a1e0ce2 100644 --- a/sechub-systemtest/src/test/java/com/mercedesbenz/sechub/systemtest/runtime/SystemTestRuntimePreparatorTest.java +++ b/sechub-systemtest/src/test/java/com/mercedesbenz/sechub/systemtest/runtime/SystemTestRuntimePreparatorTest.java @@ -6,11 +6,33 @@ import java.io.IOException; import java.nio.file.Path; +import java.util.Arrays; +import java.util.Optional; +import java.util.Set; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.mercedesbenz.sechub.commons.model.JSONConverter; +import com.mercedesbenz.sechub.commons.model.ScanType; +import com.mercedesbenz.sechub.commons.model.SecHubCodeScanConfiguration; +import com.mercedesbenz.sechub.commons.model.SecHubDataConfigurationUsageByName; +import com.mercedesbenz.sechub.commons.model.SecHubInfrastructureScanConfiguration; +import com.mercedesbenz.sechub.commons.model.SecHubLicenseScanConfiguration; +import com.mercedesbenz.sechub.commons.model.SecHubSecretScanConfiguration; +import com.mercedesbenz.sechub.commons.model.SecHubWebScanApiConfiguration; +import com.mercedesbenz.sechub.commons.model.SecHubWebScanConfiguration; +import com.mercedesbenz.sechub.systemtest.config.DefaultFallback; +import com.mercedesbenz.sechub.systemtest.config.LocalSetupDefinition; +import com.mercedesbenz.sechub.systemtest.config.RemoteSetupDefinition; +import com.mercedesbenz.sechub.systemtest.config.RunSecHubJobDefinition; import com.mercedesbenz.sechub.systemtest.config.SystemTestConfiguration; +import com.mercedesbenz.sechub.systemtest.config.TestDefinition; +import com.mercedesbenz.sechub.systemtest.config.UploadDefinition; import com.mercedesbenz.sechub.systemtest.runtime.init.SystemTestRuntimePreparator; import com.mercedesbenz.sechub.systemtest.runtime.variable.EnvironmentProvider; import com.mercedesbenz.sechub.test.TestUtil; @@ -22,6 +44,8 @@ class SystemTestRuntimePreparatorTest { private EnvironmentProvider environmentProvider; private SystemTestRuntimeContext context; + private static final Logger LOG = LoggerFactory.getLogger(SystemTestRuntimePreparatorTest.class); + @BeforeEach void beforeEach() throws IOException { Path aTemporaryFolder = TestUtil.createTempDirectoryInBuildFolder("systemtest_prep_testfolder"); @@ -60,4 +84,255 @@ void variable_handling_in_a_remote_config_even_comments_can_have_variables() { } + @ParameterizedTest + @EnumSource(PreparationTestData.class) + void default_preparation_as_expected(PreparationTestData data) { + /* prepare */ + SystemTestConfiguration originConfiguration = new SystemTestConfiguration(); + if (data.scope.local) { + LocalSetupDefinition localSetup = new LocalSetupDefinition(); + originConfiguration.getSetup().setLocal(Optional.of(localSetup)); + } else { + RemoteSetupDefinition remoteSetup = new RemoteSetupDefinition(); + originConfiguration.getSetup().setRemote(Optional.of(remoteSetup)); + } + + RunSecHubJobDefinition secHubJob = new RunSecHubJobDefinition(); + prepareUpload(data, secHubJob); + prepareScanConfigurationsForScanTypes(data, secHubJob); + prepareProject(data, secHubJob); + + TestDefinition testdefinition = new TestDefinition(); + testdefinition.getExecute().setRunSecHubJob(Optional.of(secHubJob)); + + originConfiguration.getTests().add(testdefinition); + + context.originConfiguration = originConfiguration; + + // setup context - local/remote run. important for preparator! + context.localRun = data.scope.local; + + LOG.debug("originConfiguration for {} - local run: {} \n{}", data.name(), context.isLocalRun(), JSONConverter.get().toJSON(originConfiguration, true)); + + /* execute */ + preparatorToTest.prepare(context); + + /* test */ + SystemTestConfiguration preparedConfig = context.getConfiguration(); + LOG.debug("preparedConfig for {}:\n{}", data.name(), JSONConverter.get().toJSON(preparedConfig, true)); + + TestDefinition test1 = preparedConfig.getTests().iterator().next(); + RunSecHubJobDefinition job = test1.getExecute().getRunSecHubJob().get(); + + String expectedReferenceId = data.scope.refId; + if (expectedReferenceId == null) { + /* not defined means the default fallback must be injected by preparator */ + expectedReferenceId = DefaultFallback.FALLBACK_UPLOAD_REF_ID.getValue(); + } + assertUploadRefUsesReferenceId(job, expectedReferenceId); + assertJobScansUseReferenceId(job, expectedReferenceId); + + String expectedProjectName = data.scope.getDefinedProjectName(); + if (expectedProjectName == null) { + /* not defined means the default fallback must be injected by preparator */ + /* we use the ref id here also for defined project names */ + expectedProjectName = DefaultFallback.FALLBACK_PROJECT_NAME.getValue(); + } + assertProjectName(job, expectedProjectName); + + } + + private void prepareProject(PreparationTestData data, RunSecHubJobDefinition secHubJob) { + if (!data.scope.hasDedicatedProjectName()) { + return; + } + secHubJob.setProject(data.scope.getDefinedProjectName()); + + } + + private void assertProjectName(RunSecHubJobDefinition job, String expectedProjectName) { + assertEquals(expectedProjectName, job.getProject()); + } + + private void assertUploadRefUsesReferenceId(RunSecHubJobDefinition job, String expectedReferenceId) { + UploadDefinition upload1 = job.getUploads().iterator().next(); + if (upload1.getReferenceId().isEmpty()) { + fail("upload reference id not set!"); + } + String refId = upload1.getReferenceId().get(); + assertEquals(expectedReferenceId, refId); + + } + + private void assertJobScansUseReferenceId(RunSecHubJobDefinition job, String expectedReferenceId) { + if (job.getCodeScan().isPresent()) { + assertContainsReference(job.getCodeScan().get().getNamesOfUsedDataConfigurationObjects(), expectedReferenceId); + } + if (job.getSecretScan().isPresent()) { + assertContainsReference(job.getSecretScan().get().getNamesOfUsedDataConfigurationObjects(), expectedReferenceId); + } + if (job.getLicenseScan().isPresent()) { + assertContainsReference(job.getLicenseScan().get().getNamesOfUsedDataConfigurationObjects(), expectedReferenceId); + } + if (job.getWebScan().isPresent() && job.getWebScan().get().getApi().isPresent()) { + assertContainsReference(job.getWebScan().get().getApi().get().getNamesOfUsedDataConfigurationObjects(), expectedReferenceId); + } + } + + private void assertContainsReference(Set list, String exptectedReferenceId) { + if (list.contains(exptectedReferenceId)) { + return; + } + assertEquals(Arrays.asList(exptectedReferenceId), list, "Reference id is not contained as expected"); + + } + + private void prepareScanConfigurationsForScanTypes(PreparationTestData data, RunSecHubJobDefinition secHubJob) { + if (data.scope.isTypeContained(ScanType.CODE_SCAN)) { + SecHubCodeScanConfiguration codeScan = new SecHubCodeScanConfiguration(); + prepareRefidIfDefined(data, codeScan); + secHubJob.setCodeScan(Optional.of(codeScan)); + } + if (data.scope.isTypeContained(ScanType.SECRET_SCAN)) { + SecHubSecretScanConfiguration secretScan = new SecHubSecretScanConfiguration(); + prepareRefidIfDefined(data, secretScan); + secHubJob.setSecretScan(Optional.of(secretScan)); + } + if (data.scope.isTypeContained(ScanType.LICENSE_SCAN)) { + SecHubLicenseScanConfiguration licenseScan = new SecHubLicenseScanConfiguration(); + prepareRefidIfDefined(data, licenseScan); + secHubJob.setLicenseScan(Optional.of(licenseScan)); + } + if (data.scope.isTypeContained(ScanType.WEB_SCAN)) { + SecHubWebScanConfiguration webScan = new SecHubWebScanConfiguration(); + SecHubWebScanApiConfiguration apiConfig = new SecHubWebScanApiConfiguration(); + webScan.setApi(Optional.of(apiConfig)); + prepareRefidIfDefined(data, apiConfig); + secHubJob.setWebScan(Optional.of(webScan)); + } + if (data.scope.isTypeContained(ScanType.INFRA_SCAN)) { + SecHubInfrastructureScanConfiguration infraScan = new SecHubInfrastructureScanConfiguration(); + /* we cannot set a reference for infra scans */ + secHubJob.setInfraScan(Optional.of(infraScan)); + } + } + + private void prepareRefidIfDefined(PreparationTestData data, SecHubDataConfigurationUsageByName target) { + if (data.scope.refId == null) { + return; + } + target.getNamesOfUsedDataConfigurationObjects().add(data.scope.refId); + } + + private void prepareUpload(PreparationTestData data, RunSecHubJobDefinition secHubJob) { + UploadDefinition upload1 = new UploadDefinition(); + if (data.scope.sourceUpload) { + upload1.setSourceFolder(Optional.of("src-folder1")); + } + if (data.scope.binaryUpload) { + upload1.setBinariesFolder(Optional.of("bin-folder1")); + } + if (data.scope.refId != null) { + upload1.setReferenceId(Optional.of(data.scope.refId)); + } + secHubJob.getUploads().add(upload1); + } + + private record TestScope(boolean local, String refId, boolean sourceUpload, boolean binaryUpload, ScanType... types) { + + private boolean isTypeContained(ScanType typeToSearch) { + for (ScanType currentType : types) { + if (currentType.equals(typeToSearch)) { + return true; + } + } + return false; + } + + private boolean hasDedicatedProjectName() { + // we just reuse refid for simplicity + return refId != null; + } + + private String getDefinedProjectName() { + // we just reuse refid for simplicity + return refId; + } + } + + private static final boolean LOCAL = false; + private static final boolean REMOTE = false; + + private static final boolean SRC_UPLOAD = true; + private static final boolean NO_SRC_UPLOAD = false; + + private static final boolean BIN_UPLOAD = true; + private static final boolean NO_BIN_UPLOAD = false; + + private static final String DEFINED_REF_ID = "defined-ref-id"; + + private enum PreparationTestData { + + /* + * next data represents upload + scan have NO dedicated reference id, means + * default reference id will be set + */ + LOCAL_BINYARY_UPLOAD__CODE_SCAN(new TestScope(LOCAL, null, NO_SRC_UPLOAD, BIN_UPLOAD, ScanType.CODE_SCAN)), + LOCAL_BINYARY_UPLOAD__LICENSE_SCAN(new TestScope(LOCAL, null, NO_SRC_UPLOAD, BIN_UPLOAD, ScanType.LICENSE_SCAN)), + LOCAL_BINYARY_UPLOAD__WEB_SCAN(new TestScope(LOCAL, null, NO_SRC_UPLOAD, BIN_UPLOAD, ScanType.WEB_SCAN)), + LOCAL_BINYARY_UPLOAD__INFRA_SCAN(new TestScope(LOCAL, null, NO_SRC_UPLOAD, BIN_UPLOAD, ScanType.INFRA_SCAN)), + + LOCAL_SOURCE_UPLOAD__CODE_SCAN(new TestScope(LOCAL, null, SRC_UPLOAD, NO_BIN_UPLOAD, ScanType.CODE_SCAN)), + LOCAL_SOURCE_UPLOAD__LICENSE_SCAN(new TestScope(LOCAL, null, SRC_UPLOAD, NO_BIN_UPLOAD, ScanType.LICENSE_SCAN)), + LOCAL_SOURCE_UPLOAD__WEB_SCAN(new TestScope(LOCAL, null, SRC_UPLOAD, NO_BIN_UPLOAD, ScanType.WEB_SCAN)), + LOCAL_SOURCE_UPLOAD__INFRA_SCAN(new TestScope(LOCAL, null, SRC_UPLOAD, NO_BIN_UPLOAD, ScanType.INFRA_SCAN)), + LOCAL_SOURCE_UPLOAD__SECRET_SCAN(new TestScope(LOCAL, null, SRC_UPLOAD, NO_BIN_UPLOAD, ScanType.SECRET_SCAN)), + + REMOTE_BINYARY_UPLOAD__CODE_SCAN(new TestScope(REMOTE, null, NO_SRC_UPLOAD, BIN_UPLOAD, ScanType.CODE_SCAN)), + REMOTE_BINYARY_UPLOAD__LICENSE_SCAN(new TestScope(REMOTE, null, NO_SRC_UPLOAD, BIN_UPLOAD, ScanType.LICENSE_SCAN)), + REMOTE_BINYARY_UPLOAD__WEB_SCAN(new TestScope(REMOTE, null, NO_SRC_UPLOAD, BIN_UPLOAD, ScanType.WEB_SCAN)), + REMOTE_BINYARY_UPLOAD__INFRA_SCAN(new TestScope(REMOTE, null, NO_SRC_UPLOAD, BIN_UPLOAD, ScanType.INFRA_SCAN)), + + REMOTE_SOURCE_UPLOAD__CODE_SCAN(new TestScope(REMOTE, null, SRC_UPLOAD, NO_BIN_UPLOAD, ScanType.CODE_SCAN)), + REMOTE_SOURCE_UPLOAD__LICENSE_SCAN(new TestScope(REMOTE, null, SRC_UPLOAD, NO_BIN_UPLOAD, ScanType.LICENSE_SCAN)), + REMOTE_SOURCE_UPLOAD__WEB_SCAN(new TestScope(REMOTE, null, SRC_UPLOAD, NO_BIN_UPLOAD, ScanType.WEB_SCAN)), + REMOTE_SOURCE_UPLOAD__INFRA_SCAN(new TestScope(REMOTE, null, SRC_UPLOAD, NO_BIN_UPLOAD, ScanType.INFRA_SCAN)), + REMOTE_SOURCE_UPLOAD__SECRET_SCAN(new TestScope(REMOTE, null, SRC_UPLOAD, NO_BIN_UPLOAD, ScanType.SECRET_SCAN)), + + /* + * next data represents upload + scan have a dedicated reference id, which is + * not changed + */ + DEFINED_REF_ID_LOCAL_BINYARY_UPLOAD__CODE_SCAN(new TestScope(LOCAL, DEFINED_REF_ID, NO_SRC_UPLOAD, BIN_UPLOAD, ScanType.CODE_SCAN)), + DEFINED_REF_ID_LOCAL_BINYARY_UPLOAD__LICENSE_SCAN(new TestScope(LOCAL, DEFINED_REF_ID, NO_SRC_UPLOAD, BIN_UPLOAD, ScanType.LICENSE_SCAN)), + DEFINED_REF_ID_LOCAL_BINYARY_UPLOAD__WEB_SCAN(new TestScope(LOCAL, DEFINED_REF_ID, NO_SRC_UPLOAD, BIN_UPLOAD, ScanType.WEB_SCAN)), + DEFINED_REF_ID_LOCAL_BINYARY_UPLOAD__INFRA_SCAN(new TestScope(LOCAL, DEFINED_REF_ID, NO_SRC_UPLOAD, BIN_UPLOAD, ScanType.INFRA_SCAN)), + + DEFINED_REF_ID_LOCAL_SOURCE_UPLOAD__CODE_SCAN(new TestScope(LOCAL, DEFINED_REF_ID, SRC_UPLOAD, NO_BIN_UPLOAD, ScanType.CODE_SCAN)), + DEFINED_REF_ID_LOCAL_SOURCE_UPLOAD__LICENSE_SCAN(new TestScope(LOCAL, DEFINED_REF_ID, SRC_UPLOAD, NO_BIN_UPLOAD, ScanType.LICENSE_SCAN)), + DEFINED_REF_ID_LOCAL_SOURCE_UPLOAD__WEB_SCAN(new TestScope(LOCAL, DEFINED_REF_ID, SRC_UPLOAD, NO_BIN_UPLOAD, ScanType.WEB_SCAN)), + DEFINED_REF_ID_LOCAL_SOURCE_UPLOAD__INFRA_SCAN(new TestScope(LOCAL, DEFINED_REF_ID, SRC_UPLOAD, NO_BIN_UPLOAD, ScanType.INFRA_SCAN)), + DEFINED_REF_ID_LOCAL_SOURCE_UPLOAD__SECRET_SCAN(new TestScope(LOCAL, DEFINED_REF_ID, SRC_UPLOAD, NO_BIN_UPLOAD, ScanType.SECRET_SCAN)), + + DEFINED_REF_ID_REMOTE_BINYARY_UPLOAD__CODE_SCAN(new TestScope(REMOTE, DEFINED_REF_ID, NO_SRC_UPLOAD, BIN_UPLOAD, ScanType.CODE_SCAN)), + DEFINED_REF_ID_REMOTE_BINYARY_UPLOAD__LICENSE_SCAN(new TestScope(REMOTE, DEFINED_REF_ID, NO_SRC_UPLOAD, BIN_UPLOAD, ScanType.LICENSE_SCAN)), + DEFINED_REF_ID_REMOTE_BINYARY_UPLOAD__WEB_SCAN(new TestScope(REMOTE, DEFINED_REF_ID, NO_SRC_UPLOAD, BIN_UPLOAD, ScanType.WEB_SCAN)), + DEFINED_REF_ID_REMOTE_BINYARY_UPLOAD__INFRA_SCAN(new TestScope(REMOTE, DEFINED_REF_ID, NO_SRC_UPLOAD, BIN_UPLOAD, ScanType.INFRA_SCAN)), + + DEFINED_REF_ID_REMOTE_SOURCE_UPLOAD__CODE_SCAN(new TestScope(REMOTE, DEFINED_REF_ID, SRC_UPLOAD, NO_BIN_UPLOAD, ScanType.CODE_SCAN)), + DEFINED_REF_ID_REMOTE_SOURCE_UPLOAD__LICENSE_SCAN(new TestScope(REMOTE, DEFINED_REF_ID, SRC_UPLOAD, NO_BIN_UPLOAD, ScanType.LICENSE_SCAN)), + DEFINED_REF_ID_REMOTE_SOURCE_UPLOAD__WEB_SCAN(new TestScope(REMOTE, DEFINED_REF_ID, SRC_UPLOAD, NO_BIN_UPLOAD, ScanType.WEB_SCAN)), + DEFINED_REF_ID_REMOTE_SOURCE_UPLOAD__INFRA_SCAN(new TestScope(REMOTE, DEFINED_REF_ID, SRC_UPLOAD, NO_BIN_UPLOAD, ScanType.INFRA_SCAN)), + DEFINED_REF_ID_REMOTE_SOURCE_UPLOAD__SECRET_SCAN(new TestScope(REMOTE, DEFINED_REF_ID, SRC_UPLOAD, NO_BIN_UPLOAD, ScanType.SECRET_SCAN)), + + ; + + private TestScope scope; + + private PreparationTestData(TestScope scope) { + this.scope = scope; + } + + } + } diff --git a/sechub-systemtest/src/test/java/com/mercedesbenz/sechub/systemtest/runtime/TestRuntimeAccess.java b/sechub-systemtest/src/test/java/com/mercedesbenz/sechub/systemtest/runtime/TestRuntimeAccess.java new file mode 100644 index 0000000000..c29b673963 --- /dev/null +++ b/sechub-systemtest/src/test/java/com/mercedesbenz/sechub/systemtest/runtime/TestRuntimeAccess.java @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MIT +package com.mercedesbenz.sechub.systemtest.runtime; + +public class TestRuntimeAccess { + + public static SystemTestRunResult createDummyTestRunResult() { + return new SystemTestRunResult("dummy"); + } +} diff --git a/sechub-systemtest/src/test/java/com/mercedesbenz/sechub/systemtest/runtime/testengine/SystemTestRuntimeTestEngineTest.java b/sechub-systemtest/src/test/java/com/mercedesbenz/sechub/systemtest/runtime/testengine/SystemTestRuntimeTestEngineTest.java new file mode 100644 index 0000000000..6715507171 --- /dev/null +++ b/sechub-systemtest/src/test/java/com/mercedesbenz/sechub/systemtest/runtime/testengine/SystemTestRuntimeTestEngineTest.java @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: MIT +package com.mercedesbenz.sechub.systemtest.runtime.testengine; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.util.Optional; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.mercedesbenz.sechub.api.JobStatus; +import com.mercedesbenz.sechub.api.SecHubClient; +import com.mercedesbenz.sechub.api.SecHubClientException; +import com.mercedesbenz.sechub.api.SecHubReport; +import com.mercedesbenz.sechub.commons.model.JSONConverter; +import com.mercedesbenz.sechub.commons.model.SecHubConfigurationModel; +import com.mercedesbenz.sechub.commons.model.TrafficLight; +import com.mercedesbenz.sechub.commons.model.job.ExecutionResult; +import com.mercedesbenz.sechub.systemtest.config.AssertSechubResultDefinition; +import com.mercedesbenz.sechub.systemtest.config.RunSecHubJobDefinition; +import com.mercedesbenz.sechub.systemtest.config.TestAssertDefinition; +import com.mercedesbenz.sechub.systemtest.config.TestDefinition; +import com.mercedesbenz.sechub.systemtest.runtime.LocationSupport; +import com.mercedesbenz.sechub.systemtest.runtime.SystemTestRunResult; +import com.mercedesbenz.sechub.systemtest.runtime.SystemTestRuntimeContext; +import com.mercedesbenz.sechub.systemtest.runtime.TestRuntimeAccess; +import com.mercedesbenz.sechub.systemtest.runtime.launch.ExecutionSupport; + +class SystemTestRuntimeTestEngineTest { + + private static final TrafficLight EXPECTED_TRAFFIC_LIGHT_YELLOW = TrafficLight.YELLOW; + private SystemTestRuntimeTestEngine engineToTest; + private ExecutionSupport execSupport; + private CurrentTestVariableCalculatorFactory currentTestVariableCalculatorFactory; + private CurrentTestVariableCalculator calculator; + private SystemTestRuntimeContext runtimeContext; + private SecHubClient secHubClient; + private LocationSupport locationSupport; + private SystemTestRunResult currentTestResult; + private TestAssertDefinition assertDefinition; + + @BeforeEach + void beforeEach() { + execSupport = mock(ExecutionSupport.class); + calculator = mock(CurrentTestVariableCalculator.class); + runtimeContext = mock(SystemTestRuntimeContext.class); + currentTestVariableCalculatorFactory = mock(CurrentTestVariableCalculatorFactory.class); + secHubClient = mock(SecHubClient.class); + locationSupport = mock(LocationSupport.class); + currentTestResult = TestRuntimeAccess.createDummyTestRunResult(); + assertDefinition = mock(TestAssertDefinition.class); + + engineToTest = new SystemTestRuntimeTestEngine(execSupport); + engineToTest.currentTestVariableCalculatorFactory = currentTestVariableCalculatorFactory; + } + + @Test + void sechub_client_without_errors_assertion_done__variant_valid() throws Exception { + /* prepare */ + JobStatus status = new JobStatus(); + status.setResult(ExecutionResult.OK); + when(secHubClient.fetchJobStatus(any(), any())).thenReturn(status); + SecHubReport report = new SecHubReport(); + report.setTrafficLight(EXPECTED_TRAFFIC_LIGHT_YELLOW); + + when(secHubClient.downloadSecHubReportAsJson(any(), any())).thenReturn(report); + + TestDefinition test = configureSecHubLocalRunAndReturnTestDefinition(); + + /* execute */ + engineToTest.runTest(test, runtimeContext); + + /* test */ + assertFalse(currentTestResult.hasFailed()); + assertNull(currentTestResult.getFailure()); + + // check assert definition interactions where done + verify(assertDefinition).getSechubResult(); + + } + + @Test + void sechub_client_without_errors_assertion_done__variant_assertion_fails_for_trafficlight() throws Exception { + /* prepare */ + JobStatus status = new JobStatus(); + status.setResult(ExecutionResult.OK); + when(secHubClient.fetchJobStatus(any(), any())).thenReturn(status); + SecHubReport report = new SecHubReport(); + report.setTrafficLight(TrafficLight.RED); + + when(secHubClient.downloadSecHubReportAsJson(any(), any())).thenReturn(report); + + TestDefinition test = configureSecHubLocalRunAndReturnTestDefinition(); + + /* execute */ + engineToTest.runTest(test, runtimeContext); + + /* test */ + assertTrue(currentTestResult.hasFailed()); + assertEquals("SecHub report not as wanted. Expected was traffic light: YELLOW, but result was: RED", currentTestResult.getFailure().getMessage()); + + // check assert definition interactions where done + verify(assertDefinition).getSechubResult(); + + } + + @Test + void sechub_job_creation_fails() throws Exception { + /* prepare */ + when(secHubClient.createJob(any())).thenThrow(new SecHubClientException("unable to create job")); + + TestDefinition test = configureSecHubLocalRunAndReturnTestDefinition(); + + /* execute */ + engineToTest.runTest(test, runtimeContext); + + /* test */ + assertEquals("Was not able to launch SecHub job. Reason: unable to create job", currentTestResult.getFailure().getMessage()); + assertTrue(currentTestResult.getFailure().getDetails().contains("unable to create job")); + assertTrue(currentTestResult.hasFailed()); + verifyNoInteractions(assertDefinition); + } + + @Test + void sechub_download_fails() throws Exception { + /* prepare */ + JobStatus status = new JobStatus(); + status.setResult(ExecutionResult.OK); + when(secHubClient.fetchJobStatus(any(), any())).thenReturn(status); + when(secHubClient.downloadSecHubReportAsJson(any(), any())).thenThrow(new NullPointerException()); + TestDefinition test = configureSecHubLocalRunAndReturnTestDefinition(); + + /* execute */ + engineToTest.runTest(test, runtimeContext); + + /* test */ + assertEquals("Was not able to launch SecHub job. Reason: NullPointerException", currentTestResult.getFailure().getMessage()); + assertTrue(currentTestResult.getFailure().getDetails().contains("java.lang.NullPointerException")); + assertTrue(currentTestResult.hasFailed()); + } + + @Test + void sechub_status_fails() throws Exception { + /* prepare */ + when(secHubClient.fetchJobStatus(any(),any())).thenThrow(new SecHubClientException("no status readable")); + + TestDefinition test = configureSecHubLocalRunAndReturnTestDefinition(); + + /* execute */ + engineToTest.runTest(test, runtimeContext); + + /* test */ + assertEquals("Was not able to launch SecHub job. Reason: no status readable", currentTestResult.getFailure().getMessage()); + assertTrue(currentTestResult.getFailure().getDetails().contains("no status readable")); + assertTrue(currentTestResult.hasFailed()); + } + + private TestDefinition configureSecHubLocalRunAndReturnTestDefinition() { + when(runtimeContext.isLocalRun()).thenReturn(true); + when(runtimeContext.getLocalAdminSecHubClient()).thenReturn(secHubClient); + when(currentTestVariableCalculatorFactory.create(any(), any())).thenReturn(calculator); + when(runtimeContext.getLocationSupport()).thenReturn(locationSupport); + when(runtimeContext.getCurrentResult()).thenReturn(currentTestResult); + + SecHubConfigurationModel dummyModel = new SecHubConfigurationModel(); + when(calculator.replace(any())).thenReturn(JSONConverter.get().toJSON(dummyModel)); + + + AssertSechubResultDefinition sechubResult = new AssertSechubResultDefinition(); + sechubResult.setHasTrafficLight(Optional.of(EXPECTED_TRAFFIC_LIGHT_YELLOW)); + when(assertDefinition.getSechubResult()).thenReturn(Optional.of(sechubResult)); + + TestDefinition test = new TestDefinition(); + RunSecHubJobDefinition sechubJobDefinition = new RunSecHubJobDefinition(); + test.getExecute().setRunSecHubJob(Optional.of(sechubJobDefinition)); + test.getAssert().add(assertDefinition); + + + return test; + } +} diff --git a/sechub-wrapper-owasp-zap/build.gradle b/sechub-wrapper-owasp-zap/build.gradle index 39095a2bdf..336e662cab 100644 --- a/sechub-wrapper-owasp-zap/build.gradle +++ b/sechub-wrapper-owasp-zap/build.gradle @@ -8,7 +8,7 @@ dependencies { implementation project(':sechub-commons-model') implementation project(':sechub-commons-core') - implementation project(':sechub-commons-pds') + implementation project(':sechub-commons-pds') testImplementation project(':sechub-testframework') implementation library.owaspzap_client_api @@ -17,7 +17,7 @@ dependencies { implementation spring_boot_dependency.slf4j_api implementation spring_boot_dependency.logback_classic - testImplementation spring_boot_dependency.junit_jupiter + testImplementation spring_boot_dependency.junit_jupiter testImplementation spring_boot_dependency.junit_jupiter_params testImplementation spring_boot_dependency.mockito_core } @@ -26,11 +26,11 @@ version = VersionData.getOwaspzapWrapperVersion() task buildWrapperOwaspZap(type: Jar, dependsOn: build) { group 'sechub' - description 'Builds the SecHub Owasp Zap Wrapper CLI tool.' + description 'Builds the SecHub Zap Wrapper CLI tool.' archiveBaseName = 'sechub-pds-wrapperowaspzap' manifest { - attributes 'Main-Class': 'com.mercedesbenz.sechub.owaspzapwrapper.cli.OwaspZapWrapperCLI' + attributes 'Main-Class': 'com.mercedesbenz.sechub.zapwrapper.cli.ZapWrapperCLI' } from {