diff --git a/docs/_posts/2023-12-08-v4.10.0.md b/docs/_posts/2023-12-08-v4.10.0.md index 51ae352d68..6d90c8728d 100644 --- a/docs/_posts/2023-12-08-v4.10.0.md +++ b/docs/_posts/2023-12-08-v4.10.0.md @@ -19,7 +19,7 @@ REST API mirroring now, to ensure a smooth transition. Refer to the [NVD datasou * Add support for CycloneDX `metadata.supplier`, `metadata.manufacturer`, `metadata.authors`, and `component.supplier` - [apiserver/#3090], [apiserver/#3179] * Add support for authenticating with public / non-internal repositories - [apiserver/#2876] * Add support for fetching latest versions from GitHub - [apiserver/#3112] - * Applicable to components with `pkg:github//` package URLs + * Applicable to components with `pkg:github//@` package URLs * Improve efficiency of search index operations - [apiserver/#3116] * Add option to emit log for successfully published notifications, and improve logging around notifications in general - [apiserver/#3211] * Use Java 21 JRE in container images - [apiserver/#3089] @@ -67,24 +67,24 @@ Special thanks to everyone who contributed code to implement enhancements and fi ###### dependency-track-apiserver.jar -| Algorithm | Checksum | -|:----------|:---------| -| SHA-1 | | -| SHA-256 | | +| Algorithm | Checksum | +|:----------|:-----------------------------------------------------------------| +| SHA-1 | c308b1f6a2d73fc2bba9da2cc33bf7e3ec49e851 | +| SHA-256 | d06f4550e16451ccb7843c36534172744934a7dc69e1d48e970a6eec24e49dc3 | ###### dependency-track-bundled.jar -| Algorithm | Checksum | -|:----------|:---------| -| SHA-1 | | -| SHA-256 | | +| Algorithm | Checksum | +|:----------|:-----------------------------------------------------------------| +| SHA-1 | b94fb9cbaa91c4e332bcec266e10a0f325f12e22 | +| SHA-256 | cf27db44e637b4bc551c16e659e81890f4c5d4f3b4ea9893ebf1717bff98b999 | ###### frontend-dist.zip -| Algorithm | Checksum | -|:----------|:---------| -| SHA-1 | | -| SHA-256 | | +| Algorithm | Checksum | +|:----------|:-----------------------------------------------------------------| +| SHA-1 | 217bcaab3a7da2ae2fab3103055f9503aef5db07 | +| SHA-256 | 2f6f524c45afcc4a90128cab22a557bf41b88c716aaf0992eb6bb2239ce1469c | ###### Software Bill of Materials (SBOM) diff --git a/docs/_posts/2023-12-19-v4.10.1.md b/docs/_posts/2023-12-19-v4.10.1.md new file mode 100644 index 0000000000..de988b2a5e --- /dev/null +++ b/docs/_posts/2023-12-19-v4.10.1.md @@ -0,0 +1,60 @@ +--- +title: v4.10.1 +type: patch +--- + +This release fixes various defects in the API server. +There are no changes for the frontend, the latest version of it remains 4.10.0. + +**NVD Data Feed Retirement Update:** + +The NVD has [announced](https://groups.google.com/a/list.nist.gov/g/nvd-news/c/aofnAd3HP2g) that retirement of the +legacy data feeds has been delayed *until further notice*. Dependency-Track users who: + +* ran into issues with the new NVD REST API integration, or +* did not have the time yet to migrate + +can safely continue consuming the legacy feeds, or switch back to it. + +**Fixes:** + +* Fix alert rules not working for projects where the `ACTIVE` column is `NULL` - [apiserver/#3306] +* Fix NPE in version distance policy evaluation when project has no direct dependencies - [apiserver/#3308] +* Fix `ClassCastException` when updating an existing `ProjectMetadata#authors` field - [apiserver/#3312] +* Fix NPE in GitHub repository metadata analysis for components without version - [apiserver/#3315] +* Fix last modified timestamp for NVD mirroring via REST API not taking effect until restart - [apiserver/#3323] + +For a complete list of changes, refer to the respective GitHub milestones: + +* [API server milestone 4.10.1](https://github.com/DependencyTrack/dependency-track/milestone/35?closed=1) + +We thank all organizations and individuals who contributed to this release, from logging issues to taking part in discussions on GitHub & Slack to testing of fixes. + +Special thanks to everyone who contributed code to implement enhancements and fix defects: +[@jadyndev] + +###### dependency-track-apiserver.jar + +| Algorithm | Checksum | +|:----------|:-----------------------------------------------------------------| +| SHA-1 | 1d728ce1788e5db8b3a9308338a9e7e8ab5af12e | +| SHA-256 | e30731cd1915d3a1578cf5d8c8596d247fb11a82a3fe4c1ba2fb9fad01667aef | + +###### dependency-track-bundled.jar + +| Algorithm | Checksum | +|:----------|:-----------------------------------------------------------------| +| SHA-1 | be32e1bc64d0b9b8019e340717d4ae3c12442ecd | +| SHA-256 | ffa0ab6dc9be894d0887ca3e10c4ffe3a333305d98de940413fcdbb05e2bcebd | + +###### Software Bill of Materials (SBOM) + +* API Server: [bom.json](https://github.com/DependencyTrack/dependency-track/releases/download/4.10.1/bom.json) + +[apiserver/#3306]: https://github.com/DependencyTrack/dependency-track/pull/3306 +[apiserver/#3308]: https://github.com/DependencyTrack/dependency-track/pull/3308 +[apiserver/#3312]: https://github.com/DependencyTrack/dependency-track/pull/3312 +[apiserver/#3315]: https://github.com/DependencyTrack/dependency-track/pull/3315 +[apiserver/#3323]: https://github.com/DependencyTrack/dependency-track/pull/3323 + +[@jadyndev]: https://github.com/jadyndev diff --git a/pom.xml b/pom.xml index a629da2c1a..509a56ce26 100644 --- a/pom.xml +++ b/pom.xml @@ -31,7 +31,7 @@ org.dependencytrack dependency-track war - 4.10.0 + 4.10.2-SNAPSHOT Dependency-Track https://dependencytrack.org/ diff --git a/src/main/java/org/dependencytrack/notification/NotificationRouter.java b/src/main/java/org/dependencytrack/notification/NotificationRouter.java index 033c7fea51..010a91bf6a 100644 --- a/src/main/java/org/dependencytrack/notification/NotificationRouter.java +++ b/src/main/java/org/dependencytrack/notification/NotificationRouter.java @@ -257,7 +257,8 @@ private boolean checkIfChildrenAreAffected(Project parent, UUID uuid) { return false; } for (Project child : parent.getChildren()) { - if ((child.getUuid().equals(uuid) && Boolean.TRUE.equals(child.isActive())) || isChild) { + final boolean isChildActive = child.isActive() == null || child.isActive(); + if ((child.getUuid().equals(uuid) && isChildActive) || isChild) { return true; } isChild = checkIfChildrenAreAffected(child, uuid); diff --git a/src/main/java/org/dependencytrack/policy/VersionDistancePolicyEvaluator.java b/src/main/java/org/dependencytrack/policy/VersionDistancePolicyEvaluator.java index 9523a2ac43..a999d078b0 100644 --- a/src/main/java/org/dependencytrack/policy/VersionDistancePolicyEvaluator.java +++ b/src/main/java/org/dependencytrack/policy/VersionDistancePolicyEvaluator.java @@ -164,13 +164,17 @@ private boolean matches(final Operator operator, final VersionDistance policyDis } /** - * Test if the components project direct dependencies contain a givven component + * Test if the components project direct dependencies contain a given component * If so, the component is a direct dependency of the project * * @param component component to test * @return If the components project direct dependencies contain the component */ private boolean isDirectDependency(Component component) { + if (component.getProject().getDirectDependencies() == null) { + return false; + } + return component.getProject().getDirectDependencies().contains("\"uuid\":\"" + component.getUuid().toString() + "\""); } diff --git a/src/main/java/org/dependencytrack/tasks/BomUploadProcessingTask.java b/src/main/java/org/dependencytrack/tasks/BomUploadProcessingTask.java index 8fc656ef2c..1918c51a18 100644 --- a/src/main/java/org/dependencytrack/tasks/BomUploadProcessingTask.java +++ b/src/main/java/org/dependencytrack/tasks/BomUploadProcessingTask.java @@ -117,7 +117,9 @@ public void inform(final Event e) { final var projectMetadata = new ProjectMetadata(); projectMetadata.setSupplier(ModelConverter.convert(cycloneDxBom.getMetadata().getSupplier())); - projectMetadata.setAuthors(ModelConverter.convertCdxContacts(cycloneDxBom.getMetadata().getAuthors())); + projectMetadata.setAuthors(cycloneDxBom.getMetadata().getAuthors() != null + ? new ArrayList<>(ModelConverter.convertCdxContacts(cycloneDxBom.getMetadata().getAuthors())) + : null); if (project.getMetadata() != null) { qm.runInTransaction(() -> { project.getMetadata().setSupplier(projectMetadata.getSupplier()); diff --git a/src/main/java/org/dependencytrack/tasks/NistApiMirrorTask.java b/src/main/java/org/dependencytrack/tasks/NistApiMirrorTask.java index 38377bd922..2feddbd9f1 100644 --- a/src/main/java/org/dependencytrack/tasks/NistApiMirrorTask.java +++ b/src/main/java/org/dependencytrack/tasks/NistApiMirrorTask.java @@ -395,7 +395,7 @@ private static boolean updateLastModified(final ZonedDateTime lastModifiedDateTi } LOGGER.debug("Latest captured modification date: %s".formatted(lastModifiedDateTime)); - try (final var qm = new QueryManager().withL2CacheDisabled()) { + try (final var qm = new QueryManager()) { qm.runInTransaction(() -> { final ConfigProperty property = qm.getConfigProperty( VULNERABILITY_SOURCE_NVD_API_LAST_MODIFIED_EPOCH_SECONDS.getGroupName(), diff --git a/src/main/java/org/dependencytrack/tasks/repositories/GithubMetaAnalyzer.java b/src/main/java/org/dependencytrack/tasks/repositories/GithubMetaAnalyzer.java index 732c5b6fbd..4522df0fd3 100644 --- a/src/main/java/org/dependencytrack/tasks/repositories/GithubMetaAnalyzer.java +++ b/src/main/java/org/dependencytrack/tasks/repositories/GithubMetaAnalyzer.java @@ -30,7 +30,6 @@ import org.kohsuke.github.GitHub; import java.io.IOException; -import java.util.regex.Pattern; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; @@ -45,9 +44,11 @@ public class GithubMetaAnalyzer extends AbstractMetaAnalyzer { private static final Logger LOGGER = Logger.getLogger(GithubMetaAnalyzer.class); - private static final int VERSION_TYPE_RELEASE = 1; - private static final int VERSION_TYPE_COMMIT = 2; - private static final String VERSION_TYPE_PATTERN = "[a-f0-9]{6,40}"; + private enum VersionType { + RELEASE, + COMMIT; + } + private static final VersionType DEFAULT_VERSION_TYPE = VersionType.RELEASE; private static final String REPOSITORY_DEFAULT_URL = "https://github.com"; private String repositoryUrl; private String repositoryUser; @@ -88,6 +89,27 @@ public RepositoryType supportedRepositoryType() { return RepositoryType.GITHUB; } + /** + * Checks whether the pURL version is a release or commit + * @param component Component Object + * @param repository The GH Repository Object defined by the pURL + * @return VersionType + * @throws IOException when GitHub API Calls fail + */ + private VersionType get_version_type(final Component component, GHRepository repository) throws IOException { + if (component.getPurl().getVersion() == null){ + LOGGER.debug(String.format("Version is not set, assuming %s", DEFAULT_VERSION_TYPE.name())); + return DEFAULT_VERSION_TYPE; + } + if (repository.getReleaseByTagName(component.getPurl().getVersion()) != null){ + LOGGER.debug("Version is release"); + return VersionType.RELEASE; + } else { + LOGGER.debug("Version is commit"); + return VersionType.COMMIT; + } + } + /** * {@inheritDoc} */ @@ -104,19 +126,12 @@ public MetaModel analyze(final Component component) { github = GitHub.connectAnonymously(); } - Pattern version_pattern = Pattern.compile(VERSION_TYPE_PATTERN); - final int version_type; - if (version_pattern.matcher(component.getPurl().getVersion()).find()) { - LOGGER.debug("Version is commit"); - version_type = VERSION_TYPE_COMMIT; - } else { - LOGGER.debug("Version is release"); - version_type = VERSION_TYPE_RELEASE; - } - GHRepository repository = github.getRepository(String.format("%s/%s", component.getPurl().getNamespace(), component.getPurl().getName())); LOGGER.debug(String.format("Repos is at %s", repository.getUrl())); - if (version_type == VERSION_TYPE_RELEASE) { + + final VersionType version_type = get_version_type(component, repository); + + if (version_type == VersionType.RELEASE) { GHRelease latest_release = repository.getLatestRelease(); if (latest_release != null) { meta.setLatestVersion(latest_release.getTagName()); @@ -127,12 +142,14 @@ public MetaModel analyze(final Component component) { meta.setPublishedTimestamp(current_release.getPublished_at()); LOGGER.debug(String.format("Current version published at: %s", meta.getPublishedTimestamp())); } - } else { + } + + if (version_type == VersionType.COMMIT) { GHBranch default_branch = repository.getBranch(repository.getDefaultBranch()); GHCommit latest_release = repository.getCommit(default_branch.getSHA1()); - GHCommit current_release = repository.getCommit(component.getPurl().getVersion()); meta.setLatestVersion(latest_release.getSHA1()); LOGGER.debug(String.format("Latest version: %s", meta.getLatestVersion())); + GHCommit current_release = repository.getCommit(component.getPurl().getVersion()); meta.setPublishedTimestamp(current_release.getCommitDate()); LOGGER.debug(String.format("Current version published at: %s", meta.getPublishedTimestamp())); } diff --git a/src/test/java/org/dependencytrack/notification/NotificationRouterTest.java b/src/test/java/org/dependencytrack/notification/NotificationRouterTest.java index 1e1acc4b06..f51919a14d 100644 --- a/src/test/java/org/dependencytrack/notification/NotificationRouterTest.java +++ b/src/test/java/org/dependencytrack/notification/NotificationRouterTest.java @@ -657,7 +657,42 @@ public void testAffectedInactiveChild() { Assert.assertEquals(0, rules.size()); } - + @Test + public void testAffectedActiveNullChild() { + NotificationPublisher publisher = createSlackPublisher(); + // Creates a new rule and defines when the rule should be triggered (notifyOn) + NotificationRule rule = qm.createNotificationRule("Matching Test Rule", NotificationScope.PORTFOLIO, NotificationLevel.INFORMATIONAL, publisher); + Set notifyOn = new HashSet<>(); + notifyOn.add(NotificationGroup.NEW_VULNERABILITY); + rule.setNotifyOn(notifyOn); + // Creates a project which will later be matched on + List projects = new ArrayList<>(); + Project grandParent = qm.createProject("Test Project Grandparent", null, "1.0", null, null, null, true, false); + Project parent = qm.createProject("Test Project Parent", null, "1.0", null, grandParent, null, true, false); + Project child = qm.createProject("Test Project Child", null, "1.0", null, parent, null, true, false); + Project grandChild = qm.createProject("Test Project Grandchild", null, "1.0", null, child, null, true, false); + grandChild.setActive(null); // https://github.com/DependencyTrack/dependency-track/issues/3296 + projects.add(grandParent); + rule.setProjects(projects); + // Creates a new component + Component component = new Component(); + component.setProject(grandChild); + // Creates a new notification + Notification notification = new Notification(); + notification.setScope(NotificationScope.PORTFOLIO.name()); + notification.setGroup(NotificationGroup.NEW_VULNERABILITY.name()); + notification.setLevel(NotificationLevel.INFORMATIONAL); + // Notification should be limited to only specific projects - Set the projects which are affected by the notification event + Set affectedProjects = new HashSet<>(); + affectedProjects.add(grandChild); + NewVulnerabilityIdentified subject = new NewVulnerabilityIdentified(new Vulnerability(), component, affectedProjects, null); + notification.setSubject(subject); + // Ok, let's test this + NotificationRouter router = new NotificationRouter(); + List rules = router.resolveRules(PublishContext.from(notification), notification); + Assert.assertTrue(rule.isNotifyChildren()); + Assert.assertEquals(1, rules.size()); + } private NotificationPublisher createSlackPublisher() { return qm.createNotificationPublisher( diff --git a/src/test/java/org/dependencytrack/policy/VersionDistancePolicyEvaluatorTest.java b/src/test/java/org/dependencytrack/policy/VersionDistancePolicyEvaluatorTest.java index f14b6bfe7b..967165987f 100644 --- a/src/test/java/org/dependencytrack/policy/VersionDistancePolicyEvaluatorTest.java +++ b/src/test/java/org/dependencytrack/policy/VersionDistancePolicyEvaluatorTest.java @@ -157,6 +157,11 @@ public void evaluateTest() { final PolicyConditionViolation violation = violations.get(0); assertThat(violation.getComponent()).isEqualTo(component); assertThat(violation.getPolicyCondition()).isEqualTo(condition); + + // https://github.com/DependencyTrack/dependency-track/issues/3295 + project.setDirectDependencies(null); + qm.persist(project); + assertThat(evaluator.evaluate(policy, component)).isEmpty(); } else { assertThat(violations).isEmpty(); } diff --git a/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java b/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java index 2bf72da74e..d200623b13 100644 --- a/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java +++ b/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java @@ -26,6 +26,7 @@ import alpine.notification.Subscription; import net.jcip.annotations.NotThreadSafe; import org.apache.commons.io.IOUtils; +import org.awaitility.core.ConditionTimeoutException; import org.dependencytrack.PersistenceCapableTest; import org.dependencytrack.event.BomUploadEvent; import org.dependencytrack.event.NewVulnerableDependencyAnalysisEvent; @@ -53,10 +54,13 @@ import java.nio.file.Paths; import java.time.Duration; import java.util.List; +import java.util.Optional; import java.util.concurrent.ConcurrentLinkedQueue; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatNoException; +import static org.assertj.core.api.Assertions.fail; +import static org.awaitility.Awaitility.await; import static org.dependencytrack.assertion.Assertions.assertConditionWithTimeout; @NotThreadSafe @@ -285,7 +289,7 @@ public void informWithBomContainingLicenseExpressionTest() throws Exception { """.getBytes(StandardCharsets.UTF_8); new BomUploadProcessingTask().inform(new BomUploadEvent(project.getUuid(), bomBytes)); - assertConditionWithTimeout(() -> NOTIFICATIONS.size() >= 2, Duration.ofSeconds(5)); + awaitBomProcessedNotification(); assertThat(qm.getAllComponents(project)).satisfiesExactly(component -> { assertThat(component.getLicense()).isNull(); @@ -329,7 +333,7 @@ public void informWithBomContainingLicenseExpressionWithSingleIdTest() throws Ex """.getBytes(StandardCharsets.UTF_8); new BomUploadProcessingTask().inform(new BomUploadEvent(project.getUuid(), bomBytes)); - assertConditionWithTimeout(() -> NOTIFICATIONS.size() >= 2, Duration.ofSeconds(5)); + awaitBomProcessedNotification(); assertThat(qm.getAllComponents(project)).satisfiesExactly(component -> { assertThat(component.getResolvedLicense()).isNotNull(); @@ -369,7 +373,7 @@ public void informWithBomContainingInvalidLicenseExpressionTest() throws Excepti """.getBytes(StandardCharsets.UTF_8); new BomUploadProcessingTask().inform(new BomUploadEvent(project.getUuid(), bomBytes)); - assertConditionWithTimeout(() -> NOTIFICATIONS.size() >= 2, Duration.ofSeconds(5)); + awaitBomProcessedNotification(); assertThat(qm.getAllComponents(project)).satisfiesExactly(component -> { assertThat(component.getLicense()).isNull(); @@ -378,4 +382,64 @@ public void informWithBomContainingInvalidLicenseExpressionTest() throws Excepti }); } + @Test // https://github.com/DependencyTrack/dependency-track/issues/3309 + public void informIssue3309Test() { + final var project = new Project(); + project.setName("acme-app"); + qm.persist(project); + + final Runnable assertProjectAuthors = () -> { + qm.getPersistenceManager().evictAll(); + assertThat(project.getMetadata()).isNotNull(); + assertThat(project.getMetadata().getAuthors()).satisfiesExactly(author -> { + assertThat(author.getName()).isEqualTo("Author Name"); + assertThat(author.getEmail()).isEqualTo("author@example.com"); + }); + }; + + final byte[] bomBytes = """ + { + "bomFormat": "CycloneDX", + "specVersion": "1.4", + "metadata": { + "authors": [ + { + "name": "Author Name", + "email": "author@example.com" + } + ] + } + } + """.getBytes(); + + new BomUploadProcessingTask().inform(new BomUploadEvent(project.getUuid(), bomBytes)); + awaitBomProcessedNotification(); + assertProjectAuthors.run(); + + NOTIFICATIONS.clear(); + + new BomUploadProcessingTask().inform(new BomUploadEvent(project.getUuid(), bomBytes)); + awaitBomProcessedNotification(); + assertProjectAuthors.run(); + } + + private void awaitBomProcessedNotification() { + try { + await("BOM Processed Notification") + .atMost(Duration.ofSeconds(3)) + .untilAsserted(() -> assertThat(NOTIFICATIONS) + .anyMatch(n -> NotificationGroup.BOM_PROCESSED.name().equals(n.getGroup()))); + } catch (ConditionTimeoutException e) { + final Optional optionalNotification = NOTIFICATIONS.stream() + .filter(n -> NotificationGroup.BOM_PROCESSING_FAILED.name().equals(n.getGroup())) + .findAny(); + if (optionalNotification.isEmpty()) { + throw e; + } + + final var subject = (BomProcessingFailed) optionalNotification.get().getSubject(); + fail("Expected BOM processing to succeed, but it failed due to: %s", subject.getCause()); + } + } + } diff --git a/src/test/java/org/dependencytrack/tasks/NistApiMirrorTaskTest.java b/src/test/java/org/dependencytrack/tasks/NistApiMirrorTaskTest.java index b7e4cb13ea..a10169f6d0 100644 --- a/src/test/java/org/dependencytrack/tasks/NistApiMirrorTaskTest.java +++ b/src/test/java/org/dependencytrack/tasks/NistApiMirrorTaskTest.java @@ -18,6 +18,7 @@ */ package org.dependencytrack.tasks; +import alpine.model.ConfigProperty; import com.github.tomakehurst.wiremock.junit.WireMockRule; import org.dependencytrack.PersistenceCapableTest; import org.dependencytrack.event.NistApiMirrorEvent; @@ -233,6 +234,16 @@ public void testInformWithNewVulnerability() throws Exception { assertThat(qm.hasAffectedVersionAttribution(vuln, vs, Source.NVD)).isTrue(); } ); + + // Property is in L1 cache because it was created in the test's setUp method. + // Evict L1 cache to reach L2 cache / datastore instead. + qm.getPersistenceManager().evictAll(); + final ConfigProperty lastModifiedProperty = qm.getConfigProperty( + VULNERABILITY_SOURCE_NVD_API_LAST_MODIFIED_EPOCH_SECONDS.getGroupName(), + VULNERABILITY_SOURCE_NVD_API_LAST_MODIFIED_EPOCH_SECONDS.getPropertyName() + ); + assertThat(lastModifiedProperty).isNotNull(); + assertThat(lastModifiedProperty.getPropertyValue()).isEqualTo("1691504544"); } @Test diff --git a/src/test/java/org/dependencytrack/tasks/repositories/GitHubMetaAnalyzerTest.java b/src/test/java/org/dependencytrack/tasks/repositories/GitHubMetaAnalyzerTest.java index 0f640b429b..51e47ab05f 100644 --- a/src/test/java/org/dependencytrack/tasks/repositories/GitHubMetaAnalyzerTest.java +++ b/src/test/java/org/dependencytrack/tasks/repositories/GitHubMetaAnalyzerTest.java @@ -74,4 +74,46 @@ public void testAnalyzerShortCommit() throws Exception{ Assert.assertTrue(version_pattern.matcher(metaModel.getLatestVersion()).find()); Assert.assertNotNull(metaModel.getPublishedTimestamp()); } + + @Test + public void testAnalyzerNoVersion() throws Exception{ + final var component = new Component(); + component.setPurl(new PackageURL("pkg:github/CycloneDX/cdxgen")); + + final var analyzer = new GithubMetaAnalyzer(); + Assert.assertTrue(analyzer.isApplicable(component)); + Assert.assertEquals(RepositoryType.GITHUB, analyzer.supportedRepositoryType()); + + MetaModel metaModel = analyzer.analyze(component); + Assert.assertNotNull(metaModel.getLatestVersion()); + Assert.assertTrue(metaModel.getLatestVersion().startsWith("v")); + Assert.assertNull(metaModel.getPublishedTimestamp()); + } + @Test + public void testAnalyzerInvalidCommit() throws Exception{ + final var component = new Component(); + component.setPurl(new PackageURL("pkg:github/CycloneDX/cdxgen@0000000")); + + final var analyzer = new GithubMetaAnalyzer(); + Assert.assertTrue(analyzer.isApplicable(component)); + Assert.assertEquals(RepositoryType.GITHUB, analyzer.supportedRepositoryType()); + + MetaModel metaModel = analyzer.analyze(component); + Assert.assertNotNull(metaModel.getLatestVersion()); + Assert.assertNull(metaModel.getPublishedTimestamp()); + } + + @Test + public void testAnalyzerInvalidRelease() throws Exception{ + final var component = new Component(); + component.setPurl(new PackageURL("pkg:github/CycloneDX/cdxgen@invalid-release")); + + final var analyzer = new GithubMetaAnalyzer(); + Assert.assertTrue(analyzer.isApplicable(component)); + Assert.assertEquals(RepositoryType.GITHUB, analyzer.supportedRepositoryType()); + + MetaModel metaModel = analyzer.analyze(component); + Assert.assertNotNull(metaModel.getLatestVersion()); + Assert.assertNull(metaModel.getPublishedTimestamp()); + } } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy