From bf9eac46068cd4251bb7ccc49763d6e25e89caca Mon Sep 17 00:00:00 2001 From: Laura Date: Tue, 7 May 2024 15:56:11 +0200 Subject: [PATCH] Integrating skopeo modul for prepraing docker images #3028 --- .../cli/PrepareWrapperKeyConstants.java | 10 + .../modules/PrepareWrapperModuleGit.java | 4 +- .../modules/PrepareWrapperModuleSkopeo.java | 172 ++++++++++++++++++ .../prepare/modules/SkopeoContext.java | 36 ++++ .../prepare/modules/SkopeoInputValidator.java | 62 +++++++ .../prepare/modules/WrapperSkopeo.java | 156 ++++++++++++++++ .../PrepareWrapperModuleSkopeoTest.java | 25 +++ .../modules/SkopeoInputValidatorTest.java | 72 ++++++++ .../prepare/modules/WrapperSkopeoTest.java | 28 +++ 9 files changed, 563 insertions(+), 2 deletions(-) create mode 100644 sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/PrepareWrapperModuleSkopeo.java create mode 100644 sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/SkopeoContext.java create mode 100644 sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/SkopeoInputValidator.java create mode 100644 sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/WrapperSkopeo.java create mode 100644 sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/PrepareWrapperModuleSkopeoTest.java create mode 100644 sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/SkopeoInputValidatorTest.java create mode 100644 sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/WrapperSkopeoTest.java 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/PrepareWrapperModuleGit.java b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/PrepareWrapperModuleGit.java index b0220323c..238162e40 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/PrepareWrapperModuleGit.java @@ -134,8 +134,8 @@ private void clonePrivateRepository(PrepareWrapperContext context, SecHubRemoteC /* @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 */ diff --git a/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/PrepareWrapperModuleSkopeo.java b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/PrepareWrapperModuleSkopeo.java new file mode 100644 index 000000000..9bfb9bbd7 --- /dev/null +++ b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/PrepareWrapperModuleSkopeo.java @@ -0,0 +1,172 @@ +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 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 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.core.security.CryptoAccess; +import com.mercedesbenz.sechub.commons.model.*; +import com.mercedesbenz.sechub.wrapper.prepare.prepare.PrepareWrapperContext; + +@Service +public class PrepareWrapperModuleSkopeo implements PrepareWrapperModule { + + Logger LOG = LoggerFactory.getLogger(PrepareWrapperModuleSkopeo.class); + + private static final String TYPE = "docker"; + + @Value("${" + KEY_PDS_PREPARE_MODULE_SKOPEO_ENABLED + ":true}") + private boolean pdsPrepareModuleSkopeoEnabled; + + @Autowired + SkopeoInputValidator skopeoInputValidator; + + @Autowired + WrapperSkopeo skopeo; + + @Override + public boolean isAbleToPrepare(PrepareWrapperContext context) { + + if (!pdsPrepareModuleSkopeoEnabled) { + LOG.debug("Skopeo module is disabled"); + return false; + } + + for (SecHubRemoteDataConfiguration secHubRemoteDataConfiguration : context.getRemoteDataConfigurationList()) { + String location = secHubRemoteDataConfiguration.getLocation(); + + skopeoInputValidator.validateLocationCharacters(location, null); + + if (isMatchingSkopeoType(secHubRemoteDataConfiguration.getType())) { + LOG.debug("Type is: " + TYPE); + if (!skopeoInputValidator.validateLocation(location)) { + context.getUserMessages().add(new SecHubMessage(SecHubMessageType.WARNING, "Type is " + TYPE + " but location does not match URL pattern")); + LOG.warn("User defined type as " + TYPE + ", but the defined location was not a valid location: {}", location); + return false; + } + return true; + } + + if (skopeoInputValidator.validateLocation(location)) { + LOG.debug("Location is a " + TYPE + " URL"); + return true; + } + + } + return false; + } + + @Override + public void prepare(PrepareWrapperContext context) throws IOException { + LOG.debug("Start remote data preparation for GIT repository"); + + List remoteDataConfigurationList = context.getRemoteDataConfigurationList(); + + for (SecHubRemoteDataConfiguration secHubRemoteDataConfiguration : remoteDataConfigurationList) { + prepareRemoteConfiguration(context, secHubRemoteDataConfiguration); + } + + if (!isDownloadSuccessful(context)) { + throw new IOException("Download of git repository was not successful."); + } + } + + boolean isDownloadSuccessful(PrepareWrapperContext context) { + // check if download folder contains docker archive + Path path = Paths.get(context.getEnvironment().getPdsPrepareUploadFolderDirectory()); + if (Files.isDirectory(path)) { + String gitFile = "image.tar"; + Path gitPath = Paths.get(path + "/" + gitFile); + return Files.exists(gitPath); + } + return false; + } + + private void prepareRemoteConfiguration(PrepareWrapperContext context, SecHubRemoteDataConfiguration secHubRemoteDataConfiguration) throws IOException { + String location = secHubRemoteDataConfiguration.getLocation(); + Optional credentials = secHubRemoteDataConfiguration.getCredentials(); + + if (!credentials.isPresent()) { + downloadPublicImage(context, location); + return; + } + + Optional optUser = credentials.get().getUser(); + if (optUser.isPresent()) { + SecHubRemoteCredentialUserData user = optUser.get(); + downloadPrivateImage(context, user, location); + return; + } + + throw new IllegalStateException("Defined credentials have no credential user data for location: " + location); + } + + private void downloadPrivateImage(PrepareWrapperContext context, SecHubRemoteCredentialUserData user, String location) throws IOException { + assertUserCredentials(user); + + 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, "Cloned private repository: " + 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) { + skopeoInputValidator.validateUsername(user.getName()); + skopeoInputValidator.validatePassword(user.getPassword()); + } + + 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, "Cloned public repository: " + location); + context.getUserMessages().add(message); + } + + private boolean isMatchingSkopeoType(String type) { + if (type == null || type.isBlank()) { + return false; + } + return TYPE.equalsIgnoreCase(type); + } +} diff --git a/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/SkopeoContext.java b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/SkopeoContext.java new file mode 100644 index 000000000..34b8aefdc --- /dev/null +++ b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/SkopeoContext.java @@ -0,0 +1,36 @@ +package com.mercedesbenz.sechub.wrapper.prepare.modules; + +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 filename(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/SkopeoInputValidator.java b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/SkopeoInputValidator.java new file mode 100644 index 000000000..87bd00ca6 --- /dev/null +++ b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/SkopeoInputValidator.java @@ -0,0 +1,62 @@ +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 SkopeoInputValidator implements InputValidator { + + 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,72}$"; + private static final Pattern SKOPEO_PASSWORD_PATTERN = Pattern.compile(SKOPEO_PASSWORD_REGEX); + private final List defaultForbiddenCharacters = Arrays.asList(">", "<", "!", "?", "*", "'", "\"", ";", "&", "|", "`", "$", "{", "}"); + + @Override + public boolean validateLocation(String location) { + if (location == null || location.isBlank()) { + throw new IllegalStateException("Defined location must not be null or empty."); + } + return SKOPEO_LOCATION_PATTERN.matcher(location).matches(); + } + + @Override + public void validateUsername(String username) { + if (username == null || username.isBlank()) { + throw new IllegalStateException("Defined username must not be null or empty."); + } + if (!SKOPEO_USERNAME_PATTERN.matcher(username).matches()) { + throw new IllegalStateException("Defined username must match the modules pattern."); + } + } + + @Override + public void validatePassword(String password) { + if (password == null || password.isBlank()) { + throw new IllegalStateException("Defined password must not be null or empty."); + } + if (!SKOPEO_PASSWORD_PATTERN.matcher(password).matches()) { + throw new IllegalStateException("Defined password must match the Skopeo Api token pattern."); + } + } + + @Override + 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); + } + } + } +} diff --git a/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/WrapperSkopeo.java b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/WrapperSkopeo.java new file mode 100644 index 000000000..1a6c3a694 --- /dev/null +++ b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/WrapperSkopeo.java @@ -0,0 +1,156 @@ +package com.mercedesbenz.sechub.wrapper.prepare.modules; + +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.Value; +import org.springframework.stereotype.Component; + +import com.mercedesbenz.sechub.commons.pds.ProcessAdapter; + +@Component +public class WrapperSkopeo extends WrapperTool { + + @Value("${" + KEY_PDS_PREPARE_AUTHENTICATION_FILE_SKOPEO + ":authentication.json}") + String pdsPrepareAuthenticationFileSkopeo; + + void download(SkopeoContext context) throws IOException { + login(context); + + ProcessBuilder builder = buildProcessDownload(context); + ProcessAdapter process = null; + + try { + process = processAdapterFactory.startProcess(builder); + } catch (IOException e) { + throw new IOException("Error download with Skopeo from: " + context.getLocation(), e); + } + + waitForProcessToFinish(process); + } + + @Override + 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); + exportEnvironmentVariables(builder, context.getCredentialMap()); + 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) { + // skopeo login "$LOCATION" --username "$USERNAME" --password "$PASSWORD" + // --authfile "$PDS_JOB_WORKSPACE_LOCATION/$SKOPEO_AUTH" + List commands = new ArrayList<>(); + + String location = transformLocationForLogin(context.getLocation()); + File uploadDir = Paths.get(context.getUploadDirectory()).toAbsolutePath().toFile(); + + commands.add("/bin/bash"); + commands.add("-c"); + commands.add("skopeo"); + commands.add("login"); + commands.add(location); + commands.add("--username"); + commands.add("$" + PDS_PREPARE_CREDENTIAL_USERNAME); + commands.add("--password"); + commands.add("$" + 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) { + // skopeo copy docker://$LOCATION + // docker-archive:$PDS_JOB_WORKSPACE_LOCATION/$PDS_PREPARE_UPLOAD_FOLDER + List commands = new ArrayList<>(); + + String location = transformLocationForDownload(context.getLocation()); + File uploadDir = Paths.get(context.getUploadDirectory()).toAbsolutePath().toFile(); + + commands.add("/bin/bash"); + commands.add("-c"); + commands.add("skopeo"); + commands.add("copy"); + commands.add(location); + commands.add("docker-archive:" + context.getFilename()); + commands.add("--authfile"); + commands.add(pdsPrepareAuthenticationFileSkopeo); + + ProcessBuilder builder = new ProcessBuilder(commands); + builder.directory(uploadDir); + builder.inheritIO(); + + return builder; + } + + private ProcessBuilder buildProcessClean(String pdsPrepareUploadFolderDirectory) { + List commands = new ArrayList<>(); + + File uploadDir = Paths.get(pdsPrepareUploadFolderDirectory).toAbsolutePath().toFile(); + + commands.add("/bin/bash"); + commands.add("-c"); + commands.add("rm -rf " + 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; + } +} diff --git a/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/PrepareWrapperModuleSkopeoTest.java b/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/PrepareWrapperModuleSkopeoTest.java new file mode 100644 index 000000000..0e773f777 --- /dev/null +++ b/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/PrepareWrapperModuleSkopeoTest.java @@ -0,0 +1,25 @@ +package com.mercedesbenz.sechub.wrapper.prepare.modules; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mock; + +import org.junit.jupiter.api.BeforeEach; + +class PrepareWrapperModuleSkopeoTest { + + PrepareWrapperModuleSkopeo wrapperToTest; + SkopeoInputValidator skopeoInputValidator; + WrapperSkopeo skopeo; + + @BeforeEach + void beforeEach() { + wrapperToTest = new PrepareWrapperModuleSkopeo(); + skopeoInputValidator = mock(SkopeoInputValidator.class); + skopeo = mock(WrapperSkopeo.class); + + wrapperToTest.skopeoInputValidator = skopeoInputValidator; + wrapperToTest.skopeo = skopeo; + } + + // TODO: 07.05.24 laura add tests +} \ No newline at end of file diff --git a/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/SkopeoInputValidatorTest.java b/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/SkopeoInputValidatorTest.java new file mode 100644 index 000000000..e2e7a91b8 --- /dev/null +++ b/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/SkopeoInputValidatorTest.java @@ -0,0 +1,72 @@ +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 SkopeoInputValidatorTest { + + SkopeoInputValidator validatorToTest; + + @BeforeEach + void beforeEach() { + 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 */ + assertTrue(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_returns_false_for_invalid_docker_urls(String location) { + /* execute + test */ + assertFalse(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 */ + IllegalStateException exception = assertThrows(IllegalStateException.class, () -> validatorToTest.validateUsername(username)); + + /* test */ + assertEquals("Defined username must match the modules 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 */ + IllegalStateException exception = assertThrows(IllegalStateException.class, () -> validatorToTest.validatePassword(password)); + + /* test */ + assertEquals("Defined password must match the Skopeo Api token pattern.", exception.getMessage()); + } +} diff --git a/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/WrapperSkopeoTest.java b/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/WrapperSkopeoTest.java new file mode 100644 index 000000000..da52f68de --- /dev/null +++ b/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/WrapperSkopeoTest.java @@ -0,0 +1,28 @@ +package com.mercedesbenz.sechub.wrapper.prepare.modules; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.io.IOException; + +import org.junit.jupiter.api.BeforeEach; + +import com.mercedesbenz.sechub.commons.pds.PDSProcessAdapterFactory; + +class WrapperSkopeoTest { + + WrapperSkopeo wrapperToTest; + PDSProcessAdapterFactory processAdapterFactory; + + @BeforeEach + void beforeEach() throws IOException { + wrapperToTest = new WrapperSkopeo(); + processAdapterFactory = mock(PDSProcessAdapterFactory.class); + doNothing().when(processAdapterFactory).startProcess(any()); + + wrapperToTest.processAdapterFactory = processAdapterFactory; + } + + // TODO: 07.05.24 laura add tests + +} \ No newline at end of file