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 60db4e7a89..b8916e0520 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 @@ -66,7 +66,17 @@ public enum SecHubConfigurationModelValidationError { METADATA_LABEL_KEY_CONTAINS_ILLEGAL_CHARACTERS("Meta data label key contains illegal characters."), - ; + REMOTE_DATA_CONFIGURATION_ONLY_FOR_ONE_SOURCE_OR_BINARY("Remote data configuration is only allowed for ONE source or for ONE binary."), + + REMOTE_DATA_MIXED_WITH_FILESYSTEM_NOT_ALLOWED("Remote data configuration is not allowed to be mixed with filesystem."), + + REMOTE_DATA_CONFIGURATION_LOCATION_NOT_DEFINED("Remote data configuration location is not defined."), + + REMOTE_DATA_CONFIGURATION_USER_NOT_DEFINED("Remote data configuration credentials: no user is defined."), + + REMOTE_DATA_CONFIGURATION_USER_NAME_NOT_DEFINED("Remote data configuration credentials: user name is not defined."), + + REMOTE_DATA_CONFIGURATION_USER_PASSWORD_NOT_DEFINED("Remote data configuration credentials: user password is not defined."); private String defaultMessage; 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 b75ffb8801..4a2570d1ea 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 @@ -217,7 +217,7 @@ private void handleScanConfigurations(InternalValidationContext context) { private void handleLicenseScanConfiguration(InternalValidationContext context) { Optional licenseScanOpt = context.model.getLicenseScan(); - if (!licenseScanOpt.isPresent()) { + if (licenseScanOpt.isEmpty()) { return; } SecHubDataConfigurationUsageByName licenseScan = licenseScanOpt.get(); @@ -232,7 +232,7 @@ private void handleLicenseScanConfiguration(InternalValidationContext context) { private void handleSecretScanConfiguration(InternalValidationContext context) { Optional secretScanOpt = context.model.getSecretScan(); - if (!secretScanOpt.isPresent()) { + if (secretScanOpt.isEmpty()) { return; } SecHubDataConfigurationUsageByName secretScan = secretScanOpt.get(); @@ -246,7 +246,7 @@ private void handleSecretScanConfiguration(InternalValidationContext context) { private void handleCodeScanConfiguration(InternalValidationContext context) { Optional codeScanOpt = context.model.getCodeScan(); - if (!codeScanOpt.isPresent()) { + if (codeScanOpt.isEmpty()) { return; } SecHubDataConfigurationUsageByName codeScan = codeScanOpt.get(); @@ -266,7 +266,7 @@ private void handleUsages(InternalValidationContext context, SecHubDataConfigura private void handleWebScanConfiguration(InternalValidationContext context) { Optional webScanOpt = context.model.getWebScan(); - if (!webScanOpt.isPresent()) { + if (webScanOpt.isEmpty()) { return; } @@ -308,7 +308,7 @@ private void handleIncludesAndExcludes(InternalValidationContext context, SecHub private void handleApi(InternalValidationContext context, SecHubWebScanConfiguration webScan) { Optional apiOpt = webScan.getApi(); - if (!apiOpt.isPresent()) { + if (apiOpt.isEmpty()) { return; } @@ -318,7 +318,7 @@ private void handleApi(InternalValidationContext context, SecHubWebScanConfigura private void handleHTTPHeaders(InternalValidationContext context, SecHubWebScanConfiguration webScan) { Optional> optHttpHeaders = webScan.getHeaders(); - if (!optHttpHeaders.isPresent()) { + if (optHttpHeaders.isEmpty()) { return; } String targetUrl = webScan.getUrl().toString(); @@ -495,7 +495,7 @@ private String createUrlWithoutWildCards(String url) { private void handleInfraScanConfiguration(InternalValidationContext context) { Optional infraScanOpt = context.model.getInfraScan(); - if (!infraScanOpt.isPresent()) { + if (infraScanOpt.isEmpty()) { return; } SecHubInfrastructureScanConfiguration infraScan = infraScanOpt.get(); @@ -507,7 +507,7 @@ private void handleInfraScanConfiguration(InternalValidationContext context) { private void handleDataConfiguration(InternalValidationContext context) { Optional dataOpt = context.model.getData(); - if (!dataOpt.isPresent()) { + if (dataOpt.isEmpty()) { return; } @@ -516,6 +516,11 @@ private void handleDataConfiguration(InternalValidationContext context) { validateNameUniqueAndNotNull(context, data.getSources()); validateNameUniqueAndNotNull(context, data.getBinaries()); + List sourcesAndBinaries = new ArrayList(); + sourcesAndBinaries.addAll(data.getSources()); + sourcesAndBinaries.addAll(data.getBinaries()); + + validateRemoteDataConfiguration(context, sourcesAndBinaries); } private void validateNameUniqueAndNotNull(InternalValidationContext context, Collection configurationObjects) { @@ -552,6 +557,98 @@ private void validateNameUniqueAndNotNull(InternalValidationContext context, Col } + private void validateRemoteDataConfiguration(InternalValidationContext context, Collection sourcesAndBinaries) { + + SecHubConfigurationModelValidationResult result = context.result; + + validateOnlyOneRemoteSourceOrBinary(sourcesAndBinaries, result); + validateRemoteAndFileSystemAreNotMixed(sourcesAndBinaries, result); + validate(sourcesAndBinaries, result); + } + + private void validateOnlyOneRemoteSourceOrBinary(Collection sourcesAndBinaries, + SecHubConfigurationModelValidationResult result) { + for (SecHubDataConfigurationObject sourceOrBinary : sourcesAndBinaries) { + Optional optRemoteData = sourceOrBinary.getRemote(); + + if (optRemoteData.isEmpty()) { + // no remote data is configured + continue; + } + + // When using a remote data section it is only possible to define ONE binary or + // ONE source definition. + // Means also: It is only possible to define ONE remote data section. + boolean onlyOneBinaryOrOneSource = sourcesAndBinaries.size() == 1; + if (!onlyOneBinaryOrOneSource) { + result.addError(REMOTE_DATA_CONFIGURATION_ONLY_FOR_ONE_SOURCE_OR_BINARY); + break; + } + } + } + + private void validateRemoteAndFileSystemAreNotMixed(Collection sourcesAndBinaries, + SecHubConfigurationModelValidationResult result) { + boolean containsFileSystem = false; + boolean containsRemote = false; + + for (SecHubDataConfigurationObject sourceOrBinary : sourcesAndBinaries) { + containsRemote = containsRemote || sourceOrBinary.getRemote().isPresent(); + if (sourceOrBinary instanceof SecHubFileSystemContainer) { + containsFileSystem = containsFileSystem || ((SecHubFileSystemContainer) sourceOrBinary).getFileSystem().isPresent(); + } + } + if (containsFileSystem && containsRemote) { + result.addError(REMOTE_DATA_MIXED_WITH_FILESYSTEM_NOT_ALLOWED); + } + } + + private void validate(Collection sourcesAndBinaries, SecHubConfigurationModelValidationResult result) { + for (SecHubDataConfigurationObject sourceOrBinary : sourcesAndBinaries) { + Optional optRemoteData = sourceOrBinary.getRemote(); + + if (optRemoteData.isEmpty()) { + // no remote data is configured + continue; + } + + String uniqueName = sourceOrBinary.getUniqueName(); + SecHubRemoteDataConfiguration remoteData = optRemoteData.get(); + + if (remoteData.getLocation() == null || remoteData.getLocation().isBlank()) { + result.addError(REMOTE_DATA_CONFIGURATION_LOCATION_NOT_DEFINED, "Remote data location is not defined for " + uniqueName); + } + + validateRemoteDataCredentials(result, remoteData, uniqueName); + } + } + + private void validateRemoteDataCredentials(SecHubConfigurationModelValidationResult result, SecHubRemoteDataConfiguration remoteData, String uniqueName) { + if (remoteData.getCredentials().isEmpty()) { + // credentials don't need to be defined for public accessible remote data + return; + } + SecHubRemoteCredentialConfiguration remoteCredential = remoteData.getCredentials().get(); + if (remoteCredential.getUser().isEmpty()) { + result.addError(REMOTE_DATA_CONFIGURATION_USER_NOT_DEFINED, "Remote data configuration credentials: no user is defined for " + uniqueName); + + } else { + SecHubRemoteCredentialUserData user = remoteCredential.getUser().get(); + + String name = user.getName(); + if (name == null || name.isBlank()) { + result.addError(REMOTE_DATA_CONFIGURATION_USER_NAME_NOT_DEFINED, + "Remote data configuration credentials: user name is not defined for " + uniqueName); + } + + String password = user.getPassword(); + if (password == null || password.isBlank()) { + result.addError(REMOTE_DATA_CONFIGURATION_USER_PASSWORD_NOT_DEFINED, + "Remote data configuration credentials: user password is not defined for " + uniqueName); + } + } + } + private boolean hasAtLeastOneScanConfiguration(InternalValidationContext context) { boolean atLeastOne = false; SecHubConfigurationModel model = context.model; diff --git a/sechub-commons-model/src/main/java/com/mercedesbenz/sechub/commons/model/SecHubDataConfigurationObject.java b/sechub-commons-model/src/main/java/com/mercedesbenz/sechub/commons/model/SecHubDataConfigurationObject.java index c374930e10..73394aa267 100644 --- a/sechub-commons-model/src/main/java/com/mercedesbenz/sechub/commons/model/SecHubDataConfigurationObject.java +++ b/sechub-commons-model/src/main/java/com/mercedesbenz/sechub/commons/model/SecHubDataConfigurationObject.java @@ -1,6 +1,8 @@ // SPDX-License-Identifier: MIT package com.mercedesbenz.sechub.commons.model; +import java.util.Optional; + import com.fasterxml.jackson.annotation.JsonProperty; public interface SecHubDataConfigurationObject { @@ -16,6 +18,8 @@ public interface SecHubDataConfigurationObject { * null */ @JsonProperty("name") - public String getUniqueName(); + String getUniqueName(); + + Optional getRemote(); } 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 67392e1391..5d832bd13f 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 @@ -1615,6 +1615,127 @@ void when_sechub_config_has_exactly_maximum_size_allowed_error_SECHUB_CONFIGURAT assertHasNotError(result, SECHUB_CONFIGURATION_TOO_LARGE); } + @ParameterizedTest + @ValueSource(strings = { "src/test/resources/sechub_remote_data_config_binary_code_scan_example.json", + "src/test/resources/sechub_remote_data_config_source_code_scan_example.json" }) + void when_remote_sechub_configuration_is_valid_no_errors_are_reported(String file) { + String json = TestFileReader.loadTextFile(file); + SecHubScanConfiguration sechubConfiguration = SecHubScanConfiguration.createFromJSON(json); + modelSupportCollectedScanTypes.add(ScanType.CODE_SCAN); + + /* execute */ + SecHubConfigurationModelValidationResult result = validatorToTest.validate(sechubConfiguration); + + /* test */ + assertHasNoErrors(result); + } + + @Test + void when_multiple_remote_configurations_are_configured_error_REMOTE_DATA_CONFIGURATION_ONLY_FOR_ONE_SOURCE_OR_BINARY() { + /* prepare */ + String json = TestFileReader.loadTextFile("src/test/resources/sechub_remote_data_config_invalid_multi_config.json"); + SecHubScanConfiguration sechubConfiguration = SecHubScanConfiguration.createFromJSON(json); + modelSupportCollectedScanTypes.add(ScanType.CODE_SCAN); + + /* execute */ + SecHubConfigurationModelValidationResult result = validatorToTest.validate(sechubConfiguration); + + /* test */ + assertTrue(result.hasErrors()); + assertHasError(result, REMOTE_DATA_CONFIGURATION_ONLY_FOR_ONE_SOURCE_OR_BINARY); + } + + @Test + void when_remote_configuration_location_is_not_defined_error_REMOTE_DATA_CONFIGURATION_LOCATION_NOT_DEFINED() { + /* prepare */ + String json = TestFileReader.loadTextFile("src/test/resources/sechub_remote_data_config_invalid_missing_location.json"); + SecHubScanConfiguration sechubConfiguration = SecHubScanConfiguration.createFromJSON(json); + modelSupportCollectedScanTypes.add(ScanType.CODE_SCAN); + + /* execute */ + SecHubConfigurationModelValidationResult result = validatorToTest.validate(sechubConfiguration); + + /* test */ + assertTrue(result.hasErrors()); + assertHasError(result, REMOTE_DATA_CONFIGURATION_LOCATION_NOT_DEFINED); + } + + @Test + void when_remote_configuration_has_empty_credentials_error_REMOTE_DATA_CONFIGURATION_USER_NOT_DEFINED() { + /* prepare */ + String json = TestFileReader.loadTextFile("src/test/resources/sechub_remote_data_config_invalid_credentials_empty.json"); + SecHubScanConfiguration sechubConfiguration = SecHubScanConfiguration.createFromJSON(json); + modelSupportCollectedScanTypes.add(ScanType.CODE_SCAN); + + /* execute */ + SecHubConfigurationModelValidationResult result = validatorToTest.validate(sechubConfiguration); + + /* test */ + assertTrue(result.hasErrors()); + assertHasError(result, REMOTE_DATA_CONFIGURATION_USER_NOT_DEFINED); + } + + @Test + void when_remote_configuration_credential_user_has_no_username_error_REMOTE_DATA_CONFIGURATION_USER_NAME_NOT_DEFINED() { + /* prepare */ + String json = TestFileReader.loadTextFile("src/test/resources/sechub_remote_data_config_invalid_user_credential_missing_username.json"); + SecHubScanConfiguration sechubConfiguration = SecHubScanConfiguration.createFromJSON(json); + modelSupportCollectedScanTypes.add(ScanType.CODE_SCAN); + + /* execute */ + SecHubConfigurationModelValidationResult result = validatorToTest.validate(sechubConfiguration); + + /* test */ + assertTrue(result.hasErrors()); + assertHasError(result, REMOTE_DATA_CONFIGURATION_USER_NAME_NOT_DEFINED); + } + + @Test + void when_remote_configuration_credential_user_has_no_password_error_REMOTE_DATA_CONFIGURATION_USER_PASSWORD_NOT_DEFINED() { + /* prepare */ + String json = TestFileReader.loadTextFile("src/test/resources/sechub_remote_data_config_invalid_user_credential_missing_password.json"); + SecHubScanConfiguration sechubConfiguration = SecHubScanConfiguration.createFromJSON(json); + modelSupportCollectedScanTypes.add(ScanType.CODE_SCAN); + + /* execute */ + SecHubConfigurationModelValidationResult result = validatorToTest.validate(sechubConfiguration); + + /* test */ + assertTrue(result.hasErrors()); + assertHasError(result, REMOTE_DATA_CONFIGURATION_USER_PASSWORD_NOT_DEFINED); + } + + @Test + void when_remote_configuration_is_mixed_with_filesystem_REMOTE_REMOTE_DATA_MIXED_WITH_FILESYSTEM_NOT_ALLOWED() { + /* prepare */ + String json = TestFileReader.loadTextFile("src/test/resources/sechub_remote_data_config_invalid_config_with_filesystem.json"); + SecHubScanConfiguration sechubConfiguration = SecHubScanConfiguration.createFromJSON(json); + modelSupportCollectedScanTypes.add(ScanType.CODE_SCAN); + + /* execute */ + SecHubConfigurationModelValidationResult result = validatorToTest.validate(sechubConfiguration); + + /* test */ + assertTrue(result.hasErrors()); + assertHasError(result, REMOTE_DATA_MIXED_WITH_FILESYSTEM_NOT_ALLOWED); + } + + @Test + void when_remote_data_is_configured_for_binaries_and_sources_error() { + /* prepare */ + String json = TestFileReader.loadTextFile("src/test/resources/sechub_remote_data_config_invalid_source_and_binaries.json"); + SecHubScanConfiguration sechubConfiguration = SecHubScanConfiguration.createFromJSON(json); + modelSupportCollectedScanTypes.add(ScanType.CODE_SCAN); + + /* execute */ + SecHubConfigurationModelValidationResult result = validatorToTest.validate(sechubConfiguration); + + /* test */ + assertTrue(result.hasErrors()); + assertHasError(result, REMOTE_DATA_CONFIGURATION_ONLY_FOR_ONE_SOURCE_OR_BINARY); + + } + private SecHubConfigurationModel createSecHubConfigModelWithExactly8193Characters() { // 128*64 = 8192, so we take 127 because of the overhead of the JSON model: // {"apiVersion":""} = 17 characters so we need to add 48 characters afterwards diff --git a/sechub-commons-model/src/test/resources/sechub_remote_data_config_invalid_config_with_filesystem.json b/sechub-commons-model/src/test/resources/sechub_remote_data_config_invalid_config_with_filesystem.json new file mode 100644 index 0000000000..b25589847a --- /dev/null +++ b/sechub-commons-model/src/test/resources/sechub_remote_data_config_invalid_config_with_filesystem.json @@ -0,0 +1,37 @@ +{ + "apiVersion": "1.0", + "data": { + "sources": [ + { + "name": "filesystem_example_1", + "fileSystem" : { + "folders" : [ "myProject/src" ] + } + }, + { + "name": "remote_example_name", + "remote": { + "location": "remote_example_location", + "type": "git", + "credentials": { + "user": { + "name": "my-example-user", + "password": "my-example-password" + } + } + } + }, + { + "name": "filesystem_example", + "fileSystem" : { + "folders" : [ "myProject/build" ] + } + } + ] + }, + "codeScan": { + "use": [ + "remote_example_name" + ] + } +} \ No newline at end of file diff --git a/sechub-commons-model/src/test/resources/sechub_remote_data_config_invalid_credentials_empty.json b/sechub-commons-model/src/test/resources/sechub_remote_data_config_invalid_credentials_empty.json new file mode 100644 index 0000000000..8251ca0908 --- /dev/null +++ b/sechub-commons-model/src/test/resources/sechub_remote_data_config_invalid_credentials_empty.json @@ -0,0 +1,21 @@ +{ + "apiVersion": "1.0", + "data": { + "sources": [ + { + "name": "remote_example_name", + "remote": { + "location": "remote_example_location", + "type": "git", + "credentials": { + } + } + } + ] + }, + "codeScan": { + "use": [ + "remote_example_name" + ] + } +} \ No newline at end of file diff --git a/sechub-commons-model/src/test/resources/sechub_remote_data_config_invalid_missing_location.json b/sechub-commons-model/src/test/resources/sechub_remote_data_config_invalid_missing_location.json new file mode 100644 index 0000000000..865f74e8b0 --- /dev/null +++ b/sechub-commons-model/src/test/resources/sechub_remote_data_config_invalid_missing_location.json @@ -0,0 +1,18 @@ +{ + "apiVersion": "1.0", + "data": { + "binaries": [ + { + "name": "remote_example_name", + "remote": { + "type": "docker" + } + } + ] + }, + "codeScan": { + "use": [ + "remote_example_name" + ] + } +} \ No newline at end of file diff --git a/sechub-commons-model/src/test/resources/sechub_remote_data_config_invalid_multi_config.json b/sechub-commons-model/src/test/resources/sechub_remote_data_config_invalid_multi_config.json new file mode 100644 index 0000000000..9ccee2e65e --- /dev/null +++ b/sechub-commons-model/src/test/resources/sechub_remote_data_config_invalid_multi_config.json @@ -0,0 +1,38 @@ +{ + "apiVersion": "1.0", + "data": { + "sources": [ + { + "name": "remote_example_name", + "remote": { + "location": "remote_example_location", + "type": "git", + "credentials": { + "user": { + "name": "my-example-user", + "password": "my-example-password" + } + } + } + }, + { + "name": "remote_example_name_two", + "remote": { + "location": "remote_example_location", + "type": "some-other-type", + "credentials": { + "user": { + "name": "my-example-user", + "password": "my-example-password" + } + } + } + } + ] + }, + "codeScan": { + "use": [ + "remote_example_name" + ] + } +} \ No newline at end of file diff --git a/sechub-commons-model/src/test/resources/sechub_remote_data_config_invalid_source_and_binaries.json b/sechub-commons-model/src/test/resources/sechub_remote_data_config_invalid_source_and_binaries.json new file mode 100644 index 0000000000..67d0d23811 --- /dev/null +++ b/sechub-commons-model/src/test/resources/sechub_remote_data_config_invalid_source_and_binaries.json @@ -0,0 +1,34 @@ +{ + "apiVersion": "1.0", + "data": { + "sources": [ + { + "name": "remote_example_name", + "remote": { + "location": "remote_example_location", + "type": "git", + "credentials": { + "user": { + "name": "my-example-user", + "password": "my-example-password" + } + } + } + } + ], + "binaries": [ + { + "name": "binary_example_name", + "remote": { + "location": "remote_example_location", + "type": "docker" + } + } + ] + }, + "codeScan": { + "use": [ + "remote_example_name", "binary_example_name" + ] + } +} \ No newline at end of file diff --git a/sechub-commons-model/src/test/resources/sechub_remote_data_config_invalid_user_credential_missing_password.json b/sechub-commons-model/src/test/resources/sechub_remote_data_config_invalid_user_credential_missing_password.json new file mode 100644 index 0000000000..ea548a2fca --- /dev/null +++ b/sechub-commons-model/src/test/resources/sechub_remote_data_config_invalid_user_credential_missing_password.json @@ -0,0 +1,24 @@ +{ + "apiVersion": "1.0", + "data": { + "binaries": [ + { + "name": "remote_example_name", + "remote": { + "location": "remote_example_location", + "type": "docker", + "credentials": { + "user": { + "name": "my-example-name" + } + } + } + } + ] + }, + "codeScan": { + "use": [ + "remote_example_name" + ] + } +} \ No newline at end of file diff --git a/sechub-commons-model/src/test/resources/sechub_remote_data_config_invalid_user_credential_missing_username.json b/sechub-commons-model/src/test/resources/sechub_remote_data_config_invalid_user_credential_missing_username.json new file mode 100644 index 0000000000..fdc2610e90 --- /dev/null +++ b/sechub-commons-model/src/test/resources/sechub_remote_data_config_invalid_user_credential_missing_username.json @@ -0,0 +1,24 @@ +{ + "apiVersion": "1.0", + "data": { + "binaries": [ + { + "name": "remote_example_name", + "remote": { + "location": "remote_example_location", + "type": "docker", + "credentials": { + "user": { + "password": "my-example-password" + } + } + } + } + ] + }, + "codeScan": { + "use": [ + "remote_example_name" + ] + } +} \ No newline at end of file diff --git a/sechub-developertools/scripts/sechub-api.sh b/sechub-developertools/scripts/sechub-api.sh index c379923567..88948b74fc 100755 --- a/sechub-developertools/scripts/sechub-api.sh +++ b/sechub-developertools/scripts/sechub-api.sh @@ -804,7 +804,7 @@ function sechub_user_list_open_signups { function sechub_user_reset_apitoken { - curl_with_sechub_auth -i -X POST -H 'Content-Type: application/json' "$SECHUB_SERVER/api/anonymous/refresh/apitoken/$1" | $CURL_FILTER + curl $CURL_PARAMS -i -X POST -H 'Content-Type: application/json' "$SECHUB_SERVER/api/anonymous/refresh/apitoken/$1" | $CURL_FILTER } @@ -819,7 +819,7 @@ EOF } function sechub_user_signup { - curl_with_sechub_auth -i -X POST -H 'Content-Type: application/json' \ + curl $CURL_PARAMS -i -X POST -H 'Content-Type: application/json' \ -d "$(generate_sechub_user_signup_data $1 $2)" \ "$SECHUB_SERVER/api/anonymous/signup" | $CURL_FILTER } diff --git a/sechub-integrationtest/src/main/java/com/mercedesbenz/sechub/integrationtest/api/AsUser.java b/sechub-integrationtest/src/main/java/com/mercedesbenz/sechub/integrationtest/api/AsUser.java index 5c9cdc63e8..7bff973b36 100644 --- a/sechub-integrationtest/src/main/java/com/mercedesbenz/sechub/integrationtest/api/AsUser.java +++ b/sechub-integrationtest/src/main/java/com/mercedesbenz/sechub/integrationtest/api/AsUser.java @@ -190,7 +190,19 @@ private IntegrationTestContext getContext() { /** * User trigger create of project * - * @param project + * @param project project instance + * @param owner owner of the project + * @throws RestClientException + */ + public AsUser createProject(TestProject project, TestUser owner) { + return createProject(project, owner.getUserId()); + } + + /** + * User trigger create of project + * + * @param project project instance + * @param ownerUserId owner of the project * @throws RestClientException */ public AsUser createProject(TestProject project, String ownerUserId) { diff --git a/sechub-integrationtest/src/main/java/com/mercedesbenz/sechub/integrationtest/api/TestAPI.java b/sechub-integrationtest/src/main/java/com/mercedesbenz/sechub/integrationtest/api/TestAPI.java index 490c1b590b..b3e25cb204 100644 --- a/sechub-integrationtest/src/main/java/com/mercedesbenz/sechub/integrationtest/api/TestAPI.java +++ b/sechub-integrationtest/src/main/java/com/mercedesbenz/sechub/integrationtest/api/TestAPI.java @@ -45,6 +45,7 @@ import com.mercedesbenz.sechub.integrationtest.internal.DefaultTestExecutionProfile; import com.mercedesbenz.sechub.integrationtest.internal.IntegrationTestContext; import com.mercedesbenz.sechub.integrationtest.internal.IntegrationTestDefaultProfiles; +import com.mercedesbenz.sechub.integrationtest.internal.SecHubClientExecutor.ExecutionResult; import com.mercedesbenz.sechub.integrationtest.internal.TestAutoCleanupData; import com.mercedesbenz.sechub.integrationtest.internal.TestAutoCleanupData.TestCleanupTimeUnit; import com.mercedesbenz.sechub.integrationtest.internal.TestJSONHelper; @@ -124,6 +125,10 @@ public static AssertReportUnordered assertReportUnordered(String json) { return AssertReportUnordered.assertReportUnordered(json); } + public static AssertExecutionResult assertExecutionResult(ExecutionResult result) { + return AssertExecutionResult.assertResult(result); + } + /** * Asserts given report json - for checks you will always need to explicit use * indexes and the report must have an explicit ordering - otherwise you have diff --git a/sechub-integrationtest/src/main/java/com/mercedesbenz/sechub/integrationtest/api/TestProject.java b/sechub-integrationtest/src/main/java/com/mercedesbenz/sechub/integrationtest/api/TestProject.java index a910d7b9e1..a9bb4cf548 100644 --- a/sechub-integrationtest/src/main/java/com/mercedesbenz/sechub/integrationtest/api/TestProject.java +++ b/sechub-integrationtest/src/main/java/com/mercedesbenz/sechub/integrationtest/api/TestProject.java @@ -20,6 +20,14 @@ public class TestProject { all.add(this); } + /** + * Creates a test project without a whitelist. Description is automatically + * generated and contains the project id as well + * + * @param projectIdPart name of the project if {@link #prepare(TestScenario)} is + * not called, otherwise it will be the part after the + * scenario prefix + */ public TestProject(String projectIdPart) { this(projectIdPart, false); } @@ -31,6 +39,16 @@ public TestProject(String projectIdPart, boolean withWhiteList) { this.withWhiteList = withWhiteList; } + public void setDescription(String description) { + this.description = description; + } + + /** + * Prepare the test project for the given scenario. Calculates the prefix for + * the project id from the scenario name. + * + * @param scenario + */ public void prepare(TestScenario scenario) { this.prefix = scenario.getName().toLowerCase(); whiteListUrls.clear(); diff --git a/sechub-integrationtest/src/test/java/com/mercedesbenz/sechub/integrationtest/scenario1/CheckProjectIdLengthScenario1IntTest.java b/sechub-integrationtest/src/test/java/com/mercedesbenz/sechub/integrationtest/scenario1/CheckProjectIdLengthScenario1IntTest.java new file mode 100644 index 0000000000..61cd2d482a --- /dev/null +++ b/sechub-integrationtest/src/test/java/com/mercedesbenz/sechub/integrationtest/scenario1/CheckProjectIdLengthScenario1IntTest.java @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: MIT +package com.mercedesbenz.sechub.integrationtest.scenario1; + +import static com.mercedesbenz.sechub.integrationtest.api.TestAPI.*; +import static com.mercedesbenz.sechub.integrationtest.internal.IntegrationTestDefaultProfiles.PROFILE_1; +import static com.mercedesbenz.sechub.integrationtest.scenario1.Scenario1.*; +import static org.junit.Assert.*; + +import org.junit.Rule; +import org.junit.Test; +import org.springframework.http.HttpStatus; + +import com.mercedesbenz.sechub.integrationtest.api.IntegrationTestSetup; +import com.mercedesbenz.sechub.integrationtest.api.TestProject; +import com.mercedesbenz.sechub.integrationtest.api.TestUser; +import com.mercedesbenz.sechub.integrationtest.internal.SecHubClientExecutor.ExecutionResult; +import com.mercedesbenz.sechub.sharedkernel.validation.ProjectIdValidationImpl; + +public class CheckProjectIdLengthScenario1IntTest { + + private static final int PROJECT_ID_MAX_LENGTH = ProjectIdValidationImpl.PROJECTID_LENGTH_MAX; + + private static final int PROJECT_ID_MAX_LENGTH_PLUS_ONE = ProjectIdValidationImpl.PROJECTID_LENGTH_MAX + 1; + + @Rule + public IntegrationTestSetup setup = IntegrationTestSetup.forScenario(Scenario1.class); + + @Test + public void can_create_project_with_project_id_having_maximum_length_and_execute_scan_using_sechub_cli() { + /* prepare */ + TestUser user = OWNER_1; + TestProject project = createTestProjectWithProjectIdLength(PROJECT_ID_MAX_LENGTH); + + /* execute + test 1 */ + as(SUPER_ADMIN).createProject(project, user).assignUserToProject(user, project); + as(SUPER_ADMIN).addProjectsToProfile(PROFILE_1.id, project); + + /* test2 */ + // check client is able to handle the long project id as well + ExecutionResult result = as(user).withSecHubClient().startAndWaitForCodeScan(project); + assertExecutionResult(result).isGreen(); // result is as expected... + } + + @Test + public void cannot_create_project_with_project_id_having_more_than_maximum_length() { + /* prepare */ + TestUser user = OWNER_1; + TestProject project = createTestProjectWithProjectIdLength(PROJECT_ID_MAX_LENGTH_PLUS_ONE); + + /* execute + test */ + expectHttpFailure(() -> { + as(SUPER_ADMIN).createProject(project, user); + }, HttpStatus.BAD_REQUEST); + + } + + private TestProject createTestProjectWithProjectIdLength(int projectIdLength) { + String prefix = System.currentTimeMillis() + "_"; + String postfix = "x".repeat(projectIdLength - prefix.length()); + String projectIdPart = prefix + postfix; + + assertEquals(projectIdLength, projectIdPart.length()); + + TestProject project = new TestProject(projectIdPart); + project.setDescription("a small description"); // we want to keep the description small... + return project; + } + +} diff --git a/sechub-server/src/main/resources/db/migration/U28__enlarge_project_id.sql b/sechub-server/src/main/resources/db/migration/U28__enlarge_project_id.sql new file mode 100644 index 0000000000..890de902e2 --- /dev/null +++ b/sechub-server/src/main/resources/db/migration/U28__enlarge_project_id.sql @@ -0,0 +1,24 @@ +-- SPDX-License-Identifier: MIT + +-- we switch project_id size back to old 120 (3x40) +-- If this is not possible because already added bigger data, database shall give us an error, +-- so we know we have to migrate those data manually... +ALTER TABLE adm_project ALTER COLUMN project_id TYPE varchar(120); +ALTER TABLE scan_report ALTER COLUMN project_id TYPE varchar(120); +ALTER TABLE scan_access ALTER COLUMN project_id TYPE varchar(120); +ALTER TABLE schedule_access ALTER COLUMN project_id TYPE varchar(120); +ALTER TABLE schedule_project_whitelist ALTER COLUMN project_id TYPE varchar(120); +ALTER TABLE schedule_sechub_job ALTER COLUMN project_id TYPE varchar(120); +ALTER TABLE scan_project_log ALTER COLUMN project_id TYPE varchar(120); +ALTER TABLE adm_job_information ALTER COLUMN project_id TYPE varchar(120); +ALTER TABLE scan_product_result ALTER COLUMN project_id TYPE varchar(120); +ALTER TABLE scan_project_config ALTER COLUMN project_id TYPE varchar(120); +ALTER TABLE adm_project_metadata ALTER COLUMN project_id TYPE varchar(120); +ALTER TABLE schedule_project_config ALTER COLUMN project_id TYPE varchar(120); +ALTER TABLE statistic_job ALTER COLUMN project_id TYPE varchar(120); +ALTER TABLE statistic_job_run ALTER COLUMN project_id TYPE varchar(120); + +ALTER TABLE adm_project_whitelist_uri ALTER COLUMN project_project_id TYPE varchar(120); + +ALTER TABLE adm_project_to_user ALTER COLUMN projects_project_id TYPE varchar(120); +ALTER TABLE scan_execution_profile_to_project ALTER COLUMN projects_project_id TYPE varchar(120); \ No newline at end of file diff --git a/sechub-server/src/main/resources/db/migration/V28__enlarge_project_id.sql b/sechub-server/src/main/resources/db/migration/V28__enlarge_project_id.sql new file mode 100644 index 0000000000..765ab4490b --- /dev/null +++ b/sechub-server/src/main/resources/db/migration/V28__enlarge_project_id.sql @@ -0,0 +1,22 @@ +-- SPDX-License-Identifier: MIT + +-- Update project id : we now accept 150 characters see ProjectIdValidation +ALTER TABLE adm_project ALTER COLUMN project_id TYPE varchar(150); +ALTER TABLE scan_report ALTER COLUMN project_id TYPE varchar(150); +ALTER TABLE scan_access ALTER COLUMN project_id TYPE varchar(150); +ALTER TABLE schedule_access ALTER COLUMN project_id TYPE varchar(150); +ALTER TABLE schedule_project_whitelist ALTER COLUMN project_id TYPE varchar(150); +ALTER TABLE schedule_sechub_job ALTER COLUMN project_id TYPE varchar(150); +ALTER TABLE scan_project_log ALTER COLUMN project_id TYPE varchar(150); +ALTER TABLE adm_job_information ALTER COLUMN project_id TYPE varchar(150); +ALTER TABLE scan_product_result ALTER COLUMN project_id TYPE varchar(150); +ALTER TABLE scan_project_config ALTER COLUMN project_id TYPE varchar(150); +ALTER TABLE adm_project_metadata ALTER COLUMN project_id TYPE varchar(150); +ALTER TABLE schedule_project_config ALTER COLUMN project_id TYPE varchar(150); +ALTER TABLE statistic_job ALTER COLUMN project_id TYPE varchar(150); +ALTER TABLE statistic_job_run ALTER COLUMN project_id TYPE varchar(150); + +ALTER TABLE adm_project_whitelist_uri ALTER COLUMN project_project_id TYPE varchar(150); + +ALTER TABLE adm_project_to_user ALTER COLUMN projects_project_id TYPE varchar(150); +ALTER TABLE scan_execution_profile_to_project ALTER COLUMN projects_project_id TYPE varchar(150); \ No newline at end of file diff --git a/sechub-shared-kernel/src/main/java/com/mercedesbenz/sechub/sharedkernel/validation/ProjectIdValidationImpl.java b/sechub-shared-kernel/src/main/java/com/mercedesbenz/sechub/sharedkernel/validation/ProjectIdValidationImpl.java index 1171f2a843..6201259049 100644 --- a/sechub-shared-kernel/src/main/java/com/mercedesbenz/sechub/sharedkernel/validation/ProjectIdValidationImpl.java +++ b/sechub-shared-kernel/src/main/java/com/mercedesbenz/sechub/sharedkernel/validation/ProjectIdValidationImpl.java @@ -7,7 +7,7 @@ public class ProjectIdValidationImpl extends AbstractSimpleStringValidation implements ProjectIdValidation { public static final int PROJECTID_LENGTH_MIN = 2; - public static final int PROJECTID_LENGTH_MAX = 40; + public static final int PROJECTID_LENGTH_MAX = 150; @Override protected void setup(ValidationConfig config) { diff --git a/sechub-shared-kernel/src/test/java/com/mercedesbenz/sechub/sharedkernel/validation/ProjectIdValidationImplTest.java b/sechub-shared-kernel/src/test/java/com/mercedesbenz/sechub/sharedkernel/validation/ProjectIdValidationImplTest.java index 4b44c0f5c0..baa7139406 100644 --- a/sechub-shared-kernel/src/test/java/com/mercedesbenz/sechub/sharedkernel/validation/ProjectIdValidationImplTest.java +++ b/sechub-shared-kernel/src/test/java/com/mercedesbenz/sechub/sharedkernel/validation/ProjectIdValidationImplTest.java @@ -12,10 +12,10 @@ public class ProjectIdValidationImplTest { private ProjectIdValidationImpl validationToTest = new ProjectIdValidationImpl(); - private static final String VALID_PROJECT_ID_WITH_40_CHARS = "a2345678901234567890b2345678901234567890"; + private static final String VALID_PROJECT_ID_WITH_150_CHARS = "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"; @ParameterizedTest - @ValueSource(strings = { "a2", "i-am-with-hyphens", "i_am_with_underscore", VALID_PROJECT_ID_WITH_40_CHARS }) + @ValueSource(strings = { "a2", "i-am-with-hyphens", "i_am_with_underscore", VALID_PROJECT_ID_WITH_150_CHARS }) void valid_projectIds(String projectId) { /* execute */ ValidationResult validationResult = validationToTest.validate(projectId); @@ -30,7 +30,7 @@ void valid_projectIds(String projectId) { @NullSource @EmptySource @ValueSource(strings = { "a", "i.am.with.dot", "i-am/slashy", "with\\backslash", "percent%", "dollar$", "question?", "colon:", "exclamationmark!", - VALID_PROJECT_ID_WITH_40_CHARS + "x" }) + VALID_PROJECT_ID_WITH_150_CHARS + "x" }) void invalid_projectIds(String projectId) { /* execute */ ValidationResult validationResult = validationToTest.validate(projectId); diff --git a/sechub-wrapper-owasp-zap/src/main/resources/zap-ruleset-helper/requirements.txt b/sechub-wrapper-owasp-zap/src/main/resources/zap-ruleset-helper/requirements.txt index 049e44a340..e691086472 100644 --- a/sechub-wrapper-owasp-zap/src/main/resources/zap-ruleset-helper/requirements.txt +++ b/sechub-wrapper-owasp-zap/src/main/resources/zap-ruleset-helper/requirements.txt @@ -1,3 +1,3 @@ bs4==0.0.1 beautifulsoup4==4.11.1 -requests==2.31.0 \ No newline at end of file +requests==2.32.0 \ No newline at end of file diff --git a/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/cli/PrepareWrapperKeyConstants.java b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/cli/PrepareWrapperKeyConstants.java index ae54669539..8502a2a7e4 100644 --- a/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/cli/PrepareWrapperKeyConstants.java +++ b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/cli/PrepareWrapperKeyConstants.java @@ -22,8 +22,18 @@ public class PrepareWrapperKeyConstants { */ public static final String KEY_PDS_PREPARE_MODULE_GIT_ENABLED = "pds.prepare.module.git.enabled"; + /** + * Flag to enable the skopeo prepare module + */ + public static final String KEY_PDS_PREPARE_MODULE_SKOPEO_ENABLED = "pds.prepare.module.skopeo.enabled"; + /** * Flag to clean the git folder from git files and clone without history */ public static final String KEY_PDS_PREPARE_AUTO_CLEANUP_GIT_FOLDER = "pds.prepare.auto.cleanup.git.folder"; + + /** + * Filename for skopeo authentication file + */ + public static final String KEY_PDS_PREPARE_AUTHENTICATION_FILE_SKOPEO = "pds.prepare.authentication.file.skopeo"; } diff --git a/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/AbstractInputValidator.java b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/AbstractInputValidator.java new file mode 100644 index 0000000000..d09a85c0f3 --- /dev/null +++ b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/AbstractInputValidator.java @@ -0,0 +1,133 @@ +package com.mercedesbenz.sechub.wrapper.prepare.modules; + +import static com.mercedesbenz.sechub.wrapper.prepare.modules.InputValidatorExitcode.*; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.regex.Pattern; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.mercedesbenz.sechub.commons.model.*; +import com.mercedesbenz.sechub.wrapper.prepare.prepare.PrepareWrapperContext; + +public class AbstractInputValidator implements InputValidator { + + private static final Logger LOG = LoggerFactory.getLogger(AbstractInputValidator.class); + private final String TYPE; + private final Pattern LOCATION_PATTERN; + private final Pattern USERNAME_PATTERN; + private final Pattern PASSWORD_PATTERN; + private final List forbiddenCharacters = Collections + .unmodifiableList(Arrays.asList(">", "<", "!", "?", "*", "'", "\"", ";", "&", "|", "`", "$", "{", "}")); + + public AbstractInputValidator(String type, Pattern locationPattern, Pattern usernamePattern, Pattern passwordPattern) { + assertPatternNotNull(locationPattern); + assertPatternNotNull(usernamePattern); + assertPatternNotNull(passwordPattern); + if (isTypeNullOrEmpty(type)) { + throw new IllegalArgumentException("Type must not be null or empty."); + } + + this.TYPE = type; + this.LOCATION_PATTERN = locationPattern; + this.USERNAME_PATTERN = usernamePattern; + this.PASSWORD_PATTERN = passwordPattern; + } + + public void validate(PrepareWrapperContext context) throws PrepareWrapperInputValidatorException { + validateModule(context); + validateCredentials(context); + } + + private void validateModule(PrepareWrapperContext context) throws PrepareWrapperInputValidatorException { + SecHubRemoteDataConfiguration secHubRemoteDataConfiguration = context.getRemoteDataConfiguration(); + String location = secHubRemoteDataConfiguration.getLocation(); + String type = secHubRemoteDataConfiguration.getType(); + + if (isTypeNullOrEmpty(type)) { + LOG.debug("No type defined. Location is: {}", location); + validateLocation(location); + return; + } else if (isMatchingType(type)) { + LOG.debug("Type is matching type {}. Location is: {}", TYPE, location); + validateLocation(location); + return; + } + throw new PrepareWrapperInputValidatorException("Defined type " + type + " was not modules type " + TYPE + ".", TYPE_NOT_MATCHING_PATTERN); + } + + private void validateCredentials(PrepareWrapperContext context) throws PrepareWrapperInputValidatorException { + SecHubRemoteDataConfiguration secHubRemoteDataConfiguration = context.getRemoteDataConfiguration(); + + if (secHubRemoteDataConfiguration.getCredentials().isPresent()) { + SecHubRemoteCredentialConfiguration remoteCredentialConfiguration = secHubRemoteDataConfiguration.getCredentials().get(); + if (remoteCredentialConfiguration.getUser().isPresent()) { + SecHubRemoteCredentialUserData user = remoteCredentialConfiguration.getUser().get(); + validateUsername(user.getName()); + validatePassword(user.getPassword()); + return; + } + // credentials object was empty + throw new IllegalStateException("Defined credentials must contain credential user and can not be empty."); + } + } + + public void validateUsername(String username) throws PrepareWrapperInputValidatorException { + if (username == null || username.isBlank()) { + throw new IllegalStateException("Defined username must not be null or empty. Username is required for login."); + } + + if (!USERNAME_PATTERN.matcher(username).matches()) { + throw new PrepareWrapperInputValidatorException("Defined username must match the " + TYPE + " pattern.", CREDENTIALS_USERNAME_NOT_MATCHING_PATTERN); + } + } + + public void validatePassword(String password) throws PrepareWrapperInputValidatorException { + if (password == null || password.isBlank()) { + throw new IllegalStateException("Defined password must not be null or empty. Password is required for login."); + } + + if (!PASSWORD_PATTERN.matcher(password).matches()) { + throw new PrepareWrapperInputValidatorException("Defined password must match the " + TYPE + " Api token pattern.", + CREDENTIALS_PASSWORD_NOT_MATCHING_PATTERN); + } + } + + public void validateLocation(String location) throws PrepareWrapperInputValidatorException { + if (location == null || location.isBlank()) { + throw new IllegalStateException("Defined location must not be null or empty. Location is required for download remote data."); + } + validateLocationCharacters(location); + if (!LOCATION_PATTERN.matcher(location).matches()) { + throw new PrepareWrapperInputValidatorException("Defined location must match the " + TYPE + " pattern.", LOCATION_NOT_MATCHING_PATTERN); + } + } + + private boolean isTypeNullOrEmpty(String type) { + return type == null || type.isEmpty(); + } + + private boolean isMatchingType(String type) { + return TYPE.equalsIgnoreCase(type); + } + + private void validateLocationCharacters(String url) { + if (url.contains(" ")) { + throw new IllegalArgumentException("Defined location URL must not contain whitespaces."); + } + for (String forbiddenCharacter : forbiddenCharacters) { + if (url.contains(forbiddenCharacter)) { + throw new IllegalArgumentException("Defined location URL must not contain forbidden characters: " + forbiddenCharacter); + } + } + } + + private void assertPatternNotNull(Pattern pattern) { + if (pattern == null) { + throw new IllegalArgumentException("Pattern must not be null."); + } + } +} diff --git a/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/GitInputValidator.java b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/GitInputValidator.java deleted file mode 100644 index f7d65a3ddf..0000000000 --- a/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/GitInputValidator.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.mercedesbenz.sechub.wrapper.prepare.modules; - -import java.util.Arrays; -import java.util.List; -import java.util.regex.Pattern; - -import org.springframework.stereotype.Component; - -@Component -public class GitInputValidator implements InputValidator { - private static final String GIT_LOCATION_REGEX = "((git|ssh|http(s)?)|(git@[\\w\\.]+))(:(//)?)([\\w\\.@\\:/\\-~]+)(\\.git)(/)?$"; - private static final Pattern GIT_LOCATION_PATTERN = Pattern.compile(GIT_LOCATION_REGEX); - - private static final String GIT_USERNAME_REGEX = "^[a-zA-Z0-9-_\\d](?:[a-zA-Z0-9-_\\d]|(?=[a-zA-Z0-9-_\\d])){0,38}$"; - private static final Pattern GIT_USERNAME_PATTERN = Pattern.compile(GIT_USERNAME_REGEX); - - private static final String GIT_PASSWORD_REGEX = "^(gh[ps]_[a-zA-Z0-9]{36}|github_pat_[a-zA-Z0-9]{22}_[a-zA-Z0-9]{59})$"; - private static final Pattern GIT_PASSWORD_PATTERN = Pattern.compile(GIT_PASSWORD_REGEX); - - private final List defaultForbiddenCharacters = Arrays.asList(">", "<", "!", "?", "*", "'", "\"", ";", "&", "|", "`", "$", "{", "}"); - - public void validateLocationCharacters(String url, List forbiddenCharacters) { - if (forbiddenCharacters == null) { - forbiddenCharacters = defaultForbiddenCharacters; - } - if (url.contains(" ")) { - throw new IllegalArgumentException("Defined URL must not contain whitespaces."); - } - for (String forbiddenCharacter : forbiddenCharacters) { - if (url.contains(forbiddenCharacter)) { - throw new IllegalArgumentException("Defined URL must not contain forbidden characters: " + forbiddenCharacter); - } - } - } - - public void validateUsername(String username) { - if (username == null || username.isBlank()) { - throw new IllegalStateException("Defined username must not be null or empty."); - } - - if (!GIT_USERNAME_PATTERN.matcher(username).matches()) { - throw new IllegalStateException("Defined username must match the modules pattern."); - } - } - - public void validatePassword(String password) { - if (password == null || password.isBlank()) { - throw new IllegalStateException("Defined password must not be null or empty."); - } - - if (!GIT_PASSWORD_PATTERN.matcher(password).matches()) { - throw new IllegalStateException("Defined password must match the Git Api token pattern."); - } - } - - public boolean validateLocation(String location) { - if (location == null || location.isBlank()) { - throw new IllegalStateException("Defined location must not be null or empty."); - } - return GIT_LOCATION_PATTERN.matcher(location).matches(); - } -} diff --git a/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/InputValidator.java b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/InputValidator.java index d8e28d1b75..5e4037f450 100644 --- a/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/InputValidator.java +++ b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/InputValidator.java @@ -1,15 +1,7 @@ package com.mercedesbenz.sechub.wrapper.prepare.modules; -import java.util.List; +import com.mercedesbenz.sechub.wrapper.prepare.prepare.PrepareWrapperContext; public interface InputValidator { - - boolean validateLocation(String location); - - void validateUsername(String username); - - void validatePassword(String password); - - void validateLocationCharacters(String url, List forbiddenCharacters); - + void validate(PrepareWrapperContext context) throws PrepareWrapperInputValidatorException; } diff --git a/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/InputValidatorExitcode.java b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/InputValidatorExitcode.java new file mode 100644 index 0000000000..f8d76c2b0f --- /dev/null +++ b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/InputValidatorExitcode.java @@ -0,0 +1,22 @@ +package com.mercedesbenz.sechub.wrapper.prepare.modules; + +public enum InputValidatorExitcode { + + LOCATION_NOT_MATCHING_PATTERN(1), + + CREDENTIALS_USERNAME_NOT_MATCHING_PATTERN(2), + + CREDENTIALS_PASSWORD_NOT_MATCHING_PATTERN(3), + + TYPE_NOT_MATCHING_PATTERN(4); + + private int exitCode; + + private InputValidatorExitcode(int exitCode) { + this.exitCode = exitCode; + } + + public int getExitCode() { + return exitCode; + } +} diff --git a/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/PrepareWrapperInputValidatorException.java b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/PrepareWrapperInputValidatorException.java new file mode 100644 index 0000000000..76525f3912 --- /dev/null +++ b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/PrepareWrapperInputValidatorException.java @@ -0,0 +1,20 @@ +package com.mercedesbenz.sechub.wrapper.prepare.modules; + +public class PrepareWrapperInputValidatorException extends Exception { + private static final long serialVersionUID = 1L; + + private InputValidatorExitcode exitCode; + + public PrepareWrapperInputValidatorException(String message, InputValidatorExitcode exitCode) { + this(message, null, exitCode); + } + + public PrepareWrapperInputValidatorException(String message, Exception e, InputValidatorExitcode exitCode) { + super(message, e); + this.exitCode = exitCode; + } + + public InputValidatorExitcode getExitCode() { + return exitCode; + } +} diff --git a/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/PrepareWrapperModule.java b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/PrepareWrapperModule.java index 3109d9b0a6..46d24a7fa1 100644 --- a/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/PrepareWrapperModule.java +++ b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/PrepareWrapperModule.java @@ -1,17 +1,29 @@ // SPDX-License-Identifier: MIT package com.mercedesbenz.sechub.wrapper.prepare.modules; +import static com.mercedesbenz.sechub.wrapper.prepare.cli.PrepareWrapperEnvironmentVariables.PDS_PREPARE_CREDENTIAL_PASSWORD; +import static com.mercedesbenz.sechub.wrapper.prepare.cli.PrepareWrapperEnvironmentVariables.PDS_PREPARE_CREDENTIAL_USERNAME; + import java.io.IOException; +import java.util.HashMap; + +import javax.crypto.SealedObject; import org.springframework.stereotype.Service; +import com.mercedesbenz.sechub.commons.core.security.CryptoAccess; +import com.mercedesbenz.sechub.commons.model.SecHubRemoteCredentialUserData; import com.mercedesbenz.sechub.wrapper.prepare.prepare.PrepareWrapperContext; @Service public interface PrepareWrapperModule { - boolean isAbleToPrepare(PrepareWrapperContext context); - - void prepare(PrepareWrapperContext context) throws IOException; + boolean prepare(PrepareWrapperContext context) throws IOException; + default void addSealedUserCredentials(SecHubRemoteCredentialUserData user, HashMap credentialMap) { + SealedObject sealedUsername = CryptoAccess.CRYPTO_STRING.seal(user.getName()); + SealedObject sealedPassword = CryptoAccess.CRYPTO_STRING.seal(user.getPassword()); + credentialMap.put(PDS_PREPARE_CREDENTIAL_USERNAME, sealedUsername); + credentialMap.put(PDS_PREPARE_CREDENTIAL_PASSWORD, sealedPassword); + } } diff --git a/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/WrapperTool.java b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/WrapperTool.java index ae03940deb..217ddc00d0 100644 --- a/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/WrapperTool.java +++ b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/WrapperTool.java @@ -4,24 +4,18 @@ import static com.mercedesbenz.sechub.wrapper.prepare.cli.PrepareWrapperKeyConstants.KEY_PDS_PREPARE_PROCESS_TIMEOUT_SECONDS; import java.io.IOException; -import java.util.Map; import java.util.concurrent.TimeUnit; -import javax.crypto.SealedObject; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; -import com.mercedesbenz.sechub.commons.core.security.CryptoAccess; import com.mercedesbenz.sechub.commons.pds.PDSDefaultParameterValueConstants; -import com.mercedesbenz.sechub.commons.pds.PDSProcessAdapterFactory; import com.mercedesbenz.sechub.commons.pds.ProcessAdapter; @Component -abstract class WrapperTool { +public abstract class WrapperTool { private static final Logger LOG = LoggerFactory.getLogger(WrapperTool.class); private static final int defaultMinutesToWaitForProduct = PDSDefaultParameterValueConstants.DEFAULT_MINUTES_TO_WAIT_FOR_PRODUCT; @@ -32,12 +26,9 @@ abstract class WrapperTool { @Value("${" + KEY_PDS_PREPARE_PROCESS_TIMEOUT_SECONDS + ":-1}") private int pdsPrepareProcessTimeoutSeconds; - @Autowired - PDSProcessAdapterFactory processAdapterFactory; - - abstract void cleanUploadDirectory(String uploadDirectory) throws IOException; + protected abstract void cleanUploadDirectory(String uploadDirectory) throws IOException; - void waitForProcessToFinish(ProcessAdapter process) { + protected void waitForProcessToFinish(ProcessAdapter process) { LOG.debug("Wait for wrapper to finish process."); int seconds = calculateTimeoutSeconds(); @@ -46,28 +37,17 @@ void waitForProcessToFinish(ProcessAdapter process) { try { exitDoneInTime = process.waitFor(seconds, TimeUnit.SECONDS); } catch (InterruptedException e) { - throw new RuntimeException("GIT wrapper could not finish process.", e); + throw new RuntimeException("Wrapper for executed modul " + this.getClass().getSimpleName() + " could not finish process.", e); } if (!exitDoneInTime) { - throw new RuntimeException("GIT wrapper could not finish process. Waited " + pdsPrepareProcessTimeoutSeconds + " seconds."); + throw new RuntimeException("Wrapper for executed modul " + this.getClass().getSimpleName() + " could not finish process. Waited " + + pdsPrepareProcessTimeoutSeconds + " seconds."); } if (process.exitValue() != 0) { - throw new RuntimeException("GIT wrapper process failed with exit code: " + process.exitValue()); - } - } - - void exportEnvironmentVariables(ProcessBuilder builder, Map credentialMap) throws IOException { - Map environment = builder.environment(); - if (credentialMap != null && !credentialMap.isEmpty()) { - for (Map.Entry entry : credentialMap.entrySet()) { - try { - environment.put(entry.getKey(), CryptoAccess.CRYPTO_STRING.unseal(entry.getValue())); - } catch (Exception e) { - throw new IOException("Error while unsealing credential: " + entry.getKey(), e); - } - } + throw new RuntimeException( + "Wrapper for executed modul " + this.getClass().getSimpleName() + " process failed with exit code: " + process.exitValue()); } } diff --git a/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/GitContext.java b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/git/GitContext.java similarity index 89% rename from sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/GitContext.java rename to sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/git/GitContext.java index abbd7d97dd..c68dccff53 100644 --- a/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/GitContext.java +++ b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/git/GitContext.java @@ -1,4 +1,6 @@ -package com.mercedesbenz.sechub.wrapper.prepare.modules; +package com.mercedesbenz.sechub.wrapper.prepare.modules.git; + +import com.mercedesbenz.sechub.wrapper.prepare.modules.ToolContext; public class GitContext extends ToolContext { private boolean cloneWithoutHistory; diff --git a/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/git/GitInputValidator.java b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/git/GitInputValidator.java new file mode 100644 index 0000000000..08ad23daf9 --- /dev/null +++ b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/git/GitInputValidator.java @@ -0,0 +1,24 @@ +package com.mercedesbenz.sechub.wrapper.prepare.modules.git; + +import java.util.regex.Pattern; + +import org.springframework.stereotype.Component; + +import com.mercedesbenz.sechub.commons.model.*; +import com.mercedesbenz.sechub.wrapper.prepare.modules.AbstractInputValidator; + +@Component +public class GitInputValidator extends AbstractInputValidator { + + private static final String TYPE = "git"; + private static final String GIT_LOCATION_REGEX = "((git|ssh|http(s)?)|(git@[\\w\\.]+))(:(//)?)([\\w\\.@\\:/\\-~]+)(\\.git)(/)?$"; + private static final Pattern GIT_LOCATION_PATTERN = Pattern.compile(GIT_LOCATION_REGEX); + private static final String GIT_USERNAME_REGEX = "^[a-zA-Z0-9-_\\d](?:[a-zA-Z0-9-_\\d]|(?=[a-zA-Z0-9-_\\d])){0,38}$"; + private static final Pattern GIT_USERNAME_PATTERN = Pattern.compile(GIT_USERNAME_REGEX); + private static final String GIT_PASSWORD_REGEX = "^(gh[ps]_[a-zA-Z0-9]{36}|github_pat_[a-zA-Z0-9]{22}_[a-zA-Z0-9]{59})$"; + private static final Pattern GIT_PASSWORD_PATTERN = Pattern.compile(GIT_PASSWORD_REGEX); + + public GitInputValidator() { + super(TYPE, GIT_LOCATION_PATTERN, GIT_USERNAME_PATTERN, GIT_PASSWORD_PATTERN); + } +} diff --git a/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/JGitAdapter.java b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/git/JGitAdapter.java similarity index 98% rename from sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/JGitAdapter.java rename to sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/git/JGitAdapter.java index b027a50888..a9003ca249 100644 --- a/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/JGitAdapter.java +++ b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/git/JGitAdapter.java @@ -1,4 +1,4 @@ -package com.mercedesbenz.sechub.wrapper.prepare.modules; +package com.mercedesbenz.sechub.wrapper.prepare.modules.git; import static com.mercedesbenz.sechub.wrapper.prepare.cli.PrepareWrapperEnvironmentVariables.PDS_PREPARE_CREDENTIAL_PASSWORD; import static com.mercedesbenz.sechub.wrapper.prepare.cli.PrepareWrapperEnvironmentVariables.PDS_PREPARE_CREDENTIAL_USERNAME; diff --git a/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/PrepareWrapperModuleGit.java b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/git/PrepareWrapperModuleGit.java similarity index 60% rename from sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/PrepareWrapperModuleGit.java rename to sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/git/PrepareWrapperModuleGit.java index eefc2f9403..11dd9d77e4 100644 --- a/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/PrepareWrapperModuleGit.java +++ b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/git/PrepareWrapperModuleGit.java @@ -1,4 +1,4 @@ -package com.mercedesbenz.sechub.wrapper.prepare.modules; +package com.mercedesbenz.sechub.wrapper.prepare.modules.git; import static com.mercedesbenz.sechub.wrapper.prepare.cli.PrepareWrapperEnvironmentVariables.*; import static com.mercedesbenz.sechub.wrapper.prepare.cli.PrepareWrapperKeyConstants.KEY_PDS_PREPARE_AUTO_CLEANUP_GIT_FOLDER; @@ -18,8 +18,9 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; -import com.mercedesbenz.sechub.commons.core.security.CryptoAccess; import com.mercedesbenz.sechub.commons.model.*; +import com.mercedesbenz.sechub.wrapper.prepare.modules.PrepareWrapperInputValidatorException; +import com.mercedesbenz.sechub.wrapper.prepare.modules.PrepareWrapperModule; import com.mercedesbenz.sechub.wrapper.prepare.prepare.PrepareWrapperContext; import com.mercedesbenz.sechub.wrapper.prepare.upload.FileNameSupport; import com.mercedesbenz.sechub.wrapper.prepare.upload.PrepareWrapperUploadService; @@ -27,9 +28,7 @@ @Service public class PrepareWrapperModuleGit implements PrepareWrapperModule { - Logger LOG = LoggerFactory.getLogger(PrepareWrapperModuleGit.class); - - private static final String TYPE = "git"; + private static final Logger LOG = LoggerFactory.getLogger(PrepareWrapperModuleGit.class); @Value("${" + KEY_PDS_PREPARE_AUTO_CLEANUP_GIT_FOLDER + ":true}") private boolean pdsPrepareAutoCleanupGitFolder; @@ -49,63 +48,37 @@ public class PrepareWrapperModuleGit implements PrepareWrapperModule { @Autowired FileNameSupport filesSupport; - public boolean isAbleToPrepare(PrepareWrapperContext context) { + public boolean prepare(PrepareWrapperContext context) throws IOException { if (!pdsPrepareModuleGitEnabled) { LOG.debug("Git module is disabled"); return false; } - for (SecHubRemoteDataConfiguration secHubRemoteDataConfiguration : context.getRemoteDataConfigurationList()) { - String location = secHubRemoteDataConfiguration.getLocation(); - - gitInputValidator.validateLocationCharacters(location, null); - - if (isMatchingGitType(secHubRemoteDataConfiguration.getType())) { - LOG.debug("Type is git"); - if (!gitInputValidator.validateLocation(location)) { - context.getUserMessages().add(new SecHubMessage(SecHubMessageType.WARNING, "Type is git but location does not match git URL pattern")); - LOG.warn("User defined type as 'git', but the defined location was not a valid git location: {}", location); - return false; - } - return true; - } - - if (gitInputValidator.validateLocation(location)) { - LOG.debug("Location is a git URL"); - return true; - } - + try { + gitInputValidator.validate(context); + } catch (PrepareWrapperInputValidatorException e) { + LOG.warn("Module {} could not resolve remote configuration.", getClass().getSimpleName(), e); + return false; } - return false; - } - - public void prepare(PrepareWrapperContext context) throws IOException { - LOG.debug("Start remote data preparation for GIT repository"); + LOG.debug("Module {} resolved remote configuration and will prepare.", getClass().getSimpleName()); - List remoteDataConfigurationList = context.getRemoteDataConfigurationList(); + SecHubRemoteDataConfiguration secHubRemoteDataConfiguration = context.getRemoteDataConfiguration(); - for (SecHubRemoteDataConfiguration secHubRemoteDataConfiguration : remoteDataConfigurationList) { - prepareRemoteConfiguration(context, secHubRemoteDataConfiguration); - } + prepareRemoteConfiguration(context, secHubRemoteDataConfiguration); if (!isDownloadSuccessful(context)) { + LOG.error("Download of git repository was not successful."); throw new IOException("Download of git repository was not successful."); } cleanup(context); - // TODO: 15.05.24 laura proper upload - uploadService.upload(context); - } - boolean isMatchingGitType(String type) { - if (type == null || type.isBlank()) { - return false; - } - return TYPE.equalsIgnoreCase(type); + uploadService.upload(context); + return true; } - boolean isDownloadSuccessful(PrepareWrapperContext context) { + protected boolean isDownloadSuccessful(PrepareWrapperContext context) { // check if download folder contains git String uploadFolder = context.getEnvironment().getPdsPrepareUploadFolderDirectory(); @@ -125,56 +98,41 @@ private void prepareRemoteConfiguration(PrepareWrapperContext context, SecHubRem String location = secHubRemoteDataConfiguration.getLocation(); Optional credentials = secHubRemoteDataConfiguration.getCredentials(); - if (!credentials.isPresent()) { + if (credentials.isEmpty()) { clonePublicRepository(context, location); return; } Optional optUser = credentials.get().getUser(); - if (optUser.isPresent()) { - SecHubRemoteCredentialUserData user = optUser.get(); - clonePrivateRepository(context, user, location); - return; + if (optUser.isEmpty()) { + throw new IllegalStateException("Defined credentials have no credential user data for location: " + location); } - throw new IllegalStateException("Defined credentials have no credential user data for location: " + location); + SecHubRemoteCredentialUserData user = optUser.get(); + clonePrivateRepository(context, user, location); } private void clonePrivateRepository(PrepareWrapperContext context, SecHubRemoteCredentialUserData user, String location) throws IOException { - assertUserCredentials(user); - HashMap credentialMap = new HashMap<>(); addSealedUserCredentials(user, credentialMap); /* @formatter:off */ GitContext gitContext = (GitContext) new GitContext.GitContextBuilder(). setCloneWithoutHistory(pdsPrepareAutoCleanupGitFolder). - setLocation(location) - .setCredentialMap(credentialMap). + setLocation(location). + setCredentialMap(credentialMap). setUploadDirectory(context.getEnvironment().getPdsPrepareUploadFolderDirectory()). build(); /* @formatter:on */ git.downloadRemoteData(gitContext); - SecHubMessage message = new SecHubMessage(SecHubMessageType.INFO, "Cloned private repository: " + location); + SecHubMessage message = new SecHubMessage(SecHubMessageType.INFO, "Cloned private image: " + location); context.getUserMessages().add(message); // TODO: 23.05.24 laura isDownloadSuccessful check } - private static void addSealedUserCredentials(SecHubRemoteCredentialUserData user, HashMap credentialMap) { - SealedObject sealedUsername = CryptoAccess.CRYPTO_STRING.seal(user.getName()); - SealedObject sealedPassword = CryptoAccess.CRYPTO_STRING.seal(user.getPassword()); - credentialMap.put(PDS_PREPARE_CREDENTIAL_USERNAME, sealedUsername); - credentialMap.put(PDS_PREPARE_CREDENTIAL_PASSWORD, sealedPassword); - } - - private void assertUserCredentials(SecHubRemoteCredentialUserData user) { - gitInputValidator.validateUsername(user.getName()); - gitInputValidator.validatePassword(user.getPassword()); - } - private void clonePublicRepository(PrepareWrapperContext context, String location) { /* @formatter:off */ GitContext contextGit = (GitContext) new GitContext.GitContextBuilder(). diff --git a/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/WrapperGit.java b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/git/WrapperGit.java similarity index 87% rename from sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/WrapperGit.java rename to sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/git/WrapperGit.java index bf02381275..c06a6f4e6f 100644 --- a/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/WrapperGit.java +++ b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/git/WrapperGit.java @@ -1,4 +1,4 @@ -package com.mercedesbenz.sechub.wrapper.prepare.modules; +package com.mercedesbenz.sechub.wrapper.prepare.modules.git; import java.io.File; import java.io.IOException; @@ -11,7 +11,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import com.mercedesbenz.sechub.commons.pds.PDSProcessAdapterFactory; import com.mercedesbenz.sechub.commons.pds.ProcessAdapter; +import com.mercedesbenz.sechub.wrapper.prepare.modules.WrapperTool; @Component public class WrapperGit extends WrapperTool { @@ -21,6 +23,9 @@ public class WrapperGit extends WrapperTool { @Autowired JGitAdapter jGitAdapter; + @Autowired + PDSProcessAdapterFactory processAdapterFactory; + public void downloadRemoteData(GitContext gitContext) { LOG.debug("Start cloning with JGit."); jGitAdapter.clone(gitContext); diff --git a/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/skopeo/PrepareWrapperModuleSkopeo.java b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/skopeo/PrepareWrapperModuleSkopeo.java new file mode 100644 index 0000000000..abbfce0493 --- /dev/null +++ b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/skopeo/PrepareWrapperModuleSkopeo.java @@ -0,0 +1,139 @@ +package com.mercedesbenz.sechub.wrapper.prepare.modules.skopeo; + +import static com.mercedesbenz.sechub.wrapper.prepare.cli.PrepareWrapperKeyConstants.KEY_PDS_PREPARE_MODULE_SKOPEO_ENABLED; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +import javax.crypto.SealedObject; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import com.mercedesbenz.sechub.commons.model.*; +import com.mercedesbenz.sechub.wrapper.prepare.modules.PrepareWrapperInputValidatorException; +import com.mercedesbenz.sechub.wrapper.prepare.modules.PrepareWrapperModule; +import com.mercedesbenz.sechub.wrapper.prepare.prepare.PrepareWrapperContext; + +@Service +public class PrepareWrapperModuleSkopeo implements PrepareWrapperModule { + + private static final Logger LOG = LoggerFactory.getLogger(PrepareWrapperModuleSkopeo.class); + + @Value("${" + KEY_PDS_PREPARE_MODULE_SKOPEO_ENABLED + ":true}") + private boolean pdsPrepareModuleSkopeoEnabled; + + @Autowired + SkopeoInputValidator skopeoInputValidator; + + @Autowired + WrapperSkopeo skopeo; + + @Override + public boolean prepare(PrepareWrapperContext context) throws IOException { + LOG.debug("Start remote data preparation for Docker repository"); + + if (!pdsPrepareModuleSkopeoEnabled) { + LOG.debug("Skopeo module is disabled"); + return false; + } + + try { + skopeoInputValidator.validate(context); + } catch (PrepareWrapperInputValidatorException e) { + LOG.warn("Module {} could not resolve remote configuration.", getClass().getSimpleName(), e); + return false; + } + + SecHubRemoteDataConfiguration secHubRemoteDataConfiguration = context.getRemoteDataConfiguration(); + prepareRemoteConfiguration(context, secHubRemoteDataConfiguration); + + if (!isDownloadSuccessful(context)) { + LOG.error("Download of git repository was not successful."); + throw new IOException("Download of docker image was not successful."); + } + cleanup(context); + return true; + } + + protected boolean isDownloadSuccessful(PrepareWrapperContext context) { + // check if download folder contains a .tar archive + Path path = Paths.get(context.getEnvironment().getPdsPrepareUploadFolderDirectory()); + if (Files.isDirectory(path)) { + try (Stream walk = Files.walk(path)) { + List result = walk.filter(p -> !Files.isDirectory(p)) // not a directory + .map(p -> p.toString().toLowerCase()) // convert path to string + .filter(f -> f.endsWith(".tar")) // check end with + .toList(); // collect all matched to a List + return !result.isEmpty(); + } catch (IOException e) { + return false; + } + } + return false; + } + + private void cleanup(PrepareWrapperContext context) throws IOException { + skopeo.cleanUploadDirectory(context.getEnvironment().getPdsPrepareUploadFolderDirectory()); + } + + private void prepareRemoteConfiguration(PrepareWrapperContext context, SecHubRemoteDataConfiguration secHubRemoteDataConfiguration) throws IOException { + String location = secHubRemoteDataConfiguration.getLocation(); + Optional credentials = secHubRemoteDataConfiguration.getCredentials(); + + if (credentials.isEmpty()) { + downloadPublicImage(context, location); + return; + } + + Optional optUser = credentials.get().getUser(); + if (optUser.isEmpty()) { + throw new IllegalStateException("Defined credentials have no credential user data for location: " + location); + } + + SecHubRemoteCredentialUserData user = optUser.get(); + downloadPrivateImage(context, user, location); + } + + private void downloadPrivateImage(PrepareWrapperContext context, SecHubRemoteCredentialUserData user, String location) throws IOException { + + HashMap credentialMap = new HashMap<>(); + addSealedUserCredentials(user, credentialMap); + + /* @formatter:off */ + SkopeoContext skopeoContext = (SkopeoContext) new SkopeoContext.SkopeoContextBuilder(). + setLocation(location). + setCredentialMap(credentialMap). + setUploadDirectory(context.getEnvironment().getPdsPrepareUploadFolderDirectory()). + build(); + /* @formatter:on */ + + skopeo.download(skopeoContext); + + SecHubMessage message = new SecHubMessage(SecHubMessageType.INFO, "Download private image: " + location); + context.getUserMessages().add(message); + } + + private void downloadPublicImage(PrepareWrapperContext context, String location) throws IOException { + /* @formatter:off */ + SkopeoContext skopeoContext = (SkopeoContext) new SkopeoContext.SkopeoContextBuilder(). + setLocation(location). + setUploadDirectory(context.getEnvironment().getPdsPrepareUploadFolderDirectory()). + build(); + /* @formatter:on */ + + skopeo.download(skopeoContext); + + SecHubMessage message = new SecHubMessage(SecHubMessageType.INFO, "Download public image: " + location); + context.getUserMessages().add(message); + } +} diff --git a/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/skopeo/SkopeoContext.java b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/skopeo/SkopeoContext.java new file mode 100644 index 0000000000..4a392ef92b --- /dev/null +++ b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/skopeo/SkopeoContext.java @@ -0,0 +1,38 @@ +package com.mercedesbenz.sechub.wrapper.prepare.modules.skopeo; + +import com.mercedesbenz.sechub.wrapper.prepare.modules.ToolContext; + +public class SkopeoContext extends ToolContext { + + private final String filename; + + private SkopeoContext(SkopeoContextBuilder builder) { + super(builder); + this.filename = builder.filename; + } + + public String getFilename() { + return filename; + } + + public static class SkopeoContextBuilder extends ToolContextBuilder { + + private String filename = "SkopeoDownloadFile.tar"; + + @Override + public SkopeoContext build() { + return new SkopeoContext(this); + } + + public SkopeoContextBuilder setFilename(String filename) { + if (filename == null || filename.isBlank()) { + return this; + } + if (!filename.endsWith(".tar")) { + throw new IllegalArgumentException("Filename must end with .tar"); + } + this.filename = filename; + return this; + } + } +} diff --git a/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/skopeo/SkopeoInputValidator.java b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/skopeo/SkopeoInputValidator.java new file mode 100644 index 0000000000..6ba219ab2d --- /dev/null +++ b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/skopeo/SkopeoInputValidator.java @@ -0,0 +1,25 @@ +package com.mercedesbenz.sechub.wrapper.prepare.modules.skopeo; + +import java.util.regex.Pattern; + +import org.springframework.stereotype.Component; + +import com.mercedesbenz.sechub.commons.model.*; +import com.mercedesbenz.sechub.wrapper.prepare.modules.AbstractInputValidator; + +@Component +public class SkopeoInputValidator extends AbstractInputValidator { + + public static final String TYPE = "docker"; + private static final String SKOPEO_LOCATION_REGEX = "((docker://|https://)?([a-zA-Z0-9-_.].[a-zA-Z0-9-_.]/)?[a-zA-Z0-9-_.]+(:[a-zA-Z0-9-_.]+)?(/)?)+(@sha256:[a-f0-9]{64})?"; + private static final Pattern SKOPEO_LOCATION_PATTERN = Pattern.compile(SKOPEO_LOCATION_REGEX); + private static final String SKOPEO_USERNAME_REGEX = "^[a-zA-Z0-9-_\\d](?:[a-zA-Z0-9-_\\d]|(?=[a-zA-Z0-9-_\\d])){0,38}$"; + private static final Pattern SKOPEO_USERNAME_PATTERN = Pattern.compile(SKOPEO_USERNAME_REGEX); + private static final String SKOPEO_PASSWORD_REGEX = "^[a-zA-Z0-9-_\\d]{0,80}$"; + private static final Pattern SKOPEO_PASSWORD_PATTERN = Pattern.compile(SKOPEO_PASSWORD_REGEX); + + public SkopeoInputValidator() { + super(TYPE, SKOPEO_LOCATION_PATTERN, SKOPEO_USERNAME_PATTERN, SKOPEO_PASSWORD_PATTERN); + } + +} diff --git a/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/skopeo/WrapperSkopeo.java b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/skopeo/WrapperSkopeo.java new file mode 100644 index 0000000000..c1524a19d0 --- /dev/null +++ b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/skopeo/WrapperSkopeo.java @@ -0,0 +1,159 @@ +package com.mercedesbenz.sechub.wrapper.prepare.modules.skopeo; + +import static com.mercedesbenz.sechub.wrapper.prepare.cli.PrepareWrapperEnvironmentVariables.*; +import static com.mercedesbenz.sechub.wrapper.prepare.cli.PrepareWrapperKeyConstants.KEY_PDS_PREPARE_AUTHENTICATION_FILE_SKOPEO; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import com.mercedesbenz.sechub.commons.core.security.CryptoAccess; +import com.mercedesbenz.sechub.commons.pds.PDSProcessAdapterFactory; +import com.mercedesbenz.sechub.commons.pds.ProcessAdapter; +import com.mercedesbenz.sechub.wrapper.prepare.modules.WrapperTool; + +@Component +public class WrapperSkopeo extends WrapperTool { + + @Value("${" + KEY_PDS_PREPARE_AUTHENTICATION_FILE_SKOPEO + ":authentication.json}") + String pdsPrepareAuthenticationFileSkopeo; + + @Autowired + PDSProcessAdapterFactory processAdapterFactory; + + public void download(SkopeoContext context) throws IOException { + if (!context.getCredentialMap().isEmpty()) { + login(context); + } + + ProcessBuilder builder = buildProcessDownload(context); + ProcessAdapter process = null; + + try { + process = processAdapterFactory.startProcess(builder); + } catch (IOException e) { + throw new IOException("Error while download with Skopeo from: " + context.getLocation(), e); + } + + waitForProcessToFinish(process); + } + + @Override + public void cleanUploadDirectory(String uploadDirectory) throws IOException { + ProcessBuilder builder = buildProcessClean(uploadDirectory); + ProcessAdapter process = null; + + try { + process = processAdapterFactory.startProcess(builder); + } catch (IOException e) { + throw new IOException("Error while cleaning authentication file.", e); + } + + waitForProcessToFinish(process); + } + + private void login(SkopeoContext context) throws IOException { + ProcessBuilder builder = buildProcessLogin(context); + ProcessAdapter process = null; + + try { + process = processAdapterFactory.startProcess(builder); + } catch (IOException e) { + throw new IOException("Error while login with Skopeo to: " + context.getLocation(), e); + } + + waitForProcessToFinish(process); + } + + private ProcessBuilder buildProcessLogin(SkopeoContext context) { + List commands = new ArrayList<>(); + + String location = transformLocationForLogin(context.getLocation()); + File uploadDir = Paths.get(context.getUploadDirectory()).toAbsolutePath().toFile(); + + commands.add("skopeo"); + commands.add("login"); + commands.add(location); + commands.add("--username"); + commands.add(CryptoAccess.CRYPTO_STRING.unseal(context.getCredentialMap().get(PDS_PREPARE_CREDENTIAL_USERNAME))); + commands.add("--password"); + commands.add(CryptoAccess.CRYPTO_STRING.unseal(context.getCredentialMap().get(PDS_PREPARE_CREDENTIAL_PASSWORD))); + commands.add("--authfile"); + commands.add(pdsPrepareAuthenticationFileSkopeo); + + ProcessBuilder builder = new ProcessBuilder(commands); + builder.directory(uploadDir); + builder.inheritIO(); + + return builder; + } + + private ProcessBuilder buildProcessDownload(SkopeoContext context) { + List commands = new ArrayList<>(); + + String location = transformLocationForDownload(context.getLocation()); + File uploadDir = Paths.get(context.getUploadDirectory()).toAbsolutePath().toFile(); + + commands.add("skopeo"); + commands.add("copy"); + commands.add(location); + commands.add("docker-archive:" + context.getFilename()); + if (!context.getCredentialMap().isEmpty()) { + commands.add("--authfile"); + commands.add(pdsPrepareAuthenticationFileSkopeo); + } + + ProcessBuilder builder = new ProcessBuilder(commands); + builder.directory(uploadDir); + builder.inheritIO(); + + return builder; + } + + private ProcessBuilder buildProcessClean(String pdsPrepareUploadFolderDirectory) { + // removes authentication file + List commands = new ArrayList<>(); + + File uploadDir = Paths.get(pdsPrepareUploadFolderDirectory).toAbsolutePath().toFile(); + + commands.add("rm"); + commands.add("-rf"); + commands.add(pdsPrepareAuthenticationFileSkopeo); + + ProcessBuilder builder = new ProcessBuilder(commands); + builder.directory(uploadDir); + builder.inheritIO(); + return builder; + } + + private String transformLocationForDownload(String location) { + String dockerPrefix = "docker://"; + + if (location.startsWith(dockerPrefix)) { + return location; + } + + if (location.startsWith("https://")) { + location = location.replace("https://", dockerPrefix); + return location; + } + + return dockerPrefix + location; + } + + private String transformLocationForLogin(String location) { + if (location.startsWith("docker://")) { + location = location.replace("docker://", ""); + } + if (location.startsWith("https://")) { + location = location.replace("https://", ""); + } + return location.split("/")[0]; + } +} diff --git a/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/prepare/PrepareWrapperContext.java b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/prepare/PrepareWrapperContext.java index fa72948a2d..c325d3c117 100644 --- a/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/prepare/PrepareWrapperContext.java +++ b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/prepare/PrepareWrapperContext.java @@ -13,20 +13,16 @@ public class PrepareWrapperContext { private SecHubConfigurationModel secHubConfiguration; private PrepareWrapperEnvironment environment; - private List remoteDataConfigurationList = new ArrayList<>(); private List userMessages = new ArrayList<>(); + private SecHubRemoteDataConfiguration remoteDataConfiguration; public PrepareWrapperContext(SecHubConfigurationModel secHubConfiguration, PrepareWrapperEnvironment environment) { this.secHubConfiguration = secHubConfiguration; this.environment = environment; } - public void setRemoteDataConfigurationList(List remoteDataConfigurationList) { - this.remoteDataConfigurationList = remoteDataConfigurationList; - } - - public List getRemoteDataConfigurationList() { - return remoteDataConfigurationList; + public void setRemoteDataConfiguration(SecHubRemoteDataConfiguration remoteDataConfiguration) { + this.remoteDataConfiguration = remoteDataConfiguration; } public SecHubConfigurationModel getSecHubConfiguration() { @@ -40,4 +36,8 @@ public PrepareWrapperEnvironment getEnvironment() { public List getUserMessages() { return userMessages; } + + public SecHubRemoteDataConfiguration getRemoteDataConfiguration() { + return remoteDataConfiguration; + } } diff --git a/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/prepare/PrepareWrapperContextFactory.java b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/prepare/PrepareWrapperContextFactory.java index b266b4fb18..afd2a72e76 100644 --- a/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/prepare/PrepareWrapperContextFactory.java +++ b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/prepare/PrepareWrapperContextFactory.java @@ -37,6 +37,6 @@ private SecHubConfigurationModel createSecHubConfigModel(String json) { } private void addRemoteDataConfiguration(PrepareWrapperContext context) { - context.setRemoteDataConfigurationList(extractor.extract(context.getSecHubConfiguration())); + context.setRemoteDataConfiguration(extractor.extract(context.getSecHubConfiguration())); } } diff --git a/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/prepare/PrepareWrapperPreparationService.java b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/prepare/PrepareWrapperPreparationService.java index 7095f46f0a..bd5f684ed3 100644 --- a/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/prepare/PrepareWrapperPreparationService.java +++ b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/prepare/PrepareWrapperPreparationService.java @@ -34,32 +34,25 @@ public class PrepareWrapperPreparationService { public AdapterExecutionResult startPreparation() throws IOException { - boolean atLeastOneModuleExecuted = false; - LOG.debug("Start preparation"); PrepareWrapperContext context = factory.create(environment); - List remoteDataConfigurationList = context.getRemoteDataConfigurationList(); + SecHubRemoteDataConfiguration remoteDataConfiguration = context.getRemoteDataConfiguration(); - if (remoteDataConfigurationList.isEmpty()) { + if (remoteDataConfiguration == null) { LOG.warn("No Remote configuration was found"); return createAdapterExecutionResult(PrepareStatus.OK, SecHubMessageType.WARNING, "No Remote Configuration found."); } for (PrepareWrapperModule module : modules) { - if (!module.isAbleToPrepare(context)) { - continue; - } - atLeastOneModuleExecuted = true; - context.getUserMessages().add(new SecHubMessage(SecHubMessageType.INFO, "Execute prepare module: " + module.getClass().getSimpleName())); - module.prepare(context); - } - if (!atLeastOneModuleExecuted) { - return createAdapterExecutionResult(PrepareStatus.FAILED, SecHubMessageType.ERROR, "No module was able to prepare the defined remote data."); + if (module.prepare(context)) { + context.getUserMessages().add(new SecHubMessage(SecHubMessageType.INFO, "Executed prepare module: " + module.getClass().getSimpleName())); + PrepareResult result = new PrepareResult(PrepareStatus.OK); + return new AdapterExecutionResult(result.toString(), context.getUserMessages()); + } } - PrepareResult result = new PrepareResult(PrepareStatus.OK); - return new AdapterExecutionResult(result.toString(), context.getUserMessages()); + return createAdapterExecutionResult(PrepareStatus.FAILED, SecHubMessageType.ERROR, "No module was able to prepare the defined remote data."); } private AdapterExecutionResult createAdapterExecutionResult(PrepareStatus status, SecHubMessageType type, String message) { diff --git a/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/prepare/PrepareWrapperRemoteConfigurationExtractor.java b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/prepare/PrepareWrapperRemoteConfigurationExtractor.java index 99a33b0444..d094270651 100644 --- a/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/prepare/PrepareWrapperRemoteConfigurationExtractor.java +++ b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/prepare/PrepareWrapperRemoteConfigurationExtractor.java @@ -13,7 +13,15 @@ @Component public class PrepareWrapperRemoteConfigurationExtractor { - public List extract(SecHubConfigurationModel model) { + /** + * Extracts the remote data configuration from the SecHub configuration model. + * The method returns nul, if no remote configuration could be found. If more + * than one remote configuration is found, an exception is thrown. + * + * @param model sechub configuration model + * @return remote data configuration + */ + public SecHubRemoteDataConfiguration extract(SecHubConfigurationModel model) { List remoteDataConfigurationList = new ArrayList<>(); if (model == null) { throw new IllegalStateException("Context was not initialized correctly. SecHub configuration was null"); @@ -33,7 +41,13 @@ public List extract(SecHubConfigurationModel mode remoteOpt.ifPresent(remoteDataConfigurationList::add); } } - return remoteDataConfigurationList; + if (remoteDataConfigurationList.isEmpty()) { + return null; + } else if (remoteDataConfigurationList.size() > 1) { + throw new IllegalStateException("Only one remote data configuration is allowed."); + } else { + return remoteDataConfigurationList.get(0); + } } } diff --git a/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/PrepareWrapperApplicationSpringBootTest.java b/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/PrepareWrapperApplicationSpringBootTest.java index 55c5c4ae9b..c1122d93d5 100644 --- a/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/PrepareWrapperApplicationSpringBootTest.java +++ b/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/PrepareWrapperApplicationSpringBootTest.java @@ -18,6 +18,13 @@ import com.mercedesbenz.sechub.wrapper.prepare.factory.PrepareWrapperPDSUserMessageSupportPojoFactory; import com.mercedesbenz.sechub.wrapper.prepare.factory.PrepareWrapperPojoFactory; import com.mercedesbenz.sechub.wrapper.prepare.modules.*; +import com.mercedesbenz.sechub.wrapper.prepare.modules.git.GitInputValidator; +import com.mercedesbenz.sechub.wrapper.prepare.modules.git.JGitAdapter; +import com.mercedesbenz.sechub.wrapper.prepare.modules.git.PrepareWrapperModuleGit; +import com.mercedesbenz.sechub.wrapper.prepare.modules.git.WrapperGit; +import com.mercedesbenz.sechub.wrapper.prepare.modules.skopeo.PrepareWrapperModuleSkopeo; +import com.mercedesbenz.sechub.wrapper.prepare.modules.skopeo.SkopeoInputValidator; +import com.mercedesbenz.sechub.wrapper.prepare.modules.skopeo.WrapperSkopeo; import com.mercedesbenz.sechub.wrapper.prepare.prepare.PrepareWrapperContextFactory; import com.mercedesbenz.sechub.wrapper.prepare.prepare.PrepareWrapperPreparationService; import com.mercedesbenz.sechub.wrapper.prepare.prepare.PrepareWrapperRemoteConfigurationExtractor; @@ -26,9 +33,9 @@ @SpringBootTest(classes = { PrepareWrapperContextFactory.class, PrepareWrapperPreparationService.class, PrepareWrapperPojoFactory.class, PrepareWrapperEnvironment.class, PrepareWrapperPDSUserMessageSupportPojoFactory.class, PrepareWrapperRemoteConfigurationExtractor.class, PrepareWrapperModuleGit.class, PrepareWrapperModule.class, WrapperGit.class, GitInputValidator.class, JGitAdapter.class, - PrepareWrapperStorageService.class, PrepareWrapperUploadService.class, PrepareWrapperFileUploadService.class, - PrepareWrapperSechubConfigurationSupport.class, FileNameSupport.class, PrepareWrapperArchiveCreator.class, ArchiveSupport.class, - PrepareWrapperSharedVolumePropertiesSetup.class, PrepareWrapperS3PropertiesSetup.class }) + PrepareWrapperModuleSkopeo.class, WrapperSkopeo.class, SkopeoInputValidator.class, PrepareWrapperStorageService.class, + PrepareWrapperUploadService.class, PrepareWrapperFileUploadService.class, PrepareWrapperSechubConfigurationSupport.class, FileNameSupport.class, + PrepareWrapperArchiveCreator.class, ArchiveSupport.class, PrepareWrapperSharedVolumePropertiesSetup.class, PrepareWrapperS3PropertiesSetup.class }) @ExtendWith(SpringExtension.class) @TestPropertySource(locations = "classpath:application-test-fail.properties") class PrepareWrapperApplicationSpringBootTest { diff --git a/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/AbstractInputValidatorTest.java b/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/AbstractInputValidatorTest.java new file mode 100644 index 0000000000..1c825a9666 --- /dev/null +++ b/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/AbstractInputValidatorTest.java @@ -0,0 +1,110 @@ +package com.mercedesbenz.sechub.wrapper.prepare.modules; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.regex.Pattern; + +import org.junit.jupiter.api.Test; + +class AbstractInputValidatorTest { + + TestInputValidator validatorToTest; + + @Test + void constructor_throws_exception_when_type_is_null() { + /* prepare */ + Pattern locationPattern = Pattern.compile(".*"); + Pattern usernamePattern = Pattern.compile(".*"); + Pattern passwordPattern = Pattern.compile(".*"); + + /* execute */ + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { + validatorToTest = new TestInputValidator(null, locationPattern, usernamePattern, passwordPattern); + }); + + /* test */ + assertEquals("Type must not be null or empty.", exception.getMessage()); + } + + @Test + void constructor_throws_exception_when_type_is_empty() { + /* prepare */ + Pattern locationPattern = Pattern.compile(".*"); + Pattern usernamePattern = Pattern.compile(".*"); + Pattern passwordPattern = Pattern.compile(".*"); + + /* execute */ + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { + validatorToTest = new TestInputValidator("", locationPattern, usernamePattern, passwordPattern); + }); + + /* test */ + assertEquals("Type must not be null or empty.", exception.getMessage()); + } + + @Test + void constructor_throws_exception_when_locationPattern_is_null() { + /* prepare */ + Pattern usernamePattern = Pattern.compile(".*"); + Pattern passwordPattern = Pattern.compile(".*"); + + /* execute */ + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { + validatorToTest = new TestInputValidator("type", null, usernamePattern, passwordPattern); + }); + + /* test */ + assertEquals("Pattern must not be null.", exception.getMessage()); + } + + @Test + void constructor_throws_exception_when_usernamePattern_is_null() { + /* prepare */ + Pattern locationPattern = Pattern.compile(".*"); + Pattern passwordPattern = Pattern.compile(".*"); + + /* execute */ + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { + validatorToTest = new TestInputValidator("type", locationPattern, null, passwordPattern); + }); + + /* test */ + assertEquals("Pattern must not be null.", exception.getMessage()); + } + + @Test + void constructor_throws_exception_when_passwordPattern_is_null() { + /* prepare */ + Pattern locationPattern = Pattern.compile(".*"); + Pattern usernamePattern = Pattern.compile(".*"); + + /* execute */ + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { + validatorToTest = new TestInputValidator("type", locationPattern, usernamePattern, null); + }); + + /* test */ + assertEquals("Pattern must not be null.", exception.getMessage()); + } + + @Test + void constructor_creates_instance_when_all_parameters_are_valid() { + /* prepare */ + Pattern locationPattern = Pattern.compile(".*"); + Pattern usernamePattern = Pattern.compile(".*"); + Pattern passwordPattern = Pattern.compile(".*"); + + /* execute */ + validatorToTest = new TestInputValidator("type", locationPattern, usernamePattern, passwordPattern); + + /* test */ + assertNotNull(validatorToTest); + } + + private class TestInputValidator extends AbstractInputValidator { + public TestInputValidator(String type, Pattern locationPattern, Pattern usernamePattern, Pattern passwordPattern) { + super(type, locationPattern, usernamePattern, passwordPattern); + } + } + +} \ No newline at end of file diff --git a/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/GitInputValidatorTest.java b/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/GitInputValidatorTest.java deleted file mode 100644 index 3267a81eab..0000000000 --- a/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/GitInputValidatorTest.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.mercedesbenz.sechub.wrapper.prepare.modules; - -import static org.junit.jupiter.api.Assertions.*; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; - -class GitInputValidatorTest { - - GitInputValidator gitInputValidatorToTest; - - PrepareWrapperModuleGit gitModule; - - @BeforeEach - void beforeEach() { - gitInputValidatorToTest = new GitInputValidator(); - gitModule = new PrepareWrapperModuleGit(); - } - - @ParameterizedTest - @ValueSource(strings = { "https://example.com;echoMalicious", "https://example.com.git>text.txt", "https://example.com/some-git-repo.git&&cd.." }) - void validateURL_throws_exception_when_url_does_contain_forbidden_characters(String repositoryUrl) { - /* execute */ - IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, - () -> gitInputValidatorToTest.validateLocationCharacters(repositoryUrl, null)); - - /* test */ - assertTrue(exception.getMessage().contains("Defined URL must not contain forbidden characters: ")); - } - - @ParameterizedTest - @ValueSource(strings = { "https://y-git-host/my-git-user/my-git-repo.git", "https://example.org/some-git-repo.git", "git@gitrepo:my-repo" }) - void validateURL_does_not_throw_exception_when_url_is_valid(String repositoryUrl) { - /* execute + test */ - assertDoesNotThrow(() -> gitInputValidatorToTest.validateLocationCharacters(repositoryUrl, null)); - } - - @ParameterizedTest - @ValueSource(strings = { "https://my-repo.com.git", "http://my-repo.com.git", "git://host.xz/~user/path/to/repo.git", "git@github.com:my/repo.git", - "https://host.xz/path/to/repo.git/", "http://host.xz/path/to/repo.git/", "git://host.xz/path/to/repo.git/", "git@host.com:my-repo/example.git" }) - void escapeLocation_returns_true_when_git_pattern_is_configured(String location) { - /* execute */ - boolean result = gitInputValidatorToTest.validateLocation(location); - - /* test */ - assertTrue(result); - } - - @ParameterizedTest - @ValueSource(strings = { "https://my-repo.com.notAgit_repo", "http://my-repo.com.git;./bin/bash", - "git://host.xz/~user/path/to/repo.git some eval execution", "git@github.com:my/repo.git\nexecuteMalicious" }) - void escapeLocation__returns_false_when_invalid_git_pattern_is_configured(String location) { - /* execute */ - boolean result = gitInputValidatorToTest.validateLocation(location); - - /* test */ - assertFalse(result); - } - - @ParameterizedTest - @ValueSource(strings = { "user", "user-name", "user_name", "user-name-123", "user-name-123-456", "user-name_23" }) - void escapeUsername_does_not_throw_exception_when_username_is_valid(String username) { - /* execute + test */ - assertDoesNotThrow(() -> gitInputValidatorToTest.validateUsername(username)); - } - - @ParameterizedTest - @ValueSource(strings = { "user name", "user name 123", "./bin/bash" }) - void escapeUsername_throws_exception_when_username_is_invalid(String username) { - /* execute + test */ - IllegalStateException exception = assertThrows(IllegalStateException.class, () -> gitInputValidatorToTest.validateUsername(username)); - assertEquals("Defined username must match the modules pattern.", exception.getMessage()); - } - - @ParameterizedTest - @ValueSource(strings = { "ghp_123456789012345678901234567890123456", "ghs_123456789012345678901234567890123456", - "github_pat_1234567890123456789012_1234567890123456789012345678901234567890123456789012example" }) - void escapePassword_does_not_throw_exception_when_password_is_valid(String password) { - /* execute + test */ - assertDoesNotThrow(() -> gitInputValidatorToTest.validatePassword(password)); - } - - @ParameterizedTest - @ValueSource(strings = { "./bin/bash", "ghp_1234567890123456789012345678901234567", "ghs_1234567890123456789012345678901234567", - "github_pat_123456789012345678901234567890123456_;echo 'malicious'" }) - void escapePassword_throws_exception_when_password_is_invalid(String password) { - /* execute + test */ - IllegalStateException exception = assertThrows(IllegalStateException.class, () -> gitInputValidatorToTest.validatePassword(password)); - assertEquals("Defined password must match the Git Api token pattern.", exception.getMessage()); - } - -} \ No newline at end of file diff --git a/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/PrepareWrapperModuleGitTest.java b/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/PrepareWrapperModuleGitTest.java deleted file mode 100644 index c6a7e96f95..0000000000 --- a/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/PrepareWrapperModuleGitTest.java +++ /dev/null @@ -1,329 +0,0 @@ -package com.mercedesbenz.sechub.wrapper.prepare.modules; - -import static com.mercedesbenz.sechub.commons.model.SecHubScanConfiguration.createFromJSON; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.util.ArrayList; -import java.util.List; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; -import org.springframework.test.util.ReflectionTestUtils; - -import com.mercedesbenz.sechub.commons.model.SecHubConfigurationModel; -import com.mercedesbenz.sechub.commons.model.SecHubRemoteCredentialConfiguration; -import com.mercedesbenz.sechub.commons.model.SecHubRemoteCredentialUserData; -import com.mercedesbenz.sechub.commons.model.SecHubRemoteDataConfiguration; -import com.mercedesbenz.sechub.test.TestFileWriter; -import com.mercedesbenz.sechub.wrapper.prepare.cli.PrepareWrapperEnvironment; -import com.mercedesbenz.sechub.wrapper.prepare.prepare.PrepareWrapperContext; -import com.mercedesbenz.sechub.wrapper.prepare.upload.FileNameSupport; -import com.mercedesbenz.sechub.wrapper.prepare.upload.PrepareWrapperUploadService; - -class PrepareWrapperModuleGitTest { - - PrepareWrapperModuleGit moduleToTest; - - WrapperGit git; - - TestFileWriter writer; - - GitInputValidator gitInputValidator; - - PrepareWrapperUploadService uploadService; - - FileNameSupport filesSupport; - - String subfolder = "subfolder"; - - @BeforeEach - void beforeEach() throws IOException { - moduleToTest = new PrepareWrapperModuleGit(); - writer = new TestFileWriter(); - gitInputValidator = new GitInputValidator(); - git = mock(WrapperGit.class); - uploadService = mock(PrepareWrapperUploadService.class); - filesSupport = mock(FileNameSupport.class); - - when(filesSupport.getSubfolderFileNameFromDirectory(anyString())).thenReturn(subfolder); - - moduleToTest.uploadService = uploadService; - moduleToTest.filesSupport = filesSupport; - moduleToTest.git = git; - moduleToTest.gitInputValidator = gitInputValidator; - } - - @ParameterizedTest - @ValueSource(strings = { "https://host.xz/path/to/notARepo/", "http://my.eval.com", "example.org" }) - void isAbleToPrepare_returns_false_when_no_git_remote_data_was_configured(String location) { - /* prepare */ - String json = """ - { - "apiVersion": "1.0", - "data": { - "sources": [ - { - "name": "remote_example_name", - "remote": { - "location": "$location" - } - } - ] - }, - "codeScan": { - "use": [ - "remote_example_name" - ] - } - } - """.replace("$location", location); - SecHubConfigurationModel model = createFromJSON(json); - PrepareWrapperEnvironment environment = mock(PrepareWrapperEnvironment.class); - PrepareWrapperContext context = new PrepareWrapperContext(model, environment); - - /* execute */ - boolean ableToPrepare = moduleToTest.isAbleToPrepare(context); - - /* test */ - assertFalse(ableToPrepare); - } - - @ParameterizedTest - @ValueSource(strings = { "https://host.xz/path/to/notARepo/", "http://my.eval.com", "example.org", " " }) - void isAbleToPrepare_returns_false_when_git_remote_data_type_was_configured_but_is_not_git_location(String location) { - /* prepare */ - PrepareWrapperContext context = createContext(); - List remoteDataConfigurationList = new ArrayList<>(); - SecHubRemoteDataConfiguration remoteDataConfiguration = new SecHubRemoteDataConfiguration(); - remoteDataConfiguration.setLocation(location); - remoteDataConfiguration.setType("git"); - remoteDataConfigurationList.add(remoteDataConfiguration); - - context.setRemoteDataConfigurationList(remoteDataConfigurationList); - - /* execute */ - boolean ableToPrepare = moduleToTest.isAbleToPrepare(context); - - /* test */ - assertFalse(ableToPrepare); - } - - @Test - void isAbleToPrepare_returns_false_when_configuration_is_empty() { - /* prepare */ - PrepareWrapperContext context = createContext(); - List remoteDataConfigurationList = new ArrayList<>(); - context.setRemoteDataConfigurationList(remoteDataConfigurationList); - - /* execute */ - boolean ableToPrepare = moduleToTest.isAbleToPrepare(context); - - /* test */ - assertFalse(ableToPrepare); - } - - @ParameterizedTest - @ValueSource(strings = { "https://host.xz/path/to/repo.git/", "http://host.xz/path/to/repo.git/", "git://host.xz/path/to/repo.git/", - "git@host.com:my-repo/example.git" }) - void isAbleToPrepare_returns_true_when_git_remote_location_was_configured(String location) { - /* prepare */ - PrepareWrapperContext context = createContext(); - List remoteDataConfigurationList = new ArrayList<>(); - SecHubRemoteDataConfiguration remoteDataConfiguration = new SecHubRemoteDataConfiguration(); - remoteDataConfiguration.setLocation(location); - remoteDataConfiguration.setType(""); - remoteDataConfigurationList.add(remoteDataConfiguration); - context.setRemoteDataConfigurationList(remoteDataConfigurationList); - ReflectionTestUtils.setField(moduleToTest, "pdsPrepareModuleGitEnabled", true); - - /* execute */ - boolean ableToPrepare = moduleToTest.isAbleToPrepare(context); - - /* test */ - assertTrue(ableToPrepare); - } - - @Test - void prepare_throws_exception_when_credentials_are_empty() { - /* prepare */ - PrepareWrapperContext context = createContext(); - List remoteDataConfigurationList = new ArrayList<>(); - SecHubRemoteDataConfiguration remoteDataConfiguration = new SecHubRemoteDataConfiguration(); - SecHubRemoteCredentialConfiguration credentials = new SecHubRemoteCredentialConfiguration(); - remoteDataConfiguration.setCredentials(credentials); - remoteDataConfiguration.setLocation("my-example-location"); - remoteDataConfiguration.setType("git"); - remoteDataConfigurationList.add(remoteDataConfiguration); - context.setRemoteDataConfigurationList(remoteDataConfigurationList); - ReflectionTestUtils.setField(moduleToTest, "pdsPrepareModuleGitEnabled", true); - - /* execute */ - IllegalStateException exception = assertThrows(IllegalStateException.class, () -> moduleToTest.prepare(context)); - - /* test */ - assertTrue(exception.getMessage().contains("Defined credentials have no credential")); - } - - @Test - void prepare_throws_exception_when_no_username_found() { - /* prepare */ - PrepareWrapperContext context = createContext(); - List remoteDataConfigurationList = new ArrayList<>(); - SecHubRemoteDataConfiguration remoteDataConfiguration = new SecHubRemoteDataConfiguration(); - SecHubRemoteCredentialConfiguration credentials = new SecHubRemoteCredentialConfiguration(); - SecHubRemoteCredentialUserData user = new SecHubRemoteCredentialUserData(); - user.setPassword("my-example-password"); - credentials.setUser(user); - remoteDataConfiguration.setCredentials(credentials); - remoteDataConfiguration.setLocation("my-example-location"); - remoteDataConfiguration.setType("git"); - remoteDataConfigurationList.add(remoteDataConfiguration); - context.setRemoteDataConfigurationList(remoteDataConfigurationList); - - /* execute */ - IllegalStateException exception = assertThrows(IllegalStateException.class, () -> moduleToTest.prepare(context)); - - /* test */ - assertTrue(exception.getMessage().contains("Defined username must not be null or empty.")); - } - - @Test - void prepare_throws_exception_when_no_password_found() { - /* prepare */ - PrepareWrapperContext context = createContext(); - List remoteDataConfigurationList = new ArrayList<>(); - SecHubRemoteDataConfiguration remoteDataConfiguration = new SecHubRemoteDataConfiguration(); - SecHubRemoteCredentialConfiguration credentials = new SecHubRemoteCredentialConfiguration(); - SecHubRemoteCredentialUserData user = new SecHubRemoteCredentialUserData(); - user.setName("my-example-name"); - credentials.setUser(user); - remoteDataConfiguration.setCredentials(credentials); - remoteDataConfiguration.setLocation("my-example-location"); - remoteDataConfiguration.setType("git"); - remoteDataConfigurationList.add(remoteDataConfiguration); - context.setRemoteDataConfigurationList(remoteDataConfigurationList); - - /* execute */ - IllegalStateException exception = assertThrows(IllegalStateException.class, () -> moduleToTest.prepare(context)); - - /* test */ - assertTrue(exception.getMessage().contains("Defined password must not be null or empty.")); - } - - @Test - void prepare_successful_when_user_credentials_are_configured_correctly() throws IOException { - /* prepare */ - File tempDir = Files.createTempDirectory("upload-folder").toFile(); - tempDir.deleteOnExit(); - String filename = "/" + subfolder + "/.git"; - writer.save(new File(tempDir, filename), "some text", true); - - PrepareWrapperEnvironment environment = mock(PrepareWrapperEnvironment.class); - when(environment.getPdsPrepareUploadFolderDirectory()).thenReturn(tempDir.toString()); - PrepareWrapperContext context = new PrepareWrapperContext(createFromJSON("{}"), environment); - - List remoteDataConfigurationList = new ArrayList<>(); - SecHubRemoteDataConfiguration remoteDataConfiguration = new SecHubRemoteDataConfiguration(); - SecHubRemoteCredentialConfiguration credentials = new SecHubRemoteCredentialConfiguration(); - SecHubRemoteCredentialUserData user = new SecHubRemoteCredentialUserData(); - user.setName("my-example-name"); - user.setPassword("ghp_exampleAPITOKEN8ffne3l6g9f393r8fbcsf"); - credentials.setUser(user); - remoteDataConfiguration.setCredentials(credentials); - remoteDataConfiguration.setLocation("my-example-location"); - remoteDataConfiguration.setType("git"); - remoteDataConfigurationList.add(remoteDataConfiguration); - context.setRemoteDataConfigurationList(remoteDataConfigurationList); - - ReflectionTestUtils.setField(moduleToTest, "pdsPrepareModuleGitEnabled", true); - - /* execute */ - moduleToTest.prepare(context); - - /* test */ - verify(git).downloadRemoteData(any(GitContext.class)); - } - - @Test - void prepare_successful_when_no_credentials_are_configured() throws IOException { - /* prepare */ - File tempDir = Files.createTempDirectory("upload-folder").toFile(); - tempDir.deleteOnExit(); - String filename = "/" + subfolder + "/.git"; - writer.save(new File(tempDir, filename), "some text", true); - - PrepareWrapperEnvironment environment = mock(PrepareWrapperEnvironment.class); - when(environment.getPdsPrepareUploadFolderDirectory()).thenReturn(tempDir.toString()); - PrepareWrapperContext context = new PrepareWrapperContext(createFromJSON("{}"), environment); - - List remoteDataConfigurationList = new ArrayList<>(); - SecHubRemoteDataConfiguration remoteDataConfiguration = new SecHubRemoteDataConfiguration(); - remoteDataConfiguration.setLocation("my-example-location"); - remoteDataConfiguration.setType("git"); - remoteDataConfigurationList.add(remoteDataConfiguration); - context.setRemoteDataConfigurationList(remoteDataConfigurationList); - - /* execute */ - moduleToTest.prepare(context); - - /* test */ - verify(git).downloadRemoteData(any(GitContext.class)); - } - - @Test - void isDownloadSuccessful_returns_true_when_git_file_in_directory() throws IOException { - /* prepare */ - File tempDir = Files.createTempDirectory("upload-folder").toFile(); - tempDir.deleteOnExit(); - String filename = "/" + subfolder + "/.git"; - PrepareWrapperContext context = mock(PrepareWrapperContext.class); - when(context.getEnvironment()).thenReturn(mock(PrepareWrapperEnvironment.class)); - writer.save(new File(tempDir, filename), "some text", true); - when(context.getEnvironment().getPdsPrepareUploadFolderDirectory()).thenReturn(tempDir.toString()); - - /* execute */ - boolean result = moduleToTest.isDownloadSuccessful(context); - - /* test */ - assertTrue(result); - } - - @Test - void isDownloadSuccessful_returns_false_when_no_git_file_in_directory() throws IOException { - /* prepare */ - File tempDir = Files.createTempDirectory("upload-folder").toFile(); - tempDir.deleteOnExit(); - writer.save(tempDir, "some text", true); - PrepareWrapperContext context = mock(PrepareWrapperContext.class); - when(context.getEnvironment()).thenReturn(mock(PrepareWrapperEnvironment.class)); - when(context.getEnvironment().getPdsPrepareUploadFolderDirectory()).thenReturn(tempDir.toString()); - - /* execute */ - boolean result = moduleToTest.isDownloadSuccessful(context); - - /* test */ - assertFalse(result); - } - - @ParameterizedTest - @ValueSource(strings = { "git", "GIT", "gIT" }) - void isMatchingGitType_returns_true_when_git_is_configured(String type) { - /* execute */ - boolean result = moduleToTest.isMatchingGitType(type); - - /* test */ - assertTrue(result); - } - - private PrepareWrapperContext createContext() { - PrepareWrapperEnvironment environment = mock(PrepareWrapperEnvironment.class); - when(environment.getPdsPrepareUploadFolderDirectory()).thenReturn("test-upload-folder"); - return new PrepareWrapperContext(createFromJSON("{}"), environment); - } -} \ No newline at end of file diff --git a/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/git/GitInputValidatorTest.java b/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/git/GitInputValidatorTest.java new file mode 100644 index 0000000000..55609e8978 --- /dev/null +++ b/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/git/GitInputValidatorTest.java @@ -0,0 +1,225 @@ +package com.mercedesbenz.sechub.wrapper.prepare.modules.git; + +import static com.mercedesbenz.sechub.commons.model.SecHubScanConfiguration.createFromJSON; +import static com.mercedesbenz.sechub.wrapper.prepare.modules.InputValidatorExitcode.LOCATION_NOT_MATCHING_PATTERN; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import com.mercedesbenz.sechub.commons.model.SecHubRemoteCredentialConfiguration; +import com.mercedesbenz.sechub.commons.model.SecHubRemoteCredentialUserData; +import com.mercedesbenz.sechub.commons.model.SecHubRemoteDataConfiguration; +import com.mercedesbenz.sechub.test.TestFileWriter; +import com.mercedesbenz.sechub.wrapper.prepare.cli.PrepareWrapperEnvironment; +import com.mercedesbenz.sechub.wrapper.prepare.modules.PrepareWrapperInputValidatorException; +import com.mercedesbenz.sechub.wrapper.prepare.prepare.PrepareWrapperContext; + +class GitInputValidatorTest { + + GitInputValidator gitInputValidatorToTest; + + TestFileWriter writer; + + @BeforeEach + void beforeEach() { + writer = new TestFileWriter(); + gitInputValidatorToTest = new GitInputValidator(); + } + + @ParameterizedTest + @ValueSource(strings = { "https://example.com;echoMalicious", "https://example.com.git>text.txt", "https://example.com/some-git-repo.git&&cd.." }) + void validateLocation_throws_exception_when_url_does_contain_forbidden_characters(String repositoryUrl) { + /* execute */ + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> gitInputValidatorToTest.validateLocation(repositoryUrl)); + + /* test */ + assertTrue(exception.getMessage().contains("Defined location URL must not contain forbidden characters: ")); + } + + @ParameterizedTest + @ValueSource(strings = { "https://y-git-host/my-git-user/my-git-repo.git", "https://example.org/some-git-repo.git", "git@gitrepo:my-repo.git" }) + void validateLocation_does_not_throw_exception_when_url_is_valid(String repositoryUrl) { + /* execute + test */ + assertDoesNotThrow(() -> gitInputValidatorToTest.validateLocation(repositoryUrl)); + } + + @ParameterizedTest + @ValueSource(strings = { "https://my-repo.com.git", "http://my-repo.com.git", "git://host.xz/~user/path/to/repo.git", "git@github.com:my/repo.git", + "https://host.xz/path/to/repo.git/", "http://host.xz/path/to/repo.git/", "git://host.xz/path/to/repo.git/", "git@host.com:my-repo/example.git" }) + void validateLocation_does_not_throw_exception_when_git_pattern_is_configured(String location) { + /* execute + test */ + assertDoesNotThrow(() -> gitInputValidatorToTest.validateLocation(location)); + } + + @ParameterizedTest + @ValueSource(strings = { "https://my-repo.com.notAgit_repo", "http://my-repo.com.git./bin/bash", "git://host.xz/~user/path/to/repo.gitsomeevalexecution", + "git@github.com:my/repo.git\nexecuteMalicious" }) + void validateLocation_throws_exception_when_invalid_git_location_is_configured(String location) { + /* execute */ + PrepareWrapperInputValidatorException e = assertThrows(PrepareWrapperInputValidatorException.class, + () -> gitInputValidatorToTest.validateLocation(location)); + + /* test */ + assertEquals(LOCATION_NOT_MATCHING_PATTERN, e.getExitCode()); + } + + @ParameterizedTest + @ValueSource(strings = { "user", "user-name", "user_name", "user-name-123", "user-name-123-456", "user-name_23" }) + void validateUsername_does_not_throw_exception_when_username_is_valid(String username) { + /* execute + test */ + assertDoesNotThrow(() -> gitInputValidatorToTest.validateUsername(username)); + } + + @ParameterizedTest + @ValueSource(strings = { "user name", "user name 123", "./bin/bash" }) + void validateUsername_throws_exception_when_username_is_invalid(String username) { + /* execute + test */ + PrepareWrapperInputValidatorException exception = assertThrows(PrepareWrapperInputValidatorException.class, + () -> gitInputValidatorToTest.validateUsername(username)); + assertEquals("Defined username must match the git pattern.", exception.getMessage()); + } + + @ParameterizedTest + @ValueSource(strings = { "ghp_123456789012345678901234567890123456", "ghs_123456789012345678901234567890123456", + "github_pat_1234567890123456789012_1234567890123456789012345678901234567890123456789012example" }) + void validatePassword_does_not_throw_exception_when_password_is_valid(String password) { + /* execute + test */ + assertDoesNotThrow(() -> gitInputValidatorToTest.validatePassword(password)); + } + + @ParameterizedTest + @ValueSource(strings = { "./bin/bash", "ghp_1234567890123456789012345678901234567", "ghs_1234567890123456789012345678901234567", + "github_pat_123456789012345678901234567890123456_;echo 'malicious'" }) + void validatePassword_throws_exception_when_password_is_invalid(String password) { + /* execute + test */ + PrepareWrapperInputValidatorException exception = assertThrows(PrepareWrapperInputValidatorException.class, + () -> gitInputValidatorToTest.validatePassword(password)); + assertEquals("Defined password must match the git Api token pattern.", exception.getMessage()); + } + + @Test + void validate_throws_exception_when_credentials_are_empty() { + /* prepare */ + PrepareWrapperContext context = createContextEmptyConfig(); + SecHubRemoteDataConfiguration remoteDataConfiguration = new SecHubRemoteDataConfiguration(); + SecHubRemoteCredentialConfiguration credentials = new SecHubRemoteCredentialConfiguration(); + remoteDataConfiguration.setCredentials(credentials); + remoteDataConfiguration.setLocation("https://example.com/my-repo.git"); + remoteDataConfiguration.setType("git"); + context.setRemoteDataConfiguration(remoteDataConfiguration); + + /* execute */ + IllegalStateException exception = assertThrows(IllegalStateException.class, () -> gitInputValidatorToTest.validate(context)); + + /* test */ + assertEquals("Defined credentials must contain credential user and can not be empty.", exception.getMessage()); + } + + @Test + void validate_throws_exception_when_no_username_found() { + /* prepare */ + PrepareWrapperContext context = createContextEmptyConfig(); + SecHubRemoteDataConfiguration remoteDataConfiguration = new SecHubRemoteDataConfiguration(); + SecHubRemoteCredentialConfiguration credentials = new SecHubRemoteCredentialConfiguration(); + SecHubRemoteCredentialUserData user = new SecHubRemoteCredentialUserData(); + user.setPassword("password"); + credentials.setUser(user); + remoteDataConfiguration.setCredentials(credentials); + remoteDataConfiguration.setLocation("https://example.com/my-repo.git"); + remoteDataConfiguration.setType("git"); + context.setRemoteDataConfiguration(remoteDataConfiguration); + + /* execute */ + IllegalStateException exception = assertThrows(IllegalStateException.class, () -> gitInputValidatorToTest.validate(context)); + + /* test */ + assertTrue(exception.getMessage().contains("Defined username must not be null or empty.")); + } + + @Test + void validate_throws_exception_when_no_password_found() { + /* prepare */ + PrepareWrapperContext context = createContextEmptyConfig(); + SecHubRemoteDataConfiguration remoteDataConfiguration = new SecHubRemoteDataConfiguration(); + SecHubRemoteCredentialConfiguration credentials = new SecHubRemoteCredentialConfiguration(); + SecHubRemoteCredentialUserData user = new SecHubRemoteCredentialUserData(); + user.setName("my-example-name"); + credentials.setUser(user); + remoteDataConfiguration.setCredentials(credentials); + remoteDataConfiguration.setLocation("https://example.com/my-repo.git"); + remoteDataConfiguration.setType("git"); + context.setRemoteDataConfiguration(remoteDataConfiguration); + + /* execute */ + IllegalStateException exception = assertThrows(IllegalStateException.class, () -> gitInputValidatorToTest.validate(context)); + + /* test */ + assertEquals("Defined password must not be null or empty. Password is required for login.", exception.getMessage()); + + } + + @Test + void validate_successful_when_user_credentials_are_configured_correctly() throws IOException { + /* prepare */ + File tempDir = Files.createTempDirectory("upload-folder").toFile(); + tempDir.deleteOnExit(); + String filename = "test"; + writer.save(new File(tempDir, filename), "some text", true); + + PrepareWrapperEnvironment environment = mock(PrepareWrapperEnvironment.class); + when(environment.getPdsPrepareUploadFolderDirectory()).thenReturn(tempDir.toString()); + PrepareWrapperContext context = new PrepareWrapperContext(createFromJSON("{}"), environment); + + SecHubRemoteDataConfiguration remoteDataConfiguration = new SecHubRemoteDataConfiguration(); + SecHubRemoteCredentialConfiguration credentials = new SecHubRemoteCredentialConfiguration(); + SecHubRemoteCredentialUserData user = new SecHubRemoteCredentialUserData(); + user.setName("my-example-name"); + user.setPassword("ghp_exampleAPITOKEN8ffne3l6g9f393r8fbcsf"); + credentials.setUser(user); + remoteDataConfiguration.setCredentials(credentials); + remoteDataConfiguration.setLocation("https://example.com/my-repo.git"); + remoteDataConfiguration.setType("git"); + context.setRemoteDataConfiguration(remoteDataConfiguration); + + /* execute + test */ + assertDoesNotThrow(() -> gitInputValidatorToTest.validate(context)); + } + + @Test + void validate_successful_when_no_credentials_are_configured() throws IOException { + /* prepare */ + File tempDir = Files.createTempDirectory("upload-folder").toFile(); + tempDir.deleteOnExit(); + String filename = "test"; + writer.save(new File(tempDir, filename), "some text", true); + + PrepareWrapperEnvironment environment = mock(PrepareWrapperEnvironment.class); + when(environment.getPdsPrepareUploadFolderDirectory()).thenReturn(tempDir.toString()); + PrepareWrapperContext context = new PrepareWrapperContext(createFromJSON("{}"), environment); + + SecHubRemoteDataConfiguration remoteDataConfiguration = new SecHubRemoteDataConfiguration(); + remoteDataConfiguration.setLocation("https://example.com/my-repo.git"); + remoteDataConfiguration.setType("git"); + context.setRemoteDataConfiguration(remoteDataConfiguration); + + /* execute + test */ + assertDoesNotThrow(() -> gitInputValidatorToTest.validate(context)); + + } + + private PrepareWrapperContext createContextEmptyConfig() { + PrepareWrapperEnvironment environment = mock(PrepareWrapperEnvironment.class); + when(environment.getPdsPrepareUploadFolderDirectory()).thenReturn("test-upload-folder"); + return new PrepareWrapperContext(createFromJSON("{}"), environment); + } + +} \ No newline at end of file diff --git a/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/git/PrepareWrapperModuleGitTest.java b/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/git/PrepareWrapperModuleGitTest.java new file mode 100644 index 0000000000..cdb542cb00 --- /dev/null +++ b/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/git/PrepareWrapperModuleGitTest.java @@ -0,0 +1,204 @@ +package com.mercedesbenz.sechub.wrapper.prepare.modules.git; + +import static com.mercedesbenz.sechub.commons.model.SecHubScanConfiguration.createFromJSON; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import com.mercedesbenz.sechub.commons.model.SecHubRemoteCredentialConfiguration; +import com.mercedesbenz.sechub.commons.model.SecHubRemoteCredentialUserData; +import com.mercedesbenz.sechub.commons.model.SecHubRemoteDataConfiguration; +import com.mercedesbenz.sechub.test.TestFileWriter; +import com.mercedesbenz.sechub.wrapper.prepare.cli.PrepareWrapperEnvironment; +import com.mercedesbenz.sechub.wrapper.prepare.modules.InputValidatorExitcode; +import com.mercedesbenz.sechub.wrapper.prepare.modules.PrepareWrapperInputValidatorException; +import com.mercedesbenz.sechub.wrapper.prepare.prepare.PrepareWrapperContext; +import com.mercedesbenz.sechub.wrapper.prepare.upload.FileNameSupport; +import com.mercedesbenz.sechub.wrapper.prepare.upload.PrepareWrapperUploadService; + +class PrepareWrapperModuleGitTest { + + PrepareWrapperModuleGit moduleToTest; + + WrapperGit git; + + TestFileWriter writer; + + GitInputValidator gitInputValidator; + + PrepareWrapperUploadService uploadService; + + FileNameSupport filesSupport; + + String subfolder = "subfolder"; + + @BeforeEach + void beforeEach() { + moduleToTest = new PrepareWrapperModuleGit(); + writer = new TestFileWriter(); + gitInputValidator = mock(GitInputValidator.class); + git = mock(WrapperGit.class); + uploadService = mock(PrepareWrapperUploadService.class); + filesSupport = mock(FileNameSupport.class); + + ReflectionTestUtils.setField(moduleToTest, "pdsPrepareModuleGitEnabled", true); + + when(filesSupport.getSubfolderFileNameFromDirectory(anyString())).thenReturn(subfolder); + + moduleToTest.uploadService = uploadService; + moduleToTest.filesSupport = filesSupport; + moduleToTest.git = git; + moduleToTest.gitInputValidator = gitInputValidator; + } + + @Test + void when_inputValidator_throws_InputValidatorException_prepare_return_false() throws IOException, PrepareWrapperInputValidatorException { + /* prepare */ + PrepareWrapperContext context = createContext(); + doThrow(new PrepareWrapperInputValidatorException("test", InputValidatorExitcode.LOCATION_NOT_MATCHING_PATTERN)).when(gitInputValidator) + .validate(context); + + /* execute */ + boolean result = moduleToTest.prepare(context); + + /* test */ + assertFalse(result); + } + + @Test + void when_inputValidator_throws_exception_prepare_throws_exception() throws PrepareWrapperInputValidatorException { + /* prepare */ + PrepareWrapperContext context = createContext(); + doThrow(new IllegalStateException("test")).when(gitInputValidator).validate(context); + + /* execute + test */ + assertThrows(IllegalStateException.class, () -> moduleToTest.prepare(context)); + } + + @Test + void prepare_successful_with_user_credentials_configured() throws IOException { + /* prepare */ + File tempDir = Files.createTempDirectory("upload-folder").toFile(); + tempDir.deleteOnExit(); + String filename = "/" + subfolder + "/.git"; + writer.save(new File(tempDir, filename), "some text", true); + + PrepareWrapperEnvironment environment = mock(PrepareWrapperEnvironment.class); + when(environment.getPdsPrepareUploadFolderDirectory()).thenReturn(tempDir.toString()); + PrepareWrapperContext context = new PrepareWrapperContext(createFromJSON("{}"), environment); + + SecHubRemoteDataConfiguration remoteDataConfiguration = new SecHubRemoteDataConfiguration(); + SecHubRemoteCredentialConfiguration credentials = new SecHubRemoteCredentialConfiguration(); + SecHubRemoteCredentialUserData user = new SecHubRemoteCredentialUserData(); + user.setName("my-example-name"); + user.setPassword("ghp_exampleAPITOKEN8ffne3l6g9f393r8fbcsf"); + credentials.setUser(user); + remoteDataConfiguration.setCredentials(credentials); + remoteDataConfiguration.setLocation("my-example-location"); + remoteDataConfiguration.setType("git"); + context.setRemoteDataConfiguration(remoteDataConfiguration); + + ReflectionTestUtils.setField(moduleToTest, "pdsPrepareModuleGitEnabled", true); + + /* execute */ + boolean result = moduleToTest.prepare(context); + + /* test */ + assertTrue(result); + verify(git).downloadRemoteData(any(GitContext.class)); + } + + @Test + void prepare_successful_when_no_credentials_are_configured() throws IOException { + /* prepare */ + File tempDir = Files.createTempDirectory("upload-folder").toFile(); + tempDir.deleteOnExit(); + String filename = "/" + subfolder + "/.git"; + writer.save(new File(tempDir, filename), "some text", true); + + PrepareWrapperEnvironment environment = mock(PrepareWrapperEnvironment.class); + when(environment.getPdsPrepareUploadFolderDirectory()).thenReturn(tempDir.toString()); + PrepareWrapperContext context = new PrepareWrapperContext(createFromJSON("{}"), environment); + + SecHubRemoteDataConfiguration remoteDataConfiguration = new SecHubRemoteDataConfiguration(); + remoteDataConfiguration.setLocation("my-example-location"); + remoteDataConfiguration.setType("git"); + context.setRemoteDataConfiguration(remoteDataConfiguration); + + /* execute */ + moduleToTest.prepare(context); + + /* test */ + verify(git).downloadRemoteData(any(GitContext.class)); + } + + @Test + void prepare_returns_false_when_modul_is_disabled() throws IOException { + /* prepare */ + PrepareWrapperEnvironment environment = mock(PrepareWrapperEnvironment.class); + when(environment.getPdsPrepareUploadFolderDirectory()).thenReturn("temp"); + PrepareWrapperContext context = new PrepareWrapperContext(createFromJSON("{}"), environment); + + SecHubRemoteDataConfiguration remoteDataConfiguration = new SecHubRemoteDataConfiguration(); + remoteDataConfiguration.setLocation("my-example-location"); + remoteDataConfiguration.setType("docker"); + context.setRemoteDataConfiguration(remoteDataConfiguration); + + ReflectionTestUtils.setField(moduleToTest, "pdsPrepareModuleGitEnabled", false); + + /* execute */ + boolean result = moduleToTest.prepare(context); + + /* test */ + assertFalse(result); + } + + @Test + void isDownloadSuccessful_returns_true_when_git_file_in_directory() throws IOException { + /* prepare */ + File tempDir = Files.createTempDirectory("upload-folder").toFile(); + tempDir.deleteOnExit(); + String filename = "/" + subfolder + "/.git"; + PrepareWrapperContext context = mock(PrepareWrapperContext.class); + when(context.getEnvironment()).thenReturn(mock(PrepareWrapperEnvironment.class)); + writer.save(new File(tempDir, filename), "some text", true); + when(context.getEnvironment().getPdsPrepareUploadFolderDirectory()).thenReturn(tempDir.toString()); + + /* execute */ + boolean result = moduleToTest.isDownloadSuccessful(context); + + /* test */ + assertTrue(result); + } + + @Test + void isDownloadSuccessful_returns_false_when_no_git_file_in_directory() throws IOException { + /* prepare */ + File tempDir = Files.createTempDirectory("upload-folder").toFile(); + tempDir.deleteOnExit(); + writer.save(tempDir, "some text", true); + PrepareWrapperContext context = mock(PrepareWrapperContext.class); + when(context.getEnvironment()).thenReturn(mock(PrepareWrapperEnvironment.class)); + when(context.getEnvironment().getPdsPrepareUploadFolderDirectory()).thenReturn(tempDir.toString()); + + /* execute */ + boolean result = moduleToTest.isDownloadSuccessful(context); + + /* test */ + assertFalse(result); + } + + private PrepareWrapperContext createContext() { + PrepareWrapperEnvironment environment = mock(PrepareWrapperEnvironment.class); + when(environment.getPdsPrepareUploadFolderDirectory()).thenReturn("test-upload-folder"); + return new PrepareWrapperContext(createFromJSON("{}"), environment); + } + +} \ No newline at end of file diff --git a/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/WrapperGitTest.java b/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/git/WrapperGitTest.java similarity index 97% rename from sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/WrapperGitTest.java rename to sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/git/WrapperGitTest.java index a9d41fd893..5843d7ab5e 100644 --- a/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/WrapperGitTest.java +++ b/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/git/WrapperGitTest.java @@ -1,4 +1,4 @@ -package com.mercedesbenz.sechub.wrapper.prepare.modules; +package com.mercedesbenz.sechub.wrapper.prepare.modules.git; import static com.mercedesbenz.sechub.wrapper.prepare.cli.PrepareWrapperEnvironmentVariables.PDS_PREPARE_CREDENTIAL_PASSWORD; import static com.mercedesbenz.sechub.wrapper.prepare.cli.PrepareWrapperEnvironmentVariables.PDS_PREPARE_CREDENTIAL_USERNAME; @@ -48,7 +48,7 @@ void beforeEach() throws IOException, InterruptedException { @ParameterizedTest @ValueSource(strings = { "https://host.xz/path/to/repo/.git", "http://myrepo/here/.git", "example.org.git" }) - void when_cloneRepository_is_executed_the_processAdapterFactory_starts_JGit_clone(String location) throws IOException { + void when_cloneRepository_is_executed_the_processAdapterFactory_starts_JGit_clone(String location) { /* prepare */ Map credentialMap = new HashMap<>(); credentialMap.put(PDS_PREPARE_CREDENTIAL_USERNAME, CryptoAccess.CRYPTO_STRING.seal("user")); diff --git a/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/skopeo/PrepareWrapperModuleSkopeoTest.java b/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/skopeo/PrepareWrapperModuleSkopeoTest.java new file mode 100644 index 0000000000..55758b575b --- /dev/null +++ b/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/skopeo/PrepareWrapperModuleSkopeoTest.java @@ -0,0 +1,187 @@ +package com.mercedesbenz.sechub.wrapper.prepare.modules.skopeo; + +import static com.mercedesbenz.sechub.commons.model.SecHubScanConfiguration.createFromJSON; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import com.mercedesbenz.sechub.commons.model.SecHubRemoteCredentialConfiguration; +import com.mercedesbenz.sechub.commons.model.SecHubRemoteCredentialUserData; +import com.mercedesbenz.sechub.commons.model.SecHubRemoteDataConfiguration; +import com.mercedesbenz.sechub.test.TestFileWriter; +import com.mercedesbenz.sechub.wrapper.prepare.cli.PrepareWrapperEnvironment; +import com.mercedesbenz.sechub.wrapper.prepare.modules.InputValidatorExitcode; +import com.mercedesbenz.sechub.wrapper.prepare.modules.PrepareWrapperInputValidatorException; +import com.mercedesbenz.sechub.wrapper.prepare.prepare.PrepareWrapperContext; + +class PrepareWrapperModuleSkopeoTest { + + PrepareWrapperModuleSkopeo moduleToTest; + SkopeoInputValidator skopeoInputValidator; + WrapperSkopeo skopeo; + TestFileWriter writer; + + @BeforeEach + void beforeEach() { + moduleToTest = new PrepareWrapperModuleSkopeo(); + skopeoInputValidator = mock(SkopeoInputValidator.class); + writer = new TestFileWriter(); + skopeo = mock(WrapperSkopeo.class); + + ReflectionTestUtils.setField(moduleToTest, "pdsPrepareModuleSkopeoEnabled", true); + + moduleToTest.skopeoInputValidator = skopeoInputValidator; + moduleToTest.skopeo = skopeo; + } + + @Test + void when_inputValidator_throws_InputValidatorException_prepare_return_false() throws IOException, PrepareWrapperInputValidatorException { + /* prepare */ + PrepareWrapperContext context = createContext(); + doThrow(new PrepareWrapperInputValidatorException("test", InputValidatorExitcode.LOCATION_NOT_MATCHING_PATTERN)).when(skopeoInputValidator) + .validate(context); + + /* execute */ + boolean result = moduleToTest.prepare(context); + + /* test */ + assertFalse(result); + } + + @Test + void when_inputvalidator_throws_exception_prepare_throws_exception() throws PrepareWrapperInputValidatorException { + /* prepare */ + PrepareWrapperContext context = createContext(); + doThrow(new IllegalStateException("test")).when(skopeoInputValidator).validate(context); + + /* execute + test */ + assertThrows(IllegalStateException.class, () -> moduleToTest.prepare(context)); + } + + @Test + void prepare_successful_when_user_credentials_are_configured_correctly() throws IOException { + /* prepare */ + File tempDir = Files.createTempDirectory("upload-folder").toFile(); + tempDir.deleteOnExit(); + String filename = "testimage.tar"; + writer.save(new File(tempDir, filename), "some text", true); + + PrepareWrapperEnvironment environment = mock(PrepareWrapperEnvironment.class); + when(environment.getPdsPrepareUploadFolderDirectory()).thenReturn(tempDir.toString()); + PrepareWrapperContext context = new PrepareWrapperContext(createFromJSON("{}"), environment); + + SecHubRemoteDataConfiguration remoteDataConfiguration = new SecHubRemoteDataConfiguration(); + SecHubRemoteCredentialConfiguration credentials = new SecHubRemoteCredentialConfiguration(); + SecHubRemoteCredentialUserData user = new SecHubRemoteCredentialUserData(); + user.setName("my-example-name"); + user.setPassword("ghp_exampleAPITOKEN8ffne3l6g9f393r8fbcsf"); + credentials.setUser(user); + remoteDataConfiguration.setCredentials(credentials); + remoteDataConfiguration.setLocation("my-example-location"); + remoteDataConfiguration.setType("docker"); + context.setRemoteDataConfiguration(remoteDataConfiguration); + + /* execute */ + moduleToTest.prepare(context); + + /* test */ + verify(skopeo).download(any(SkopeoContext.class)); + verify(skopeo).cleanUploadDirectory(tempDir.toString()); + } + + @Test + void prepare_successful_when_no_credentials_are_configured() throws IOException { + /* prepare */ + File tempDir = Files.createTempDirectory("upload-folder").toFile(); + tempDir.deleteOnExit(); + String filename = "testimage.tar"; + writer.save(new File(tempDir, filename), "some text", true); + + PrepareWrapperEnvironment environment = mock(PrepareWrapperEnvironment.class); + when(environment.getPdsPrepareUploadFolderDirectory()).thenReturn(tempDir.toString()); + PrepareWrapperContext context = new PrepareWrapperContext(createFromJSON("{}"), environment); + + SecHubRemoteDataConfiguration remoteDataConfiguration = new SecHubRemoteDataConfiguration(); + remoteDataConfiguration.setLocation("my-example-location"); + remoteDataConfiguration.setType("docker"); + context.setRemoteDataConfiguration(remoteDataConfiguration); + /* execute */ + moduleToTest.prepare(context); + + /* test */ + verify(skopeo).download(any(SkopeoContext.class)); + verify(skopeo).cleanUploadDirectory(tempDir.toString()); + } + + @Test + void prepare_returns_false_when_modul_is_disabled() throws IOException { + /* prepare */ + + PrepareWrapperEnvironment environment = mock(PrepareWrapperEnvironment.class); + when(environment.getPdsPrepareUploadFolderDirectory()).thenReturn("temp"); + PrepareWrapperContext context = new PrepareWrapperContext(createFromJSON("{}"), environment); + + SecHubRemoteDataConfiguration remoteDataConfiguration = new SecHubRemoteDataConfiguration(); + remoteDataConfiguration.setLocation("my-example-location"); + remoteDataConfiguration.setType("docker"); + context.setRemoteDataConfiguration(remoteDataConfiguration); + + ReflectionTestUtils.setField(moduleToTest, "pdsPrepareModuleSkopeoEnabled", false); + + /* execute */ + boolean result = moduleToTest.prepare(context); + + /* test */ + assertFalse(result); + } + + @Test + void isDownloadSuccessful_returns_true_when_tar_file_in_directory() throws IOException { + /* prepare */ + File tempDir = Files.createTempDirectory("upload-folder").toFile(); + tempDir.deleteOnExit(); + String filename = "testimage.tar"; + PrepareWrapperContext context = mock(PrepareWrapperContext.class); + when(context.getEnvironment()).thenReturn(mock(PrepareWrapperEnvironment.class)); + writer.save(new File(tempDir, filename), "some text", true); + when(context.getEnvironment().getPdsPrepareUploadFolderDirectory()).thenReturn(tempDir.toString()); + + /* execute */ + boolean result = moduleToTest.isDownloadSuccessful(context); + + /* test */ + assertTrue(result); + } + + @Test + void isDownloadSuccessful_returns_false_when_no_tar_file_in_directory() throws IOException { + /* prepare */ + File tempDir = Files.createTempDirectory("upload-folder").toFile(); + tempDir.deleteOnExit(); + writer.save(tempDir, "some text", true); + PrepareWrapperContext context = mock(PrepareWrapperContext.class); + when(context.getEnvironment()).thenReturn(mock(PrepareWrapperEnvironment.class)); + when(context.getEnvironment().getPdsPrepareUploadFolderDirectory()).thenReturn(tempDir.toString()); + + /* execute */ + boolean result = moduleToTest.isDownloadSuccessful(context); + + /* test */ + assertFalse(result); + } + + private PrepareWrapperContext createContext() { + PrepareWrapperEnvironment environment = mock(PrepareWrapperEnvironment.class); + when(environment.getPdsPrepareUploadFolderDirectory()).thenReturn("test-upload-folder"); + return new PrepareWrapperContext(createFromJSON("{}"), environment); + } + +} \ No newline at end of file diff --git a/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/skopeo/SkopeoInputValidatorTest.java b/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/skopeo/SkopeoInputValidatorTest.java new file mode 100644 index 0000000000..dc2e33c0cf --- /dev/null +++ b/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/skopeo/SkopeoInputValidatorTest.java @@ -0,0 +1,208 @@ +package com.mercedesbenz.sechub.wrapper.prepare.modules.skopeo; + +import static com.mercedesbenz.sechub.commons.model.SecHubScanConfiguration.createFromJSON; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import com.mercedesbenz.sechub.commons.model.SecHubRemoteCredentialConfiguration; +import com.mercedesbenz.sechub.commons.model.SecHubRemoteCredentialUserData; +import com.mercedesbenz.sechub.commons.model.SecHubRemoteDataConfiguration; +import com.mercedesbenz.sechub.test.TestFileWriter; +import com.mercedesbenz.sechub.wrapper.prepare.cli.PrepareWrapperEnvironment; +import com.mercedesbenz.sechub.wrapper.prepare.modules.PrepareWrapperInputValidatorException; +import com.mercedesbenz.sechub.wrapper.prepare.prepare.PrepareWrapperContext; + +class SkopeoInputValidatorTest { + + SkopeoInputValidator validatorToTest; + + TestFileWriter writer; + + @BeforeEach + void beforeEach() { + writer = new TestFileWriter(); + validatorToTest = new SkopeoInputValidator(); + } + + @ParameterizedTest + @ValueSource(strings = { "ubuntu:22.04", "ubuntu", "docker://ubuntu:22.04", "docker://ubuntu", "oci:busybox_ocilayout:latest", "https://hub.docker.com", + "docker://docker.io/library/busybox:latest", "ubuntu@sha256:26c68657ccce2cb0a31b330cb0be2b5e108d467f641c62e13ab40cbec258c68d", + "ghcr.io/owner/repo:tag" }) + void validateLocation_returns_true_for_valid_docker_urls(String location) { + /* execute + test */ + assertDoesNotThrow(() -> validatorToTest.validateLocation(location)); + } + + @ParameterizedTest + @ValueSource(strings = { "invalid-registry ubuntu:22.04", "docker://registry/ubuntu:invalid tag", "docker://ubuntu:tag$maliciousCode", + "docker://ubuntu:tag|maliciousCode", "my-registry/oci:busybox_ocilayout;latest", }) + void validateLocation_throws_IllegalArgumentException_for_invalid_docker_urls(String location) { + /* execute + test */ + assertThrows(IllegalArgumentException.class, () -> validatorToTest.validateLocation(location)); + } + + @ParameterizedTest + @ValueSource(strings = { "username", "username123", "username_123", "username-123", "username-123_456", "username1234567890123456789003890123456", + "user_user_user" }) + void validateUsername_returns_true_for_valid_usernames(String username) { + /* execute + test */ + assertDoesNotThrow(() -> validatorToTest.validateUsername(username)); + } + + @ParameterizedTest + @ValueSource(strings = { "user name", "username?", "username!", "username>", "username<", "username'", "username\"", "username;", "username&", "username|", + "username`", "username$", "username{", "username}" }) + void validateUsername_throws_exception_for_invalid_usernames(String username) { + /* execute */ + PrepareWrapperInputValidatorException exception = assertThrows(PrepareWrapperInputValidatorException.class, + () -> validatorToTest.validateUsername(username)); + + /* test */ + assertEquals("Defined username must match the docker pattern.", exception.getMessage()); + } + + @ParameterizedTest + @ValueSource(strings = { "password", "password123", "password_123", "password-123", "password-123_456", "password1234567890123456789003890123456", + "dXNlckBleGFtcGxlLmNvbTpzZWNyZXQexample", "Z2hjcl9wczpzc2VjcmV0example" }) + void validatePassword_returns_true_for_valid_passwords(String password) { + /* execute + test */ + assertDoesNotThrow(() -> validatorToTest.validatePassword(password)); + } + + @ParameterizedTest + @ValueSource(strings = { "password?", "password!", "password>", "password<", "password'", "password\"", "password;", "password&", "password|", "password`", + "password$", "password{", "password}", "password;echo 'malicious'" }) + void validatePassword_throws_exception_for_invalid_passwords(String password) { + /* execute */ + PrepareWrapperInputValidatorException exception = assertThrows(PrepareWrapperInputValidatorException.class, + () -> validatorToTest.validatePassword(password)); + + /* test */ + assertEquals("Defined password must match the docker Api token pattern.", exception.getMessage()); + } + + @Test + void validate_throws_exception_when_credentials_are_empty() { + /* prepare */ + PrepareWrapperContext context = createContextEmptyConfig(); + SecHubRemoteDataConfiguration remoteDataConfiguration = new SecHubRemoteDataConfiguration(); + SecHubRemoteCredentialConfiguration credentials = new SecHubRemoteCredentialConfiguration(); + remoteDataConfiguration.setCredentials(credentials); + remoteDataConfiguration.setLocation("my-example-location"); + remoteDataConfiguration.setType("docker"); + context.setRemoteDataConfiguration(remoteDataConfiguration); + + /* execute */ + IllegalStateException exception = assertThrows(IllegalStateException.class, () -> validatorToTest.validate(context)); + + /* test */ + assertEquals("Defined credentials must contain credential user and can not be empty.", exception.getMessage()); + } + + @Test + void validate_throws_exception_when_no_username_found() { + /* prepare */ + PrepareWrapperContext context = createContextEmptyConfig(); + SecHubRemoteDataConfiguration remoteDataConfiguration = new SecHubRemoteDataConfiguration(); + SecHubRemoteCredentialConfiguration credentials = new SecHubRemoteCredentialConfiguration(); + SecHubRemoteCredentialUserData user = new SecHubRemoteCredentialUserData(); + user.setPassword("my-example-password"); + credentials.setUser(user); + remoteDataConfiguration.setCredentials(credentials); + remoteDataConfiguration.setLocation("my-example-location"); + remoteDataConfiguration.setType("docker"); + context.setRemoteDataConfiguration(remoteDataConfiguration); + + /* execute */ + IllegalStateException exception = assertThrows(IllegalStateException.class, () -> validatorToTest.validate(context)); + + /* test */ + assertTrue(exception.getMessage().contains("Defined username must not be null or empty.")); + } + + @Test + void validate_throws_exception_when_no_password_found() { + /* prepare */ + PrepareWrapperContext context = createContextEmptyConfig(); + SecHubRemoteDataConfiguration remoteDataConfiguration = new SecHubRemoteDataConfiguration(); + SecHubRemoteCredentialConfiguration credentials = new SecHubRemoteCredentialConfiguration(); + SecHubRemoteCredentialUserData user = new SecHubRemoteCredentialUserData(); + user.setName("my-example-name"); + credentials.setUser(user); + remoteDataConfiguration.setCredentials(credentials); + remoteDataConfiguration.setLocation("my-example-location"); + remoteDataConfiguration.setType("docker"); + context.setRemoteDataConfiguration(remoteDataConfiguration); + + /* execute */ + IllegalStateException exception = assertThrows(IllegalStateException.class, () -> validatorToTest.validate(context)); + + /* test */ + assertEquals("Defined password must not be null or empty. Password is required for login.", exception.getMessage()); + + } + + @Test + void validate_successful_when_user_credentials_are_configured_correctly() throws IOException { + /* prepare */ + File tempDir = Files.createTempDirectory("upload-folder").toFile(); + tempDir.deleteOnExit(); + String filename = "testimage.tar"; + writer.save(new File(tempDir, filename), "some text", true); + + PrepareWrapperEnvironment environment = mock(PrepareWrapperEnvironment.class); + when(environment.getPdsPrepareUploadFolderDirectory()).thenReturn(tempDir.toString()); + PrepareWrapperContext context = new PrepareWrapperContext(createFromJSON("{}"), environment); + + SecHubRemoteDataConfiguration remoteDataConfiguration = new SecHubRemoteDataConfiguration(); + SecHubRemoteCredentialConfiguration credentials = new SecHubRemoteCredentialConfiguration(); + SecHubRemoteCredentialUserData user = new SecHubRemoteCredentialUserData(); + user.setName("my-example-name"); + user.setPassword("ghp_exampleAPITOKEN8ffne3l6g9f393r8fbcsf"); + credentials.setUser(user); + remoteDataConfiguration.setCredentials(credentials); + remoteDataConfiguration.setLocation("my-example-location"); + remoteDataConfiguration.setType("docker"); + context.setRemoteDataConfiguration(remoteDataConfiguration); + + /* execute + test */ + assertDoesNotThrow(() -> validatorToTest.validate(context)); + } + + @Test + void validate_successful_when_no_credentials_are_configured() throws IOException { + /* prepare */ + File tempDir = Files.createTempDirectory("upload-folder").toFile(); + tempDir.deleteOnExit(); + String filename = "testimage.tar"; + writer.save(new File(tempDir, filename), "some text", true); + + PrepareWrapperEnvironment environment = mock(PrepareWrapperEnvironment.class); + when(environment.getPdsPrepareUploadFolderDirectory()).thenReturn(tempDir.toString()); + PrepareWrapperContext context = new PrepareWrapperContext(createFromJSON("{}"), environment); + + SecHubRemoteDataConfiguration remoteDataConfiguration = new SecHubRemoteDataConfiguration(); + remoteDataConfiguration.setLocation("my-example-location"); + remoteDataConfiguration.setType("docker"); + context.setRemoteDataConfiguration(remoteDataConfiguration); + + /* execute + test */ + assertDoesNotThrow(() -> validatorToTest.validate(context)); + + } + + private PrepareWrapperContext createContextEmptyConfig() { + PrepareWrapperEnvironment environment = mock(PrepareWrapperEnvironment.class); + when(environment.getPdsPrepareUploadFolderDirectory()).thenReturn("test-upload-folder"); + return new PrepareWrapperContext(createFromJSON("{}"), environment); + } +} diff --git a/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/skopeo/WrapperSkopeoTest.java b/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/skopeo/WrapperSkopeoTest.java new file mode 100644 index 0000000000..c1d518a757 --- /dev/null +++ b/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/skopeo/WrapperSkopeoTest.java @@ -0,0 +1,95 @@ +package com.mercedesbenz.sechub.wrapper.prepare.modules.skopeo; + +import static com.mercedesbenz.sechub.wrapper.prepare.cli.PrepareWrapperEnvironmentVariables.PDS_PREPARE_CREDENTIAL_PASSWORD; +import static com.mercedesbenz.sechub.wrapper.prepare.cli.PrepareWrapperEnvironmentVariables.PDS_PREPARE_CREDENTIAL_USERNAME; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import javax.crypto.SealedObject; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.mercedesbenz.sechub.commons.core.security.CryptoAccess; +import com.mercedesbenz.sechub.commons.pds.PDSProcessAdapterFactory; +import com.mercedesbenz.sechub.commons.pds.ProcessAdapter; + +class WrapperSkopeoTest { + + WrapperSkopeo wrapperToTest; + PDSProcessAdapterFactory processAdapterFactory; + ProcessAdapter processAdapter; + + @BeforeEach + void beforeEach() throws IOException, InterruptedException { + wrapperToTest = new WrapperSkopeo(); + processAdapterFactory = mock(PDSProcessAdapterFactory.class); + processAdapter = mock(ProcessAdapter.class); + when(processAdapterFactory.startProcess(any())).thenReturn(processAdapter); + when(processAdapter.waitFor(any(Long.class), any(TimeUnit.class))).thenReturn(true); + + wrapperToTest.processAdapterFactory = processAdapterFactory; + } + + @Test + void when_download_is_executed_download_process_is_executed() throws IOException { + /* prepare */ + SkopeoContext context = (SkopeoContext) new SkopeoContext.SkopeoContextBuilder().setLocation("docker://ubuntu:22.04").setUploadDirectory("folder") + .build(); + + /* execute */ + assertDoesNotThrow(() -> wrapperToTest.download(context)); + + /* test */ + verify(processAdapterFactory, times(1)).startProcess(any()); + } + + @Test + void when_download_is_executed_with_credentials_download_and_login_process_are_executed() throws IOException { + /* prepare */ + Map credentialMap = new HashMap(); + credentialMap.put(PDS_PREPARE_CREDENTIAL_USERNAME, CryptoAccess.CRYPTO_STRING.seal("username")); + credentialMap.put(PDS_PREPARE_CREDENTIAL_PASSWORD, CryptoAccess.CRYPTO_STRING.seal("password")); + SkopeoContext context = (SkopeoContext) new SkopeoContext.SkopeoContextBuilder().setLocation("docker://ubuntu:22.04").setUploadDirectory("folder") + .setCredentialMap(credentialMap).build(); + + /* execute */ + assertDoesNotThrow(() -> wrapperToTest.download(context)); + + /* test */ + verify(processAdapterFactory, times(2)).startProcess(any()); + } + + @Test + void when_process_throws_exception_then_download_throws_exception() throws IOException { + /* prepare */ + String location = "docker://ubuntu:22.04"; + SkopeoContext context = (SkopeoContext) new SkopeoContext.SkopeoContextBuilder().setLocation(location).setUploadDirectory("folder").build(); + when(processAdapterFactory.startProcess(any())).thenThrow(new IOException()); + + /* execute */ + IOException exception = assertThrows(IOException.class, () -> wrapperToTest.download(context)); + + /* test */ + assertEquals("Error while download with Skopeo from: " + location, exception.getMessage()); + + } + + @Test + void when_cleanUploadDirectory_is_executed_clean_process_is_executed() throws IOException { + /* prepare */ + String uploadDirectory = "folder"; + + /* execute */ + assertDoesNotThrow(() -> wrapperToTest.cleanUploadDirectory(uploadDirectory)); + + /* test */ + verify(processAdapterFactory, times(1)).startProcess(any()); + } + +} \ No newline at end of file diff --git a/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/prepare/PrepareWrapperPreparationServiceTest.java b/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/prepare/PrepareWrapperPreparationServiceTest.java index 474cfe0baf..4b225b5507 100644 --- a/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/prepare/PrepareWrapperPreparationServiceTest.java +++ b/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/prepare/PrepareWrapperPreparationServiceTest.java @@ -6,7 +6,6 @@ import java.io.IOException; import java.util.ArrayList; -import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -14,7 +13,7 @@ import com.mercedesbenz.sechub.adapter.AdapterExecutionResult; import com.mercedesbenz.sechub.commons.model.SecHubRemoteDataConfiguration; import com.mercedesbenz.sechub.wrapper.prepare.cli.PrepareWrapperEnvironment; -import com.mercedesbenz.sechub.wrapper.prepare.modules.PrepareWrapperModuleGit; +import com.mercedesbenz.sechub.wrapper.prepare.modules.git.PrepareWrapperModuleGit; class PrepareWrapperPreparationServiceTest { @@ -53,12 +52,10 @@ void when_no_remote_data_was_configured_return_preparation_success_with_warn_mes @Test void when_remote_data_was_configured_but_no_module_executed_return_preparation_failed_with_message() throws IOException { /* prepare */ - List remoteDataConfigurationList = new ArrayList<>(); SecHubRemoteDataConfiguration remoteDataConfiguration = new SecHubRemoteDataConfiguration(); remoteDataConfiguration.setLocation("my-example_location"); remoteDataConfiguration.setType("git"); - remoteDataConfigurationList.add(remoteDataConfiguration); - when(context.getRemoteDataConfigurationList()).thenReturn(remoteDataConfigurationList); + when(context.getRemoteDataConfiguration()).thenReturn(remoteDataConfiguration); /* execute */ AdapterExecutionResult result = serviceToTest.startPreparation(); @@ -73,16 +70,13 @@ void when_remote_data_was_configured_but_no_module_executed_return_preparation_f void when_remote_data_was_configured_and_git_module_added_return_preparation_success_without_message() throws IOException { /* prepare */ PrepareWrapperModuleGit gitModule = mock(PrepareWrapperModuleGit.class); + when(gitModule.prepare(context)).thenReturn(true); serviceToTest.modules.add(gitModule); - List remoteDataConfigurationList = new ArrayList<>(); SecHubRemoteDataConfiguration remoteDataConfiguration = new SecHubRemoteDataConfiguration(); remoteDataConfiguration.setLocation("my-example_location"); remoteDataConfiguration.setType("git"); - remoteDataConfigurationList.add(remoteDataConfiguration); - when(context.getRemoteDataConfigurationList()).thenReturn(remoteDataConfigurationList); - - when(gitModule.isAbleToPrepare(context)).thenReturn(true); + when(context.getRemoteDataConfiguration()).thenReturn(remoteDataConfiguration); /* execute */ AdapterExecutionResult result = serviceToTest.startPreparation(); diff --git a/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/prepare/PrepareWrapperRemoteConfigurationExtractorTest.java b/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/prepare/PrepareWrapperRemoteConfigurationExtractorTest.java index 7671702cfd..0337d33381 100644 --- a/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/prepare/PrepareWrapperRemoteConfigurationExtractorTest.java +++ b/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/prepare/PrepareWrapperRemoteConfigurationExtractorTest.java @@ -3,8 +3,6 @@ import static com.mercedesbenz.sechub.commons.model.SecHubScanConfiguration.createFromJSON; import static org.junit.jupiter.api.Assertions.*; -import java.util.List; - import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -46,12 +44,11 @@ void extractor_returns_list_with_one_element_when_one_remote_section_is_configur SecHubConfigurationModel model = createFromJSON(json); /* execute */ - List result = extractorToTest.extract(model); + SecHubRemoteDataConfiguration result = extractorToTest.extract(model); /* test */ - assertEquals(1, result.size()); - assertEquals("remote_example_location", result.get(0).getLocation()); - assertEquals("git", result.get(0).getType()); + assertEquals("remote_example_location", result.getLocation()); + assertEquals("git", result.getType()); } @Test @@ -60,10 +57,10 @@ void extractor_returns_empty_list_when_sechub_model_is_empty() { SecHubConfigurationModel model = new SecHubConfigurationModel(); /* execute */ - List result = extractorToTest.extract(model); + SecHubRemoteDataConfiguration result = extractorToTest.extract(model); /* test */ - assertTrue(result.isEmpty()); + assertNull(result); } @Test @@ -114,18 +111,10 @@ void extractor_returns_list_with_three_elements_when_three_remote_sections_are_c SecHubConfigurationModel model = createFromJSON(json); /* execute */ - List result = extractorToTest.extract(model); + IllegalStateException exception = assertThrows(IllegalStateException.class, () -> extractorToTest.extract(model)); /* test */ - assertEquals(3, result.size()); - assertEquals("remote_example_location", result.get(0).getLocation()); - assertEquals("git", result.get(0).getType()); - - assertEquals("remote_example_location2", result.get(1).getLocation()); - assertEquals("docker", result.get(1).getType()); - - assertEquals("remote_example_location3", result.get(2).getLocation()); - assertEquals("", result.get(2).getType()); + assertTrue(exception.getMessage().contains("Only one remote data configuration is allowed.")); } } \ No newline at end of file diff --git a/sechub-wrapper-prepare/src/test/resources/application-test-fail.properties b/sechub-wrapper-prepare/src/test/resources/application-test-fail.properties index a60ff68547..e15c61e3dd 100644 --- a/sechub-wrapper-prepare/src/test/resources/application-test-fail.properties +++ b/sechub-wrapper-prepare/src/test/resources/application-test-fail.properties @@ -4,10 +4,11 @@ # --------------- sechub.job.uuid=665dc4e8-d2de-4d2f-a3a3-4c447630b229 -pds.scan.configuration={"projectId":"project1","data": {"binaries": [{"name": "remote_example_name","remote": {"location": "https://not-a-git.repo","type": "not-git"}}]},"codeScan": {"use": ["remote_example_name"]}} +pds.scan.configuration={"projectId":"project1","data": {"binaries": [{"name": "remote_example_name","remote": {"location": "https://not-any.repo/","type": "not-git"}}]},"codeScan": {"use": ["remote_example_name"]}} pds.job.user.messages.folder=./build/pds-prepare/tmp/messages pds.prepare.upload.directory=src/test/resources/test-upload-folder pds.prepare.auto.cleanup.git.folder=true pds.prepare.module.git.enabled=true +pds.prepare.module.skopeo.enabled=true pds.config.product.timeout.minutes=30 pds.storage.sharedvolume.upload.dir=temp \ No newline at end of file