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 ee95b11b1..990c8c4d5 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 @@ -20,8 +20,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 000000000..d09a85c0f --- /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 f7d65a3dd..000000000 --- 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 d8e28d1b7..5e4037f45 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 000000000..f8d76c2b0 --- /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 000000000..76525f391 --- /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 3109d9b0a..46d24a7fa 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 ae03940de..217ddc00d 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 85% 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 b8564988e..643278c0c 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 000000000..08ad23daf --- /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 219436a65..9b271438f 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 56% 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 b0220323c..4ac7ca0cb 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,16 +18,15 @@ 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; @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; @@ -41,61 +40,35 @@ public class PrepareWrapperModuleGit implements PrepareWrapperModule { @Autowired GitInputValidator gitInputValidator; - 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("Module {} resolved remote configuration and will prepare.", getClass().getSimpleName()); - LOG.debug("Start remote data preparation for GIT repository"); + SecHubRemoteDataConfiguration secHubRemoteDataConfiguration = context.getRemoteDataConfiguration(); - List remoteDataConfigurationList = context.getRemoteDataConfigurationList(); - - 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); + return true; } - boolean isMatchingGitType(String type) { - if (type == null || type.isBlank()) { - return false; - } - return TYPE.equalsIgnoreCase(type); - } - - boolean isDownloadSuccessful(PrepareWrapperContext context) { + protected boolean isDownloadSuccessful(PrepareWrapperContext context) { // check if download folder contains git Path path = Paths.get(context.getEnvironment().getPdsPrepareUploadFolderDirectory()); if (Files.isDirectory(path)) { @@ -110,54 +83,39 @@ 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); } - 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 bf0238127..c06a6f4e6 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 000000000..abbfce049 --- /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 000000000..4a392ef92 --- /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 000000000..6ba219ab2 --- /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 000000000..c1524a19d --- /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 fa72948a2..c325d3c11 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 b266b4fb1..afd2a72e7 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 7095f46f0..bd5f684ed 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 99a33b044..d09427065 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 54ca94e64..52cb47d2e 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 @@ -17,13 +17,21 @@ 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; @SpringBootTest(classes = { PrepareWrapperContextFactory.class, PrepareWrapperPreparationService.class, PrepareWrapperPojoFactory.class, PrepareWrapperEnvironment.class, PrepareWrapperPDSUserMessageSupportPojoFactory.class, PrepareWrapperRemoteConfigurationExtractor.class, - PrepareWrapperModuleGit.class, PrepareWrapperModule.class, WrapperGit.class, GitInputValidator.class, JGitAdapter.class }) + PrepareWrapperModuleGit.class, PrepareWrapperModule.class, WrapperGit.class, GitInputValidator.class, JGitAdapter.class, + PrepareWrapperModuleSkopeo.class, WrapperSkopeo.class, SkopeoInputValidator.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 000000000..1c825a966 --- /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 3267a81ea..000000000 --- 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 162b90650..000000000 --- a/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/PrepareWrapperModuleGitTest.java +++ /dev/null @@ -1,316 +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; - -class PrepareWrapperModuleGitTest { - - private PrepareWrapperModuleGit moduleToTest; - - private WrapperGit git; - - TestFileWriter writer; - - GitInputValidator gitInputValidator; - - @BeforeEach - void beforeEach() { - moduleToTest = new PrepareWrapperModuleGit(); - writer = new TestFileWriter(); - gitInputValidator = new GitInputValidator(); - git = mock(WrapperGit.class); - - 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 = ".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 = ".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 = ".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 000000000..55609e897 --- /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 000000000..56a81466e --- /dev/null +++ b/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/git/PrepareWrapperModuleGitTest.java @@ -0,0 +1,189 @@ +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; + +class PrepareWrapperModuleGitTest { + + private PrepareWrapperModuleGit moduleToTest; + + private WrapperGit git; + + TestFileWriter writer; + + GitInputValidator gitInputValidator; + + @BeforeEach + void beforeEach() { + moduleToTest = new PrepareWrapperModuleGit(); + writer = new TestFileWriter(); + gitInputValidator = mock(GitInputValidator.class); + git = mock(WrapperGit.class); + ReflectionTestUtils.setField(moduleToTest, "pdsPrepareModuleGitEnabled", true); + + 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 = ".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 = ".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 = ".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 a9d41fd89..5843d7ab5 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 000000000..55758b575 --- /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 000000000..dc2e33c0c --- /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 000000000..c1d518a75 --- /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 474cfe0ba..4b225b550 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 7671702cf..0337d3338 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 e0df022af..7876fb0f8 100644 --- a/sechub-wrapper-prepare/src/test/resources/application-test-fail.properties +++ b/sechub-wrapper-prepare/src/test/resources/application-test-fail.properties @@ -4,9 +4,10 @@ # --------------- 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