package com.synopsys.integration.alert.provider.blackduck.collector; import static com.synopsys.integration.alert.provider.blackduck.collector.VulnerabilityTestConstants.HIGH_VULNERABILITY; import static com.synopsys.integration.alert.provider.blackduck.collector.VulnerabilityTestConstants.LOW_VULNERABILITY; import static com.synopsys.integration.alert.provider.blackduck.collector.VulnerabilityTestConstants.MEDIUM_VULNERABILITY; import static com.synopsys.integration.alert.provider.blackduck.collector.VulnerabilityTestConstants.UNKNOWN_VULNERABILITY; import static com.synopsys.integration.alert.provider.blackduck.collector.VulnerabilityTestConstants.VULNERABILITY_URL_BDSA_4; import static com.synopsys.integration.alert.provider.blackduck.collector.VulnerabilityTestConstants.VULNERABILITY_URL_CVE_1; import static com.synopsys.integration.alert.provider.blackduck.collector.VulnerabilityTestConstants.VULNERABILITY_URL_CVE_10; import static com.synopsys.integration.alert.provider.blackduck.collector.VulnerabilityTestConstants.VULNERABILITY_URL_CVE_11; import static com.synopsys.integration.alert.provider.blackduck.collector.VulnerabilityTestConstants.VULNERABILITY_URL_CVE_12; import static com.synopsys.integration.alert.provider.blackduck.collector.VulnerabilityTestConstants.VULNERABILITY_URL_CVE_13; import static com.synopsys.integration.alert.provider.blackduck.collector.VulnerabilityTestConstants.VULNERABILITY_URL_CVE_2; import static com.synopsys.integration.alert.provider.blackduck.collector.VulnerabilityTestConstants.VULNERABILITY_URL_CVE_3; import static com.synopsys.integration.alert.provider.blackduck.collector.VulnerabilityTestConstants.VULNERABILITY_URL_CVE_4; import static com.synopsys.integration.alert.provider.blackduck.collector.VulnerabilityTestConstants.VULNERABILITY_URL_CVE_5; import static com.synopsys.integration.alert.provider.blackduck.collector.VulnerabilityTestConstants.VULNERABILITY_URL_CVE_6; import static com.synopsys.integration.alert.provider.blackduck.collector.VulnerabilityTestConstants.VULNERABILITY_URL_CVE_7; import static com.synopsys.integration.alert.provider.blackduck.collector.VulnerabilityTestConstants.VULNERABILITY_URL_CVE_8; import static com.synopsys.integration.alert.provider.blackduck.collector.VulnerabilityTestConstants.VULNERABILITY_URL_CVE_9; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.io.IOException; import java.nio.charset.Charset; import java.util.Collection; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.slf4j.Logger; import org.springframework.core.io.ClassPathResource; import com.google.common.io.Files; import com.google.gson.Gson; import com.synopsys.integration.alert.TestConstants; import com.synopsys.integration.alert.common.message.model.CommonMessageData; import com.synopsys.integration.alert.common.message.model.ComponentItem; import com.synopsys.integration.alert.common.message.model.LinkableItem; import com.synopsys.integration.alert.common.message.model.ProviderMessageContent; import com.synopsys.integration.alert.common.persistence.accessor.FieldAccessor; import com.synopsys.integration.alert.common.persistence.model.ConfigurationJobModel; import com.synopsys.integration.alert.common.util.DateUtils; import com.synopsys.integration.alert.provider.blackduck.BlackDuckProperties; import com.synopsys.integration.alert.provider.blackduck.BlackDuckProviderKey; import com.synopsys.integration.alert.provider.blackduck.collector.builder.BlackDuckIssueTrackerCallbackUtility; import com.synopsys.integration.alert.provider.blackduck.collector.builder.MessageBuilderConstants; import com.synopsys.integration.alert.provider.blackduck.collector.builder.VulnerabilityNotificationMessageBuilder; import com.synopsys.integration.blackduck.api.generated.enumeration.ProjectVersionVulnerableBomComponentsItemsVulnerabilityWithRemediationSeverityType; import com.synopsys.integration.blackduck.api.generated.view.ComponentVersionView; import com.synopsys.integration.blackduck.api.generated.view.VulnerabilityView; import com.synopsys.integration.blackduck.api.manual.view.VulnerabilityNotificationView; import com.synopsys.integration.blackduck.rest.BlackDuckHttpClient; import com.synopsys.integration.blackduck.service.BlackDuckService; import com.synopsys.integration.blackduck.service.BlackDuckServicesFactory; import com.synopsys.integration.blackduck.service.ComponentService; import com.synopsys.integration.blackduck.service.ProjectService; import com.synopsys.integration.blackduck.service.bucket.BlackDuckBucket; import com.synopsys.integration.blackduck.service.bucket.BlackDuckBucketService; import com.synopsys.integration.exception.IntegrationException; import com.synopsys.integration.log.Slf4jIntLogger; public class VulnerabilityMessageBuilderTest { private final Gson gson = new Gson(); private final BlackDuckIssueTrackerCallbackUtility blackDuckIssueTrackerCallbackUtility = new BlackDuckIssueTrackerCallbackUtility(new BlackDuckProviderKey()); @Test public void testCollectingVulnerability() throws Exception { BlackDuckBucket blackDuckBucket = new BlackDuckBucket(); BlackDuckHttpClient blackDuckHttpClient = BlackDuckMessageBuilderTestHelper.mockHttpClient(); BlackDuckServicesFactory blackDuckServicesFactory = BlackDuckMessageBuilderTestHelper.mockServicesFactory(); BlackDuckService blackDuckService = BlackDuckMessageBuilderTestHelper.mockBlackDuckService(); ProjectService projectService = BlackDuckMessageBuilderTestHelper.mockProjectService(); ComponentService componentService = Mockito.mock(ComponentService.class); Mockito.when(blackDuckServicesFactory.getBlackDuckHttpClient()).thenReturn(blackDuckHttpClient); Mockito.when(blackDuckService.getResponse(Mockito.contains("https://a-hub-server.blackduck.com/api/vulnerabilities/"), Mockito.any())).thenThrow(new IntegrationException("Test Integration Exception")); Mockito.when(blackDuckServicesFactory.createBlackDuckService()).thenReturn(blackDuckService); Mockito.when(blackDuckServicesFactory.createProjectService()).thenReturn(projectService); Mockito.when(componentService.getRemediationInformation(Mockito.any(ComponentVersionView.class))).thenReturn(Optional.empty()); Mockito.when(blackDuckServicesFactory.createComponentService()).thenReturn(componentService); VulnerabilityNotificationMessageBuilder vulnMessageBuilder = new VulnerabilityNotificationMessageBuilder(blackDuckIssueTrackerCallbackUtility); ConfigurationJobModel job = Mockito.mock(ConfigurationJobModel.class); Mockito.when(job.getFieldAccessor()).thenReturn(new FieldAccessor(Map.of())); VulnerabilityNotificationView notificationView = getNotificationView(TestConstants.VULNERABILITY_NOTIFICATION_JSON_PATH); CommonMessageData commonMessageData = new CommonMessageData(1L, 1L, "provider", "providerConfigName", "providerUrl", DateUtils.createCurrentDateTimestamp(), job); List<ProviderMessageContent> providerMessageContents = vulnMessageBuilder.buildMessageContents(commonMessageData, notificationView, blackDuckBucket, blackDuckServicesFactory); assertFalse(providerMessageContents.isEmpty()); List<ComponentItem> componentItems = getComponentItems(providerMessageContents); assertComponentItemContent(componentItems, "Custom Component", "1.0.0"); } @Test public void testCollectingVulnerabilityWithConnection() throws Exception { BlackDuckBucket blackDuckBucket = new BlackDuckBucket(); BlackDuckHttpClient blackDuckHttpClient = BlackDuckMessageBuilderTestHelper.mockHttpClient(); BlackDuckServicesFactory blackDuckServicesFactory = BlackDuckMessageBuilderTestHelper.mockServicesFactory(); BlackDuckService blackDuckService = BlackDuckMessageBuilderTestHelper.mockBlackDuckService(); ProjectService projectService = BlackDuckMessageBuilderTestHelper.mockProjectService(); BlackDuckBucketService bucketService = BlackDuckMessageBuilderTestHelper.mockBucketService(); ComponentService componentService = Mockito.mock(ComponentService.class); Mockito.when(blackDuckServicesFactory.getBlackDuckHttpClient()).thenReturn(blackDuckHttpClient); Mockito.when(blackDuckServicesFactory.createBlackDuckService()).thenReturn(blackDuckService); Mockito.when(blackDuckServicesFactory.createProjectService()).thenReturn(projectService); Mockito.when(blackDuckServicesFactory.createBlackDuckBucketService()).thenReturn(bucketService); Mockito.when(componentService.getRemediationInformation(Mockito.any(ComponentVersionView.class))).thenReturn(Optional.empty()); Mockito.when(blackDuckServicesFactory.createComponentService()).thenReturn(componentService); VulnerabilityNotificationMessageBuilder vulnMessageBuilder = new VulnerabilityNotificationMessageBuilder(blackDuckIssueTrackerCallbackUtility); ConfigurationJobModel job = Mockito.mock(ConfigurationJobModel.class); Mockito.when(job.getFieldAccessor()).thenReturn(new FieldAccessor(Map.of())); VulnerabilityNotificationView notificationView = getNotificationView(TestConstants.VULNERABILITY_NOTIFICATION_JSON_PATH); CommonMessageData commonMessageData = new CommonMessageData(1L, 1L, "provider", "providerConfigName", "providerUrl", DateUtils.createCurrentDateTimestamp(), job); List<ProviderMessageContent> providerMessageContents = vulnMessageBuilder.buildMessageContents(commonMessageData, notificationView, blackDuckBucket, blackDuckServicesFactory); List<ComponentItem> componentItems = getComponentItems(providerMessageContents); assertComponentItemContent(componentItems, "Custom Component", "1.0.0"); } @Test public void testCollectingVulnerabilityGetSeverityException() throws Exception { BlackDuckBucket blackDuckBucket = new BlackDuckBucket(); BlackDuckHttpClient blackDuckHttpClient = BlackDuckMessageBuilderTestHelper.mockHttpClient(); BlackDuckServicesFactory blackDuckServicesFactory = BlackDuckMessageBuilderTestHelper.mockServicesFactory(); BlackDuckService blackDuckService = BlackDuckMessageBuilderTestHelper.mockBlackDuckService(); ProjectService projectService = BlackDuckMessageBuilderTestHelper.mockProjectService(); BlackDuckProperties blackDuckProperties = BlackDuckMessageBuilderTestHelper.mockProperties(); ComponentService componentService = Mockito.mock(ComponentService.class); Mockito.when(blackDuckServicesFactory.getBlackDuckHttpClient()).thenReturn(blackDuckHttpClient); Mockito.when(blackDuckService.getResponse(Mockito.contains("https://a-hub-server.blackduck.com/api/vulnerabilities/"), Mockito.any())).thenThrow(new IntegrationException("Test Integration Exception")); Mockito.when(blackDuckServicesFactory.createBlackDuckService()).thenReturn(blackDuckService); Mockito.when(blackDuckServicesFactory.createProjectService()).thenReturn(projectService); Mockito.when(blackDuckProperties.createBlackDuckHttpClientAndLogErrors(Mockito.any(Logger.class))).thenReturn(Optional.of(blackDuckHttpClient)); Mockito.when(blackDuckProperties.createBlackDuckServicesFactory(Mockito.eq(blackDuckHttpClient), Mockito.any(Slf4jIntLogger.class))).thenReturn(blackDuckServicesFactory); Mockito.when(componentService.getRemediationInformation(Mockito.any(ComponentVersionView.class))).thenReturn(Optional.empty()); Mockito.when(blackDuckServicesFactory.createComponentService()).thenReturn(componentService); VulnerabilityNotificationMessageBuilder vulnMessageBuilder = new VulnerabilityNotificationMessageBuilder(blackDuckIssueTrackerCallbackUtility); ConfigurationJobModel job = Mockito.mock(ConfigurationJobModel.class); Mockito.when(job.getFieldAccessor()).thenReturn(new FieldAccessor(Map.of())); VulnerabilityNotificationView notificationView = getNotificationView(TestConstants.VULNERABILITY_NOTIFICATION_JSON_PATH); CommonMessageData commonMessageData = new CommonMessageData(1L, 1L, "provider", "providerConfigName", "providerUrl", DateUtils.createCurrentDateTimestamp(), job); List<ProviderMessageContent> messageContents = vulnMessageBuilder.buildMessageContents(commonMessageData, notificationView, blackDuckBucket, blackDuckServicesFactory); List<ComponentItem> componentItems = getComponentItems(messageContents); assertComponentItemContent(componentItems, "Custom Component", "1.0.0"); } @Test public void testCollectingVulnerabilityOrdered() throws Exception { BlackDuckBucket blackDuckBucket = new BlackDuckBucket(); BlackDuckHttpClient blackDuckHttpClient = BlackDuckMessageBuilderTestHelper.mockHttpClient(); BlackDuckServicesFactory blackDuckServicesFactory = BlackDuckMessageBuilderTestHelper.mockServicesFactory(); BlackDuckService blackDuckService = BlackDuckMessageBuilderTestHelper.mockBlackDuckService(); ProjectService projectService = BlackDuckMessageBuilderTestHelper.mockProjectService(); BlackDuckProperties blackDuckProperties = BlackDuckMessageBuilderTestHelper.mockProperties(); BlackDuckBucketService bucketService = BlackDuckMessageBuilderTestHelper.mockBucketService(); ComponentService componentService = Mockito.mock(ComponentService.class); Mockito.when(blackDuckServicesFactory.getBlackDuckHttpClient()).thenReturn(blackDuckHttpClient); VulnerabilityView highVulnerabilityView = new VulnerabilityView(); highVulnerabilityView.setName("vulnerability"); highVulnerabilityView.setSeverity(ProjectVersionVulnerableBomComponentsItemsVulnerabilityWithRemediationSeverityType.HIGH); VulnerabilityView mediumVulnerabilityView = new VulnerabilityView(); mediumVulnerabilityView.setName("vulnerability"); mediumVulnerabilityView.setSeverity(ProjectVersionVulnerableBomComponentsItemsVulnerabilityWithRemediationSeverityType.MEDIUM); VulnerabilityView lowVulnerabilityView = new VulnerabilityView(); lowVulnerabilityView.setName("vulnerability"); lowVulnerabilityView.setSeverity(ProjectVersionVulnerableBomComponentsItemsVulnerabilityWithRemediationSeverityType.LOW); Map<String, VulnerabilityView> urlToSeverityMap = new HashMap<>(); urlToSeverityMap.put(VULNERABILITY_URL_CVE_1, lowVulnerabilityView); urlToSeverityMap.put(VULNERABILITY_URL_CVE_2, highVulnerabilityView); urlToSeverityMap.put(VULNERABILITY_URL_CVE_3, mediumVulnerabilityView); Mockito.when(blackDuckServicesFactory.createBlackDuckService()).thenReturn(blackDuckService); Mockito.when(blackDuckServicesFactory.createProjectService()).thenReturn(projectService); Mockito.when(blackDuckServicesFactory.createBlackDuckBucketService()).thenReturn(bucketService); Mockito.when(blackDuckProperties.createBlackDuckHttpClientAndLogErrors(Mockito.any(Logger.class))).thenReturn(Optional.of(blackDuckHttpClient)); Mockito.when(blackDuckProperties.createBlackDuckServicesFactory(Mockito.eq(blackDuckHttpClient), Mockito.any(Slf4jIntLogger.class))).thenReturn(blackDuckServicesFactory); Mockito.when(componentService.getRemediationInformation(Mockito.any(ComponentVersionView.class))).thenReturn(Optional.empty()); Mockito.when(blackDuckServicesFactory.createComponentService()).thenReturn(componentService); VulnerabilityNotificationView notification1 = getNotificationView(TestConstants.VULNERABILITY_NOTIFICATION_SIMPLE_01_JSON_PATH); VulnerabilityNotificationView notification2 = getNotificationView(TestConstants.VULNERABILITY_NOTIFICATION_SIMPLE_02_JSON_PATH); List<VulnerabilityNotificationView> orderedNotifications = List.of(notification1, notification2); VulnerabilityNotificationMessageBuilder vulnMessageBuilder = new VulnerabilityNotificationMessageBuilder(blackDuckIssueTrackerCallbackUtility); Long notificationId = 1L; ConfigurationJobModel job = Mockito.mock(ConfigurationJobModel.class); Mockito.when(job.getFieldAccessor()).thenReturn(new FieldAccessor(Map.of())); LinkedList<ProviderMessageContent> providerMessageContents = new LinkedList<>(); for (VulnerabilityNotificationView notificationView : orderedNotifications) { CommonMessageData commonMessageData = new CommonMessageData(notificationId, 1L, "provider", "providerConfigName", "providerUrl", DateUtils.createCurrentDateTimestamp(), job); List<ProviderMessageContent> messageContents = vulnMessageBuilder.buildMessageContents(commonMessageData, notificationView, blackDuckBucket, blackDuckServicesFactory); providerMessageContents.addAll(messageContents); notificationId++; } for (ProviderMessageContent providerMessageContent : providerMessageContents) { System.out.println(providerMessageContent.getSubTopic()); for (ComponentItem componentItem : providerMessageContent.getComponentItems()) { LinkableItem vulnerabilityItem = componentItem.getCategoryItem(); String vulnerabilityUrl = vulnerabilityItem.getUrl().orElseThrow(() -> new IllegalStateException("Vulnerability URL missing")); Optional<LinkableItem> categoryGroupingAttribute = componentItem.getCategoryGroupingAttribute(); assertFalse(categoryGroupingAttribute.isEmpty(), "The component should have a grouping attribute."); LinkableItem groupingAttribute = categoryGroupingAttribute.get(); if (groupingAttribute.getName().equals(MessageBuilderConstants.LABEL_VULNERABILITY_SEVERITY)) { assertTrue(urlToSeverityMap.containsKey(vulnerabilityUrl)); assertEquals(urlToSeverityMap.get(vulnerabilityUrl).getSeverity().name(), groupingAttribute.getValue(), String.format("Failed on component: %s", componentItem.toString())); } } } } private void assertComponentItemContent(List<ComponentItem> componentItems, String componentName, String componentVersion) { Map<String, String> urlToSeverityMap = new HashMap<>(); urlToSeverityMap.put(VULNERABILITY_URL_CVE_1, HIGH_VULNERABILITY); urlToSeverityMap.put(VULNERABILITY_URL_CVE_2, HIGH_VULNERABILITY); urlToSeverityMap.put(VULNERABILITY_URL_CVE_3, HIGH_VULNERABILITY); urlToSeverityMap.put(VULNERABILITY_URL_CVE_4, HIGH_VULNERABILITY); urlToSeverityMap.put(VULNERABILITY_URL_CVE_5, HIGH_VULNERABILITY); urlToSeverityMap.put(VULNERABILITY_URL_CVE_6, HIGH_VULNERABILITY); urlToSeverityMap.put(VULNERABILITY_URL_CVE_7, HIGH_VULNERABILITY); urlToSeverityMap.put(VULNERABILITY_URL_CVE_8, HIGH_VULNERABILITY); urlToSeverityMap.put(VULNERABILITY_URL_CVE_9, HIGH_VULNERABILITY); urlToSeverityMap.put(VULNERABILITY_URL_CVE_10, UNKNOWN_VULNERABILITY); urlToSeverityMap.put(VULNERABILITY_URL_CVE_11, LOW_VULNERABILITY); urlToSeverityMap.put(VULNERABILITY_URL_CVE_12, MEDIUM_VULNERABILITY); urlToSeverityMap.put(VULNERABILITY_URL_CVE_13, UNKNOWN_VULNERABILITY); urlToSeverityMap.put(VULNERABILITY_URL_BDSA_4, LOW_VULNERABILITY); assertEquals(45, componentItems.size()); for (ComponentItem item : componentItems) { assertEquals(componentName, item.getComponent().getValue()); assertEquals(componentVersion, item.getSubComponent().map(LinkableItem::getValue).orElseThrow(() -> new IllegalStateException("Component Version missing"))); assertTrue(item.getCategoryItem().getUrl().isPresent()); String vulnerabilityUrl = item.getCategoryItem().getUrl().get(); String expectedSeverity = urlToSeverityMap.get(vulnerabilityUrl); Optional<LinkableItem> severityItem = item.getCategoryGroupingAttribute(); assertTrue(severityItem.isPresent()); String actualSeverity = severityItem.get().getValue(); assertEquals(expectedSeverity, actualSeverity); } } private VulnerabilityNotificationView getNotificationView(String path) throws IOException { ClassPathResource classPathResource = new ClassPathResource(path); File jsonFile = classPathResource.getFile(); String notificationString = Files.toString(jsonFile, Charset.defaultCharset()); return gson.fromJson(notificationString, VulnerabilityNotificationView.class); } private List<ComponentItem> getComponentItems(List<ProviderMessageContent> providerMessageContents) { return providerMessageContents .stream() .map(ProviderMessageContent::getComponentItems) .flatMap(Collection::stream) .collect(Collectors.toList()); } }