Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: resolve ecosystem based on package type #120

Merged
merged 1 commit into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
import io.snyk.plugins.artifactory.configuration.UserAgent;
import io.snyk.plugins.artifactory.configuration.properties.ArtifactProperty;
import io.snyk.plugins.artifactory.configuration.ConfigurationModule;
import io.snyk.plugins.artifactory.ecosystem.Ecosystem;
import io.snyk.plugins.artifactory.exception.CannotScanException;
import io.snyk.plugins.artifactory.exception.SnykAPIFailureException;
import io.snyk.plugins.artifactory.exception.SnykRuntimeException;
import io.snyk.plugins.artifactory.scanner.ScannerModule;
import io.snyk.plugins.artifactory.scanner.*;
import io.snyk.sdk.SnykConfig;
import io.snyk.sdk.api.v1.SnykClient;
import io.snyk.sdk.api.v1.SnykResult;
Expand Down Expand Up @@ -63,7 +64,8 @@ public SnykPlugin(@Nonnull Repositories repositories, File pluginsDirectory) {
final SnykClient snykClient = createSnykClient(configurationModule, pluginVersion);

auditModule = new AuditModule();
scannerModule = new ScannerModule(configurationModule, repositories, snykClient);
ScannerResolver scannerResolver = ScannerResolver.setup(configurationModule, snykClient);
scannerModule = new ScannerModule(configurationModule, repositories, scannerResolver);

LOG.info("Plugin version: {}", pluginVersion);
} catch (Exception ex) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
package io.snyk.plugins.artifactory.scanner;
package io.snyk.plugins.artifactory.ecosystem;

import io.snyk.plugins.artifactory.configuration.PluginConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Optional;

public enum Ecosystem {


MAVEN(PluginConfiguration.SCANNER_PACKAGE_TYPE_MAVEN),
NPM(PluginConfiguration.SCANNER_PACKAGE_TYPE_NPM),
PYPI(PluginConfiguration.SCANNER_PACKAGE_TYPE_PYPI),
;

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

private final PluginConfiguration configProperty;

Ecosystem(PluginConfiguration configProperty) {
Expand All @@ -21,19 +26,14 @@ public PluginConfiguration getConfigProperty() {
return configProperty;
}

static Optional<Ecosystem> fromPackagePath(String path) {
if (path.endsWith(".jar")) {
return Optional.of(MAVEN);
}

if (path.endsWith(".tgz")) {
return Optional.of(NPM);
}

if (path.endsWith(".whl") || path.endsWith(".tar.gz") || path.endsWith(".zip") || path.endsWith(".egg")) {
return Optional.of(PYPI);
public static Optional<Ecosystem> fromPackageType(String artifactoryPackageType) {
switch (artifactoryPackageType.toLowerCase()) {
case "maven": return Optional.of(MAVEN);
case "npm": return Optional.of(NPM);
case "pypi": return Optional.of(PYPI);
}

LOG.error("Unknown package type: {}", artifactoryPackageType);
return Optional.empty();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package io.snyk.plugins.artifactory.ecosystem;

import org.artifactory.repo.RepoPath;

import java.util.Optional;

public interface EcosystemResolver {

Optional<Ecosystem> getFor(RepoPath repoPath);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package io.snyk.plugins.artifactory.ecosystem;

import org.artifactory.repo.RepoPath;
import org.artifactory.repo.Repositories;
import org.artifactory.repo.RepositoryConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Optional;

public class RepositoryMetadataEcosystemResolver implements EcosystemResolver {

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

private final Repositories repositories;

public RepositoryMetadataEcosystemResolver(Repositories repositories) {
this.repositories = repositories;
}

@Override
public Optional<Ecosystem> getFor(RepoPath repoPath) {
RepositoryConfiguration repositoryConfiguration = repositories.getRepositoryConfiguration(repoPath.getRepoKey());
if(repositoryConfiguration == null) {
LOG.error("No repository configuration for {}", repoPath);
return Optional.empty();
}

String packageType = repositoryConfiguration.getPackageType();
if(packageType == null) {
LOG.error("No package type for {}", repoPath);
return Optional.empty();
}

return Ecosystem.fromPackageType(packageType);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
import org.artifactory.fs.FileLayoutInfo;
import org.artifactory.repo.RepoPath;

interface PackageScanner {
public interface PackageScanner {
TestResult scan(FileLayoutInfo fileLayoutInfo, RepoPath repoPath);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
import io.snyk.plugins.artifactory.configuration.PluginConfiguration;
import io.snyk.plugins.artifactory.configuration.properties.ArtifactProperties;
import io.snyk.plugins.artifactory.configuration.properties.RepositoryArtifactProperties;
import io.snyk.plugins.artifactory.exception.CannotScanException;
import io.snyk.plugins.artifactory.ecosystem.EcosystemResolver;
import io.snyk.plugins.artifactory.ecosystem.RepositoryMetadataEcosystemResolver;
import io.snyk.plugins.artifactory.model.Ignores;
import io.snyk.plugins.artifactory.model.MonitoredArtifact;
import io.snyk.plugins.artifactory.model.TestResult;
import io.snyk.plugins.artifactory.model.ValidationSettings;
import io.snyk.sdk.api.v1.SnykClient;
import org.artifactory.fs.FileLayoutInfo;
import org.artifactory.repo.RepoPath;
import org.artifactory.repo.Repositories;
Expand All @@ -21,25 +21,23 @@
import java.time.Duration;
import java.util.Optional;

import static java.lang.String.format;
import static java.util.Objects.requireNonNull;

public class ScannerModule {
private static final Logger LOG = LoggerFactory.getLogger(ScannerModule.class);
private final ConfigurationModule configurationModule;
private final Repositories repositories;
private final MavenScanner mavenScanner;
private final NpmScanner npmScanner;
private final PythonScanner pythonScanner;
private final EcosystemResolver ecosystemResolver;
private final ScannerResolver scannerResolver;
private final ArtifactResolver artifactResolver;

public ScannerModule(@Nonnull ConfigurationModule configurationModule, @Nonnull Repositories repositories, @Nonnull SnykClient snykClient) {
public ScannerModule(ConfigurationModule configurationModule, @Nonnull Repositories repositories, ScannerResolver scannerResolver) {
this.configurationModule = requireNonNull(configurationModule);
this.repositories = requireNonNull(repositories);

mavenScanner = new MavenScanner(configurationModule, snykClient);
npmScanner = new NpmScanner(configurationModule, snykClient);
pythonScanner = new PythonScanner(configurationModule, snykClient);
ecosystemResolver = new RepositoryMetadataEcosystemResolver(repositories);

this.scannerResolver = scannerResolver;

artifactResolver = shouldTestContinuously() ? new ArtifactCache(
durationHoursProperty(PluginConfiguration.TEST_FREQUENCY_HOURS, configurationModule),
Expand Down Expand Up @@ -68,7 +66,9 @@ private ArtifactProperties properties(RepoPath repoPath) {
}

private @NotNull Optional<MonitoredArtifact> runTest(RepoPath repoPath) {
return getScannerForPackageType(repoPath).map(scanner -> runTestWith(scanner, repoPath));
return ecosystemResolver.getFor(repoPath)
.flatMap(ecosystem -> scannerResolver.getFor(ecosystem))
.map(scanner -> runTestWith(scanner, repoPath));
}

private MonitoredArtifact runTestWith(PackageScanner scanner, RepoPath repoPath) {
Expand All @@ -88,33 +88,6 @@ private void filter(MonitoredArtifact artifact) {
return new MonitoredArtifact(repoPath.toString(), testResult, ignores);
}

protected Optional<PackageScanner> getScannerForPackageType(RepoPath repoPath) {
String path = Optional.ofNullable(repoPath.getPath())
.orElseThrow(() -> new CannotScanException("Path not provided."));
return getScannerForPackageType(path);
}

protected Optional<PackageScanner> getScannerForPackageType(String path) {
return Ecosystem.fromPackagePath(path).map(this::getScanner);
}

private PackageScanner getScanner(Ecosystem ecosystem) {
if (!configurationModule.getPropertyOrDefault(ecosystem.getConfigProperty()).equals("true")) {
throw new CannotScanException(format("Plugin Property \"%s\" is not \"true\".", ecosystem.getConfigProperty().propertyKey()));
}

switch (ecosystem) {
case MAVEN:
return mavenScanner;
case NPM:
return npmScanner;
case PYPI:
return pythonScanner;
default:
throw new IllegalStateException("Unsupported ecosystem: " + ecosystem.name());
}
}

private boolean shouldTestContinuously() {
return configurationModule.getPropertyOrDefault(PluginConfiguration.TEST_CONTINUOUSLY).equals("true");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package io.snyk.plugins.artifactory.scanner;

import io.snyk.plugins.artifactory.configuration.ConfigurationModule;
import io.snyk.plugins.artifactory.configuration.PluginConfiguration;
import io.snyk.plugins.artifactory.ecosystem.Ecosystem;
import io.snyk.sdk.api.v1.SnykClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;

public class ScannerResolver {
private static final Logger LOG = LoggerFactory.getLogger(ScannerResolver.class);
private final Function<PluginConfiguration, String> getConfig;
private final Map<Ecosystem, PackageScanner> scannerByEcosystem = new HashMap<>();

public ScannerResolver(Function<PluginConfiguration, String> getConfig) {
this.getConfig = getConfig;
}

public ScannerResolver register(Ecosystem ecosystem, PackageScanner scanner) {
scannerByEcosystem.put(ecosystem, scanner);
return this;
}

public Optional<PackageScanner> getFor(Ecosystem ecosystem) {
PluginConfiguration configKey = ecosystem.getConfigProperty();
String configValue = getConfig.apply(configKey);
if (!"true".equals(configValue)) {
LOG.info("Snyk scanner disabled for {}. Config: {} = {}", ecosystem.name(), configKey.propertyKey(), configValue);
return Optional.empty();
}

PackageScanner scanner = scannerByEcosystem.get(ecosystem);

if (scanner == null) {
LOG.error("No scanner registered for {}", ecosystem.name());
}

return Optional.ofNullable(scanner);
}

public static ScannerResolver setup(ConfigurationModule configurationModule, SnykClient snykClient) {
return new ScannerResolver(configurationModule::getPropertyOrDefault)
.register(Ecosystem.MAVEN, new MavenScanner(configurationModule, snykClient))
.register(Ecosystem.NPM, new NpmScanner(configurationModule, snykClient))
.register(Ecosystem.PYPI, new PythonScanner(configurationModule, snykClient));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package io.snyk.plugins.artifactory.ecosystem;

import org.junit.jupiter.api.Test;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;

class EcosystemTest {

@Test
void ecosystemByPackageType() {
assertThat(Ecosystem.fromPackageType("maven")).contains(Ecosystem.MAVEN);
assertThat(Ecosystem.fromPackageType("npm")).contains(Ecosystem.NPM);
assertThat(Ecosystem.fromPackageType("pypi")).contains(Ecosystem.PYPI);
assertThat(Ecosystem.fromPackageType("nuget")).isEmpty();
}
}

This file was deleted.

Loading
Loading