From a0faba42af9c22576e649a124b310f92a80a706e Mon Sep 17 00:00:00 2001 From: Laura Date: Wed, 8 May 2024 15:42:37 +0200 Subject: [PATCH] Added tests and tested skopeo moduel #3028 --- .../modules/PrepareWrapperModuleGit.java | 12 +- .../modules/PrepareWrapperModuleSkopeo.java | 51 ++- .../prepare/modules/SkopeoInputValidator.java | 2 +- .../prepare/modules/WrapperSkopeo.java | 45 ++- .../wrapper/prepare/modules/WrapperTool.java | 8 +- ...epareWrapperApplicationSpringBootTest.java | 3 +- .../modules/PrepareWrapperModuleGitTest.java | 1 - .../PrepareWrapperModuleSkopeoTest.java | 307 +++++++++++++++++- .../prepare/modules/WrapperSkopeoTest.java | 74 ++++- .../application-test-fail.properties | 3 +- 10 files changed, 458 insertions(+), 48 deletions(-) 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 238162e401..86d1aadbc1 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 @@ -50,10 +50,11 @@ public boolean isAbleToPrepare(PrepareWrapperContext context) { for (SecHubRemoteDataConfiguration secHubRemoteDataConfiguration : context.getRemoteDataConfigurationList()) { String location = secHubRemoteDataConfiguration.getLocation(); + String type = secHubRemoteDataConfiguration.getType(); gitInputValidator.validateLocationCharacters(location, null); - if (isMatchingGitType(secHubRemoteDataConfiguration.getType())) { + if (isMatchingGitType(type)) { 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")); @@ -63,6 +64,11 @@ public boolean isAbleToPrepare(PrepareWrapperContext context) { return true; } + if (!isTypeNullOrEmpty(type)) { + // type was explicitly defined but is not matching + return false; + } + if (gitInputValidator.validateLocation(location)) { LOG.debug("Location is a git URL"); return true; @@ -106,6 +112,10 @@ boolean isDownloadSuccessful(PrepareWrapperContext context) { return false; } + private boolean isTypeNullOrEmpty(String type) { + return type == null || type.isBlank(); + } + private void prepareRemoteConfiguration(PrepareWrapperContext context, SecHubRemoteDataConfiguration secHubRemoteDataConfiguration) throws IOException { String location = secHubRemoteDataConfiguration.getLocation(); Optional credentials = secHubRemoteDataConfiguration.getCredentials(); 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 index 9bfb9bbd71..22e6375644 100644 --- 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 @@ -11,6 +11,7 @@ import java.util.HashMap; import java.util.List; import java.util.Optional; +import java.util.stream.Stream; import javax.crypto.SealedObject; @@ -50,10 +51,11 @@ public boolean isAbleToPrepare(PrepareWrapperContext context) { for (SecHubRemoteDataConfiguration secHubRemoteDataConfiguration : context.getRemoteDataConfigurationList()) { String location = secHubRemoteDataConfiguration.getLocation(); + String type = secHubRemoteDataConfiguration.getType(); skopeoInputValidator.validateLocationCharacters(location, null); - if (isMatchingSkopeoType(secHubRemoteDataConfiguration.getType())) { + if (isMatchingSkopeoType(type)) { 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")); @@ -63,18 +65,22 @@ public boolean isAbleToPrepare(PrepareWrapperContext context) { return true; } + if (!isTypeNullOrEmpty(type)) { + // type was explicitly defined but is not matching + return false; + } + 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"); + LOG.debug("Start remote data preparation for Docker repository"); List remoteDataConfigurationList = context.getRemoteDataConfigurationList(); @@ -83,21 +89,43 @@ public void prepare(PrepareWrapperContext context) throws IOException { } if (!isDownloadSuccessful(context)) { - throw new IOException("Download of git repository was not successful."); + throw new IOException("Download of docker image was not successful."); } + cleanup(context); } boolean isDownloadSuccessful(PrepareWrapperContext context) { - // check if download folder contains docker archive + // check if download folder contains a .tar 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); + 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) { + throw new RuntimeException("Error while checking download of docker image", e); + } } return false; } + boolean isMatchingSkopeoType(String type) { + if (type == null || type.isBlank()) { + return false; + } + return TYPE.equalsIgnoreCase(type); + } + + private boolean isTypeNullOrEmpty(String type) { + return type == null || type.isBlank(); + } + + 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(); @@ -162,11 +190,4 @@ private void downloadPublicImage(PrepareWrapperContext context, String location) 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/SkopeoInputValidator.java b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/SkopeoInputValidator.java index 87bd00ca63..e0c8a8711d 100644 --- 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 @@ -13,7 +13,7 @@ public class SkopeoInputValidator implements InputValidator { 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 String SKOPEO_PASSWORD_REGEX = "^[a-zA-Z0-9-_\\d]{0,80}$"; private static final Pattern SKOPEO_PASSWORD_PATTERN = Pattern.compile(SKOPEO_PASSWORD_REGEX); private final List defaultForbiddenCharacters = Arrays.asList(">", "<", "!", "?", "*", "'", "\"", ";", "&", "|", "`", "$", "{", "}"); 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 index 1a6c3a6946..0f7c0e991e 100644 --- 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 @@ -21,7 +21,9 @@ public class WrapperSkopeo extends WrapperTool { String pdsPrepareAuthenticationFileSkopeo; void download(SkopeoContext context) throws IOException { - login(context); + if (!context.getCredentialMap().isEmpty()) { + login(context); + } ProcessBuilder builder = buildProcessDownload(context); ProcessAdapter process = null; @@ -29,7 +31,7 @@ void download(SkopeoContext context) throws IOException { try { process = processAdapterFactory.startProcess(builder); } catch (IOException e) { - throw new IOException("Error download with Skopeo from: " + context.getLocation(), e); + throw new IOException("Error while download with Skopeo from: " + context.getLocation(), e); } waitForProcessToFinish(process); @@ -73,15 +75,16 @@ private ProcessBuilder buildProcessLogin(SkopeoContext context) { 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); + commands.add("skopeo login " + location + " --username $" + PDS_PREPARE_CREDENTIAL_USERNAME + " --password $" + PDS_PREPARE_CREDENTIAL_PASSWORD + + " --authfile " + pdsPrepareAuthenticationFileSkopeo); + + /* + * 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); @@ -98,14 +101,23 @@ private ProcessBuilder buildProcessDownload(SkopeoContext context) { String location = transformLocationForDownload(context.getLocation()); File uploadDir = Paths.get(context.getUploadDirectory()).toAbsolutePath().toFile(); - commands.add("/bin/bash"); - commands.add("-c"); + /* + * commands.add("/bin/bash"); commands.add("-c"); if + * (!context.getCredentialMap().isEmpty()){ commands.add("skopeo copy " + + * location + " docker-archive:" + context.getFilename()); }else{ + * commands.add("skopeo copy " + location + " docker-archive:" + + * context.getFilename() + " --authfile " + pdsPrepareAuthenticationFileSkopeo); + * } + */ + commands.add("skopeo"); commands.add("copy"); commands.add(location); commands.add("docker-archive:" + context.getFilename()); - commands.add("--authfile"); - commands.add(pdsPrepareAuthenticationFileSkopeo); + if (!context.getCredentialMap().isEmpty()) { + commands.add("--authfile"); + commands.add(pdsPrepareAuthenticationFileSkopeo); + } ProcessBuilder builder = new ProcessBuilder(commands); builder.directory(uploadDir); @@ -115,6 +127,7 @@ private ProcessBuilder buildProcessDownload(SkopeoContext context) { } private ProcessBuilder buildProcessClean(String pdsPrepareUploadFolderDirectory) { + // removes authentication file List commands = new ArrayList<>(); File uploadDir = Paths.get(pdsPrepareUploadFolderDirectory).toAbsolutePath().toFile(); @@ -151,6 +164,6 @@ private String transformLocationForLogin(String location) { if (location.startsWith("https://")) { location = location.replace("https://", ""); } - return location; + return location.split("/")[0]; } } diff --git a/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/WrapperTool.java b/sechub-wrapper-prepare/src/main/java/com/mercedesbenz/sechub/wrapper/prepare/modules/WrapperTool.java index ae03940deb..6a132b092f 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 @@ -46,15 +46,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()); + throw new RuntimeException( + "Wrapper for executed modul " + this.getClass().getSimpleName() + " process failed with exit code: " + process.exitValue()); } } 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 54ca94e648..0febd7dec8 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 @@ -23,7 +23,8 @@ @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/PrepareWrapperModuleGitTest.java b/sechub-wrapper-prepare/src/test/java/com/mercedesbenz/sechub/wrapper/prepare/modules/PrepareWrapperModuleGitTest.java index 162b906505..fa5fb0b42e 100644 --- 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 @@ -147,7 +147,6 @@ void prepare_throws_exception_when_credentials_are_empty() { remoteDataConfiguration.setType("git"); remoteDataConfigurationList.add(remoteDataConfiguration); context.setRemoteDataConfigurationList(remoteDataConfigurationList); - ReflectionTestUtils.setField(moduleToTest, "pdsPrepareModuleGitEnabled", true); /* execute */ IllegalStateException exception = assertThrows(IllegalStateException.class, () -> moduleToTest.prepare(context)); 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 index 0e773f7770..f1ae977704 100644 --- 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 @@ -1,25 +1,320 @@ 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.mock; +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 java.util.ArrayList; +import java.util.List; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.test.util.ReflectionTestUtils; + +import com.mercedesbenz.sechub.commons.model.SecHubConfigurationModel; +import com.mercedesbenz.sechub.commons.model.SecHubRemoteCredentialConfiguration; +import com.mercedesbenz.sechub.commons.model.SecHubRemoteCredentialUserData; +import com.mercedesbenz.sechub.commons.model.SecHubRemoteDataConfiguration; +import com.mercedesbenz.sechub.test.TestFileWriter; +import com.mercedesbenz.sechub.wrapper.prepare.cli.PrepareWrapperEnvironment; +import com.mercedesbenz.sechub.wrapper.prepare.prepare.PrepareWrapperContext; +import com.mercedesbenz.sechub.wrapper.prepare.prepare.PrepareWrapperRemoteConfigurationExtractor; class PrepareWrapperModuleSkopeoTest { - PrepareWrapperModuleSkopeo wrapperToTest; + PrepareWrapperModuleSkopeo moduleToTest; SkopeoInputValidator skopeoInputValidator; WrapperSkopeo skopeo; + TestFileWriter writer; + @BeforeEach void beforeEach() { - wrapperToTest = new PrepareWrapperModuleSkopeo(); - skopeoInputValidator = mock(SkopeoInputValidator.class); + moduleToTest = new PrepareWrapperModuleSkopeo(); + skopeoInputValidator = new SkopeoInputValidator(); + writer = new TestFileWriter(); skopeo = mock(WrapperSkopeo.class); - wrapperToTest.skopeoInputValidator = skopeoInputValidator; - wrapperToTest.skopeo = skopeo; + moduleToTest.skopeoInputValidator = skopeoInputValidator; + moduleToTest.skopeo = skopeo; } // TODO: 07.05.24 laura add tests + + @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 isAbleToPrepare_returnsFalse_whenSkopeoModuleIsDisabled(String location) { + /* prepare */ + PrepareWrapperContext context = createContextWithRemoteDataConfig(location); + ReflectionTestUtils.setField(moduleToTest, "pdsPrepareModuleSkopeoEnabled", false); + + /* execute */ + boolean result = moduleToTest.isAbleToPrepare(context); + + /* test */ + assertFalse(result); + } + + @Test + void isAbleToPrepare_returnsFalse_whenNoRemoteDataConfigurationIsAvailable() { + /* prepare */ + PrepareWrapperContext context = createContextEmptyConfig(); + ReflectionTestUtils.setField(moduleToTest, "pdsPrepareModuleSkopeoEnabled", true); + + /* execute */ + boolean result = moduleToTest.isAbleToPrepare(context); + + /* test */ + assertFalse(result); + } + + @ParameterizedTest + @ValueSource(strings = { "http://host.xz/path/to/repo.git/", "git://host.xz/path/to/repo.git/", "git@host.com:my-repo/example.git" }) + void isAbleToPrepare_returnsFalse_whenRemoteDataConfigurationIsNotSkopeo(String location) { + /* prepare */ + PrepareWrapperContext context = createContextWithRemoteDataConfig(location); + ReflectionTestUtils.setField(moduleToTest, "pdsPrepareModuleSkopeoEnabled", true); + + /* execute */ + boolean result = moduleToTest.isAbleToPrepare(context); + + /* test */ + assertFalse(result); + } + + @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 isAbleToPrepare_returnsTrue_whenRemoteDataConfigurationIsSkopeo(String location) { + /* prepare */ + PrepareWrapperContext context = createContextWithRemoteDataConfig(location); + ReflectionTestUtils.setField(moduleToTest, "pdsPrepareModuleSkopeoEnabled", true); + + /* execute */ + boolean result = moduleToTest.isAbleToPrepare(context); + + /* test */ + assertTrue(result); + } + + @Test + void prepare_throws_exception_when_credentials_are_empty() { + /* prepare */ + PrepareWrapperContext context = createContextEmptyConfig(); + List remoteDataConfigurationList = new ArrayList<>(); + SecHubRemoteDataConfiguration remoteDataConfiguration = new SecHubRemoteDataConfiguration(); + SecHubRemoteCredentialConfiguration credentials = new SecHubRemoteCredentialConfiguration(); + remoteDataConfiguration.setCredentials(credentials); + remoteDataConfiguration.setLocation("my-example-location"); + remoteDataConfiguration.setType("docker"); + remoteDataConfigurationList.add(remoteDataConfiguration); + context.setRemoteDataConfigurationList(remoteDataConfigurationList); + + /* 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 = createContextEmptyConfig(); + 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("docker"); + 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 = createContextEmptyConfig(); + 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 = "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); + + 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("docker"); + remoteDataConfigurationList.add(remoteDataConfiguration); + context.setRemoteDataConfigurationList(remoteDataConfigurationList); + + /* 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); + + List remoteDataConfigurationList = new ArrayList<>(); + SecHubRemoteDataConfiguration remoteDataConfiguration = new SecHubRemoteDataConfiguration(); + SecHubRemoteCredentialUserData user = new SecHubRemoteCredentialUserData(); + user.setName("my-example-name"); + user.setPassword("ghp_exampleAPITOKEN8ffne3l6g9f393r8fbcsf"); + remoteDataConfiguration.setLocation("my-example-location"); + remoteDataConfiguration.setType("docker"); + remoteDataConfigurationList.add(remoteDataConfiguration); + context.setRemoteDataConfigurationList(remoteDataConfigurationList); + + /* execute */ + moduleToTest.prepare(context); + + /* test */ + verify(skopeo).download(any(SkopeoContext.class)); + verify(skopeo).cleanUploadDirectory(tempDir.toString()); + } + + @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); + } + + @ParameterizedTest + @ValueSource(strings = { "docker", "DOCKER", "DockEr" }) + void isMatchingSkopeoType_returns_true_when_docker_is_configured(String type) { + /* execute */ + boolean result = moduleToTest.isMatchingSkopeoType(type); + + /* test */ + assertTrue(result); + } + + private PrepareWrapperContext createContextEmptyConfig() { + PrepareWrapperEnvironment environment = mock(PrepareWrapperEnvironment.class); + when(environment.getPdsPrepareUploadFolderDirectory()).thenReturn("test-upload-folder"); + return new PrepareWrapperContext(createFromJSON("{}"), environment); + } + + private PrepareWrapperContext createContextWithRemoteDataConfig(String location) { + String json = """ + { + "apiVersion": "1.0", + "data": { + "sources": [ + { + "name": "remote_example_name", + "remote": { + "location": "$location", + "type": "docker" + } + } + ] + }, + "codeScan": { + "use": [ + "remote_example_name" + ] + } + } + """.replace("$location", location); + SecHubConfigurationModel model = createFromJSON(json); + PrepareWrapperEnvironment environment = mock(PrepareWrapperEnvironment.class); + PrepareWrapperRemoteConfigurationExtractor extractor = new PrepareWrapperRemoteConfigurationExtractor(); + List creds = extractor.extract(model); + when(environment.getPdsPrepareUploadFolderDirectory()).thenReturn("test-upload-folder"); + PrepareWrapperContext context = new PrepareWrapperContext(model, environment); + context.setRemoteDataConfigurationList(creds); + return context; + } } \ No newline at end of file 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 index da52f68de9..d837079d3e 100644 --- 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 @@ -1,28 +1,96 @@ 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 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 { + void beforeEach() throws IOException, InterruptedException { wrapperToTest = new WrapperSkopeo(); processAdapterFactory = mock(PDSProcessAdapterFactory.class); - doNothing().when(processAdapterFactory).startProcess(any()); + processAdapter = mock(ProcessAdapter.class); + when(processAdapterFactory.startProcess(any())).thenReturn(processAdapter); + when(processAdapter.waitFor(any(Long.class), any(TimeUnit.class))).thenReturn(true); wrapperToTest.processAdapterFactory = processAdapterFactory; } - // TODO: 07.05.24 laura add tests + @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/resources/application-test-fail.properties b/sechub-wrapper-prepare/src/test/resources/application-test-fail.properties index e0df022af3..7876fb0f87 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