Skip to content

Commit

Permalink
Refactoring + changes in validation #3026
Browse files Browse the repository at this point in the history
- validation now without inheritance by introducing input validation
  support class
- renamed some classes - e.g. WrapperSkopeo -> SkopeoWrapper
- changed tests for validation
  • Loading branch information
de-jcup committed Jun 3, 2024
1 parent c0d27f6 commit ffa77bd
Show file tree
Hide file tree
Showing 24 changed files with 627 additions and 389 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// SPDX-License-Identifier: MIT
package com.mercedesbenz.sechub.wrapper.prepare.modules;
package com.mercedesbenz.sechub.wrapper.prepare;

import static com.mercedesbenz.sechub.wrapper.prepare.modules.InputValidatorExitcode.*;
import static com.mercedesbenz.sechub.wrapper.prepare.modules.UsageExceptionExitCode.*;
Expand All @@ -11,37 +10,41 @@

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.mercedesbenz.sechub.commons.model.*;
import com.mercedesbenz.sechub.pds.commons.core.PDSLogSanitizer;
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.wrapper.prepare.modules.LogSanitizerProvider;
import com.mercedesbenz.sechub.wrapper.prepare.modules.PrepareWrapperInputValidatorException;
import com.mercedesbenz.sechub.wrapper.prepare.modules.PrepareWrapperUsageException;
import com.mercedesbenz.sechub.wrapper.prepare.prepare.PrepareWrapperContext;

public class AbstractInputValidator implements InputValidator {
public class InputValidationSupport {

private static final Logger LOG = LoggerFactory.getLogger(InputValidationSupport.class);
private String type;

private Pattern locationPattern;
private Pattern usernamePattern;
private Pattern passwordPattern;

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<String> forbiddenCharacters = Collections
.unmodifiableList(Arrays.asList(">", "<", "!", "?", "*", "'", "\"", ";", "&", "|", "`", "$", "{", "}"));

@Autowired
public PDSLogSanitizer pdsLogSanitizer;
private LogSanitizerProvider logSanitizerProvider;

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.");
}
public static InputValidationSupportBuilder builder() {
return new InputValidationSupportBuilder();
}

this.TYPE = type;
this.LOCATION_PATTERN = locationPattern;
this.USERNAME_PATTERN = usernamePattern;
this.PASSWORD_PATTERN = passwordPattern;
private static boolean isTypeNullOrEmpty(String type) {
return type == null || type.isEmpty();
}

private static void assertPatternNotNull(Pattern pattern) {
if (pattern == null) {
throw new IllegalArgumentException("Pattern must not be null.");
}
}

public void validate(PrepareWrapperContext context) throws PrepareWrapperInputValidatorException {
Expand All @@ -55,15 +58,19 @@ private void validateModule(PrepareWrapperContext context) throws PrepareWrapper
String type = secHubRemoteDataConfiguration.getType();

if (isTypeNullOrEmpty(type)) {
LOG.debug("No type was defined for location {}.", pdsLogSanitizer.sanitize(location, 1024));
if (LOG.isDebugEnabled()) {
LOG.debug("No type was defined for location {}.", logSanitizerProvider.getLogSanitizer().sanitize(location, 1024));
}
validateLocation(location);
return;
} else if (isMatchingType(type)) {
LOG.debug("Type is matching type {}. Location is: {}", TYPE, pdsLogSanitizer.sanitize(location, 1024));
if (LOG.isDebugEnabled()) {
LOG.debug("Type is matching type {}. Location is: {}", type, logSanitizerProvider.getLogSanitizer().sanitize(location, 1024));
}
validateLocation(location);
return;
}
throw new PrepareWrapperInputValidatorException("Defined type " + type + " was not modules type " + TYPE + ".", TYPE_NOT_MATCHING_PATTERN);
throw new PrepareWrapperInputValidatorException("Defined type " + type + " was not modules type " + type + ".", TYPE_NOT_MATCHING_PATTERN);
}

private void validateCredentials(PrepareWrapperContext context) throws PrepareWrapperInputValidatorException {
Expand All @@ -82,46 +89,42 @@ private void validateCredentials(PrepareWrapperContext context) throws PrepareWr
}
}

public void validateUsername(String username) throws PrepareWrapperInputValidatorException {
private void validateUsername(String username) throws PrepareWrapperInputValidatorException {
if (username == null || username.isBlank()) {
throw new PrepareWrapperUsageException("Defined username must not be null or empty. Username is required for login.",
CREDENTIAL_USER_NAME_NOT_DEFINED);
}

if (!USERNAME_PATTERN.matcher(username).matches()) {
throw new PrepareWrapperInputValidatorException("Defined username must match the " + TYPE + " pattern.", CREDENTIALS_USERNAME_NOT_MATCHING_PATTERN);
if (!usernamePattern.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 {
private void validatePassword(String password) throws PrepareWrapperInputValidatorException {
if (password == null || password.isBlank()) {
throw new PrepareWrapperUsageException("Defined password must not be null or empty. Password is required for login.",
CREDENTIAL_USER_PASSWORD_NOT_DEFINED);
}

if (!PASSWORD_PATTERN.matcher(password).matches()) {
throw new PrepareWrapperInputValidatorException("Defined password must match the " + TYPE + " Api token pattern.",
if (!passwordPattern.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 {
private void validateLocation(String location) throws PrepareWrapperInputValidatorException {
if (location == null || location.isBlank()) {
throw new PrepareWrapperUsageException("Defined location must not be null or empty. Location is required for download remote data.",
LOCATION_NOT_DEFINED);
}
validateLocationCharacters(location);
if (!LOCATION_PATTERN.matcher(location).matches()) {
throw new PrepareWrapperInputValidatorException("Defined location must match the " + TYPE + " pattern.", LOCATION_NOT_MATCHING_PATTERN);
if (!locationPattern.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);
return type.equalsIgnoreCase(type);
}

private void validateLocationCharacters(String url) {
Expand All @@ -136,9 +139,66 @@ private void validateLocationCharacters(String url) {
}
}

private void assertPatternNotNull(Pattern pattern) {
if (pattern == null) {
throw new IllegalArgumentException("Pattern must not be null.");
public static class InputValidationSupportBuilder {
private String type;

private Pattern locationPattern;
private Pattern usernamePattern;
private Pattern passwordPattern;

private LogSanitizerProvider logSanitizerProvider;

private InputValidationSupportBuilder() {

}

public InputValidationSupportBuilder setLocationPattern(Pattern locationPattern) {
this.locationPattern = locationPattern;
return this;
}

public InputValidationSupportBuilder setUserNamePattern(Pattern usernamePattern) {
this.usernamePattern = usernamePattern;
return this;
}

public InputValidationSupportBuilder setPasswordPattern(Pattern passwordPattern) {
this.passwordPattern = passwordPattern;
return this;
}

public InputValidationSupportBuilder setType(String type) {
this.type = type;
return this;
}

public InputValidationSupportBuilder setLogSanitizerProvider(LogSanitizerProvider logSanitizerProvider) {
this.logSanitizerProvider = logSanitizerProvider;
return this;
}

public InputValidationSupport build() {
assertPatternNotNull(locationPattern);
assertPatternNotNull(usernamePattern);
assertPatternNotNull(passwordPattern);

if (isTypeNullOrEmpty(type)) {
throw new IllegalArgumentException("Type must not be null or empty.");
}

if (logSanitizerProvider == null) {
throw new IllegalArgumentException("Log sanitizer provider not defined");
}

InputValidationSupport result = new InputValidationSupport();
result.type = type;
result.locationPattern = locationPattern;
result.usernamePattern = usernamePattern;
result.passwordPattern = passwordPattern;
result.logSanitizerProvider = logSanitizerProvider;

return result;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ 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
* Flag to enable the skopeoWrapper prepare module
*/
public static final String KEY_PDS_PREPARE_MODULE_SKOPEO_ENABLED = "pds.prepare.module.skopeo.enabled";

Expand All @@ -26,7 +26,7 @@ public class PrepareWrapperKeyConstants {
public static final String KEY_PDS_PREPARE_AUTO_CLEANUP_GIT_FOLDER = "pds.prepare.auto.cleanup.git.folder";

/**
* Filename for skopeo authentication file
* Filename for skopeoWrapper authentication file
*/
public static final String KEY_PDS_PREPARE_AUTHENTICATION_FILE_SKOPEO = "pds.prepare.authentication.file.skopeo";
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
import com.mercedesbenz.sechub.wrapper.prepare.prepare.PrepareWrapperContext;

public interface InputValidator {

void validate(PrepareWrapperContext context) throws PrepareWrapperInputValidatorException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.mercedesbenz.sechub.wrapper.prepare.modules;

import com.mercedesbenz.sechub.pds.commons.core.PDSLogSanitizer;

public interface LogSanitizerProvider {

public PDSLogSanitizer getLogSanitizer();
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,24 @@
@Service
public interface PrepareWrapperModule {

/**
* Tries to prepare
*
* @param context
* @return <code>false</code> when prepare was not possible, <code>true</code>
* when prepare was done
* @throws IOException
*/
boolean prepare(PrepareWrapperContext context) throws IOException;

String getPreparationDoneDescription();

default void addSealedUserCredentials(SecHubRemoteCredentialUserData user, HashMap<String, SealedObject> credentialMap) {

SealedObject sealedUsername = CryptoAccess.CRYPTO_STRING.seal(user.getName());
SealedObject sealedPassword = CryptoAccess.CRYPTO_STRING.seal(user.getPassword());
credentialMap.put(PDS_PREPARE_CREDENTIAL_USERNAME, sealedUsername);

SealedObject sealedPassword = CryptoAccess.CRYPTO_STRING.seal(user.getPassword());
credentialMap.put(PDS_PREPARE_CREDENTIAL_PASSWORD, sealedPassword);
}

Expand All @@ -39,4 +51,5 @@ default void createDownloadDirectory(Path path) {
throw new RuntimeException("Error while creating download directory: " + path, e);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
import com.mercedesbenz.sechub.commons.pds.ProcessAdapter;

@Component
public abstract class WrapperTool {
public abstract class ToolWrapper {

private static final Logger LOG = LoggerFactory.getLogger(WrapperTool.class);
private static final Logger LOG = LoggerFactory.getLogger(ToolWrapper.class);
private static final int defaultMinutesToWaitForProduct = PDSDefaultParameterValueConstants.DEFAULT_MINUTES_TO_WAIT_FOR_PRODUCT;

@Value("${" + PARAM_KEY_PDS_CONFIG_PRODUCT_TIMEOUT_MINUTES + ":" + defaultMinutesToWaitForProduct + "}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,55 @@

import java.util.regex.Pattern;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.mercedesbenz.sechub.commons.model.*;
import com.mercedesbenz.sechub.wrapper.prepare.modules.AbstractInputValidator;
import com.mercedesbenz.sechub.pds.commons.core.PDSLogSanitizer;
import com.mercedesbenz.sechub.wrapper.prepare.InputValidationSupport;
import com.mercedesbenz.sechub.wrapper.prepare.modules.InputValidator;
import com.mercedesbenz.sechub.wrapper.prepare.modules.LogSanitizerProvider;
import com.mercedesbenz.sechub.wrapper.prepare.modules.PrepareWrapperInputValidatorException;
import com.mercedesbenz.sechub.wrapper.prepare.prepare.PrepareWrapperContext;

@Component
public class GitInputValidator extends AbstractInputValidator {
public class GitInputValidator implements InputValidator, LogSanitizerProvider {

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);

private InputValidationSupport support;

@Autowired
PDSLogSanitizer logSanitizer;

public GitInputValidator() {
super(TYPE, GIT_LOCATION_PATTERN, GIT_USERNAME_PATTERN, GIT_PASSWORD_PATTERN);
/* @formatter:off */
this.support = InputValidationSupport.builder().
setType(TYPE).
setLogSanitizerProvider(this).
setLocationPattern(GIT_LOCATION_PATTERN).
setUserNamePattern(GIT_USERNAME_PATTERN).
setPasswordPattern(GIT_PASSWORD_PATTERN).
build();
/* @formatter:on */
}

@Override
public void validate(PrepareWrapperContext context) throws PrepareWrapperInputValidatorException {
support.validate(context);

}

@Override
public PDSLogSanitizer getLogSanitizer() {
return logSanitizer;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@

import com.mercedesbenz.sechub.commons.pds.PDSProcessAdapterFactory;
import com.mercedesbenz.sechub.commons.pds.ProcessAdapter;
import com.mercedesbenz.sechub.wrapper.prepare.modules.WrapperTool;
import com.mercedesbenz.sechub.wrapper.prepare.modules.ToolWrapper;

@Component
public class WrapperGit extends WrapperTool {
public class GitWrapper extends ToolWrapper {

private static final Logger LOG = LoggerFactory.getLogger(WrapperGit.class);
private static final Logger LOG = LoggerFactory.getLogger(GitWrapper.class);

@Autowired
JGitAdapter jGitAdapter;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public class PrepareWrapperModuleGit implements PrepareWrapperModule {
private boolean pdsPrepareModuleGitEnabled;

@Autowired
WrapperGit git;
GitWrapper git;

@Autowired
GitInputValidator gitInputValidator;
Expand Down Expand Up @@ -165,4 +165,14 @@ private void cleanup(GitContext gitContext) throws IOException {
git.cleanUploadDirectory(gitContext.getToolDownloadDirectory());
}
}

@Override
public String getPreparationDoneDescription() {
return "Git";
}

@Override
public String toString() {
return getClass().getSimpleName();
}
}
Loading

0 comments on commit ffa77bd

Please sign in to comment.