diff --git a/core/src/main/java/org/owasp/dependencycheck/analyzer/OssIndexAnalyzer.java b/core/src/main/java/org/owasp/dependencycheck/analyzer/OssIndexAnalyzer.java index 265fb7c97c2..7b259ad661f 100644 --- a/core/src/main/java/org/owasp/dependencycheck/analyzer/OssIndexAnalyzer.java +++ b/core/src/main/java/org/owasp/dependencycheck/analyzer/OssIndexAnalyzer.java @@ -37,7 +37,9 @@ import org.owasp.dependencycheck.dependency.VulnerableSoftwareBuilder; import org.owasp.dependencycheck.dependency.naming.Identifier; import org.owasp.dependencycheck.dependency.naming.PurlIdentifier; +import org.owasp.dependencycheck.exception.InitializationException; import org.owasp.dependencycheck.utils.Settings; +import org.owasp.dependencycheck.utils.Settings.KEYS; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import us.springett.parsers.cpe.exceptions.CpeValidationException; @@ -127,6 +129,16 @@ protected void closeAnalyzer() throws Exception { } } + @Override + protected void prepareAnalyzer(Engine engine) throws InitializationException { + synchronized (FETCH_MUTIX) { + if (StringUtils.isEmpty(getSettings().getString(KEYS.ANALYZER_OSSINDEX_USER, StringUtils.EMPTY)) || + StringUtils.isEmpty(getSettings().getString(KEYS.ANALYZER_OSSINDEX_PASSWORD, StringUtils.EMPTY))) { + throw new InitializationException("Error initializing OSS Index analyzer due to missing user/password credentials. Authentication is now required: https://ossindex.sonatype.org/doc/auth-required"); + } + } + } + @Override protected void analyzeDependency(final Dependency dependency, final Engine engine) throws AnalysisException { // batch request component-reports for all dependencies diff --git a/core/src/test/java/org/owasp/dependencycheck/analyzer/OssIndexAnalyzerTest.java b/core/src/test/java/org/owasp/dependencycheck/analyzer/OssIndexAnalyzerTest.java index b56c9556b6b..8b8c34492ad 100644 --- a/core/src/test/java/org/owasp/dependencycheck/analyzer/OssIndexAnalyzerTest.java +++ b/core/src/test/java/org/owasp/dependencycheck/analyzer/OssIndexAnalyzerTest.java @@ -8,14 +8,19 @@ import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.naming.Identifier; import org.owasp.dependencycheck.dependency.naming.PurlIdentifier; +import org.owasp.dependencycheck.exception.InitializationException; import org.owasp.dependencycheck.utils.Settings; +import org.owasp.dependencycheck.utils.Settings.KEYS; + import org.sonatype.goodies.packageurl.PackageUrl; import org.sonatype.ossindex.service.api.componentreport.ComponentReport; import org.sonatype.ossindex.service.client.OssindexClient; import org.sonatype.ossindex.service.client.transport.Transport; import java.net.SocketTimeoutException; +import java.net.URI; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; @@ -25,6 +30,7 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; class OssIndexAnalyzerTest extends BaseTest { @@ -42,10 +48,12 @@ void should_enrich_be_included_in_mutex_to_prevent_NPE() Dependency dependency = new Dependency(); dependency.addSoftwareIdentifier(identifier); Settings settings = getSettings(); + setCredentials(settings); Engine engine = new Engine(settings); engine.setDependencies(Collections.singletonList(dependency)); analyzer.initialize(settings); + analyzer.prepareAnalyzer(engine); String expectedOutput = "https://ossindex.sonatype.org/component/pkg:maven/test/test@1.0"; @@ -75,6 +83,11 @@ void should_enrich_be_included_in_mutex_to_prevent_NPE() */ static final class SproutOssIndexAnalyzer extends OssIndexAnalyzer { private Future pendingClosureTask; + @Override + OssindexClient newOssIndexClient() { + return new OssIndexClientOk(); + } + @Override void enrich(Dependency dependency) { ExecutorService executor = Executors.newSingleThreadExecutor(); @@ -93,19 +106,46 @@ void awaitPendingClosure() throws ExecutionException, InterruptedException { } } + private static final class OssIndexClientOk implements OssindexClient { + + @Override + public Map requestComponentReports(List coordinates) throws Exception { + HashMap reports = new HashMap<>(); + ComponentReport report = new ComponentReport(); + PackageUrl packageUrl = coordinates.get(0); + report.setCoordinates(packageUrl); + report.setReference(new URI("https://ossindex.sonatype.org/component/pkg:maven/test/test@1.0?utm_source=dependency-check&utm_medium=integration&utm_content=12.1.4-SNAPSHOT")); + reports.put(packageUrl, report); + return reports; + } + + @Override + public ComponentReport requestComponentReport(PackageUrl coordinates) throws Exception { + return new ComponentReport(); + } + + @Override + public void close() { + + } + } + @Test void should_analyzeDependency_return_a_dedicated_error_message_when_403_response_from_sonatype() throws Exception { // Given OssIndexAnalyzer analyzer = new OssIndexAnalyzerThrowing403(); - analyzer.initialize(getSettings()); + Settings settings = getSettings(); + setCredentials(settings); + Engine engine = new Engine(settings); + + analyzer.initialize(settings); + analyzer.prepareAnalyzer(engine); Identifier identifier = new PurlIdentifier("maven", "test", "test", "1.0", Confidence.HIGHEST); Dependency dependency = new Dependency(); dependency.addSoftwareIdentifier(identifier); - Settings settings = getSettings(); - Engine engine = new Engine(settings); engine.setDependencies(Collections.singletonList(dependency)); // When @@ -126,17 +166,19 @@ void should_analyzeDependency_return_a_dedicated_error_message_when_403_response void should_analyzeDependency_only_warn_when_transport_error_from_sonatype() throws Exception { // Given OssIndexAnalyzer analyzer = new OssIndexAnalyzerThrowing502(); + Settings settings = getSettings(); + setCredentials(settings); + settings.setBoolean(Settings.KEYS.ANALYZER_OSSINDEX_WARN_ONLY_ON_REMOTE_ERRORS, true); + Engine engine = new Engine(settings); - getSettings().setBoolean(Settings.KEYS.ANALYZER_OSSINDEX_WARN_ONLY_ON_REMOTE_ERRORS, true); - analyzer.initialize(getSettings()); + analyzer.initialize(settings); + analyzer.prepareAnalyzer(engine); Identifier identifier = new PurlIdentifier("maven", "test", "test", "1.0", Confidence.HIGHEST); Dependency dependency = new Dependency(); dependency.addSoftwareIdentifier(identifier); - Settings settings = getSettings(); - Engine engine = new Engine(settings); // When try (engine) { @@ -148,22 +190,23 @@ void should_analyzeDependency_only_warn_when_transport_error_from_sonatype() thr } } - @Test void should_analyzeDependency_only_warn_when_socket_error_from_sonatype() throws Exception { // Given OssIndexAnalyzer analyzer = new OssIndexAnalyzerThrowingSocketTimeout(); + Settings settings = getSettings(); + setCredentials(settings); + settings.setBoolean(Settings.KEYS.ANALYZER_OSSINDEX_WARN_ONLY_ON_REMOTE_ERRORS, true); + analyzer.initialize(settings); - getSettings().setBoolean(Settings.KEYS.ANALYZER_OSSINDEX_WARN_ONLY_ON_REMOTE_ERRORS, true); - analyzer.initialize(getSettings()); + Engine engine = new Engine(settings); + analyzer.prepareAnalyzer(engine); Identifier identifier = new PurlIdentifier("maven", "test", "test", "1.0", Confidence.HIGHEST); Dependency dependency = new Dependency(); dependency.addSoftwareIdentifier(identifier); - Settings settings = getSettings(); - Engine engine = new Engine(settings); // When try (engine) { @@ -180,17 +223,19 @@ void should_analyzeDependency_only_warn_when_socket_error_from_sonatype() throws void should_analyzeDependency_fail_when_socket_error_from_sonatype() throws Exception { // Given OssIndexAnalyzer analyzer = new OssIndexAnalyzerThrowingSocketTimeout(); + Settings settings = getSettings(); + setCredentials(settings); + settings.setBoolean(Settings.KEYS.ANALYZER_OSSINDEX_WARN_ONLY_ON_REMOTE_ERRORS, false); + Engine engine = new Engine(settings); - getSettings().setBoolean(Settings.KEYS.ANALYZER_OSSINDEX_WARN_ONLY_ON_REMOTE_ERRORS, false); - analyzer.initialize(getSettings()); + analyzer.initialize(settings); + analyzer.prepareAnalyzer(engine); Identifier identifier = new PurlIdentifier("maven", "test", "test", "1.0", Confidence.HIGHEST); Dependency dependency = new Dependency(); dependency.addSoftwareIdentifier(identifier); - Settings settings = getSettings(); - Engine engine = new Engine(settings); engine.setDependencies(Collections.singletonList(dependency)); // When @@ -206,7 +251,25 @@ void should_analyzeDependency_fail_when_socket_error_from_sonatype() throws Exce analyzer.close(); } + @Test + void should_prepareAnalyzer_fail_when_credentials_not_set() throws Exception { + OssIndexAnalyzer analyzer = new OssIndexAnalyzer(); + Settings settings = getSettings(); + Engine engine = new Engine(settings); + analyzer.initialize(settings); + try { + analyzer.prepareAnalyzer(engine); + assertThrows(InitializationException.class, () -> analyzer.prepareAnalyzer(engine)); + } catch (InitializationException e) { + analyzer.close(); + engine.close(); + } + } + private static void setCredentials(final Settings settings) { + settings.setString(KEYS.ANALYZER_OSSINDEX_USER, "user"); + settings.setString(KEYS.ANALYZER_OSSINDEX_PASSWORD, "pass"); + } static final class OssIndexAnalyzerThrowing403 extends OssIndexAnalyzer { @Override diff --git a/src/site/markdown/analyzers/oss-index-analyzer.md b/src/site/markdown/analyzers/oss-index-analyzer.md index 4a85d4e7753..65636da4f67 100644 --- a/src/site/markdown/analyzers/oss-index-analyzer.md +++ b/src/site/markdown/analyzers/oss-index-analyzer.md @@ -8,3 +8,9 @@ identified vulnerabilities are included in the report. In addition, vulnerabilit found in both the NVD and OSS Index may have additional references added. This analyzer requires an Internet connection. + +Sonatype [announced](https://ossindex.sonatype.org/doc/auth-required) that OSS Index requires authentication. + +You can get an API Token following these steps: +1. [Sign In](https://ossindex.sonatype.org/user/signin) or [Sign Up](https://ossindex.sonatype.org/user/register) for free. +2. Get the API Token from user Settings. \ No newline at end of file