package biz.karms.sinkit.ejb.virustotal; import biz.karms.sinkit.ejb.ArchiveService; import biz.karms.sinkit.ejb.virustotal.impl.VirusTotalEnricher; import biz.karms.sinkit.eventlog.EventLogRecord; import biz.karms.sinkit.eventlog.EventReason; import biz.karms.sinkit.eventlog.VirusTotalRequest; import biz.karms.sinkit.eventlog.VirusTotalRequestStatus; import biz.karms.sinkit.ioc.IoCRecord; import biz.karms.sinkit.ioc.IoCVirusTotalReport; import com.kanishka.virustotal.dto.FileScanReport; import com.kanishka.virustotal.dto.VirusScanInfo; import com.kanishka.virustotal.exception.InvalidArguentsException; import com.kanishka.virustotal.exception.UnauthorizedAccessException; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import java.io.IOException; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.logging.Logger; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import static org.mockito.internal.verification.VerificationModeFactory.times; /** * @author Tomas Kozel */ @RunWith(MockitoJUnitRunner.class) public class VirusTotalEnricherTest { @Mock private ArchiveService archiveService; @Mock private VirusTotalService virusTotalService; @Mock private Logger log; @Captor ArgumentCaptor<EventLogRecord> logRecordCaptor; @Captor ArgumentCaptor<IoCVirusTotalReport[]> reportCaptor; @InjectMocks private VirusTotalEnricher enricher; @Test public void testSuccessfulEnrichment() throws Exception { IoCRecord ioc = createIoCRecord("unique", null); EventLogRecord logRecord = createLogRecord("6.6.6.6", null, VirusTotalRequestStatus.WAITING, new String[]{"unique"}); when(archiveService.getLogRecordWaitingForVTReport(2)) .thenReturn(null) .thenReturn(logRecord) .thenReturn(null); when(archiveService.getLogRecordWaitingForVTScan(2)) .thenReturn(logRecord) .thenReturn(null); when(archiveService.getIoCRecordByUniqueRef("unique")).thenReturn(ioc); enricher.doEnrichment(); verify(archiveService, times(1)).getLogRecordWaitingForVTReport(2); verify(archiveService, times(2)).getLogRecordWaitingForVTScan(2); verify(archiveService, times(1)).getIoCRecordByUniqueRef("unique"); verify(virusTotalService, times(1)).scanUrl("http://6.6.6.6/"); verify(archiveService).archiveEventLogRecord(logRecordCaptor.capture()); assertNotNull(logRecordCaptor.getValue()); assertNotNull(logRecordCaptor.getValue().getVirusTotalRequest()); assertEquals(VirusTotalRequestStatus.WAITING_FOR_REPORT, logRecordCaptor.getValue().getVirusTotalRequest().getStatus()); when(virusTotalService.getUrlScanReport("http://6.6.6.6/")).thenReturn(createScanReport("now", "whalebone", "malware")); when(virusTotalService.parseDate("now")).thenReturn(new Date()); enricher.doEnrichment(); verify(archiveService, times(3)).getLogRecordWaitingForVTReport(2); verify(archiveService, times(3)).getLogRecordWaitingForVTScan(2); verify(virusTotalService).getUrlScanReport("http://6.6.6.6/"); verify(virusTotalService).parseDate("now"); verify(archiveService, times(2)).getIoCRecordByUniqueRef("unique"); verify(archiveService, times(1)).setVirusTotalReportToIoCRecord(eq(ioc), reportCaptor.capture()); assertNotNull(reportCaptor.getValue()); assertEquals(1, reportCaptor.getValue().length); assertEquals("6.6.6.6", reportCaptor.getValue()[0].getIp()); assertNotNull(reportCaptor.getValue()[0].getScanDate()); assertNotNull(reportCaptor.getValue()[0].getUrlReport()); assertNotNull(reportCaptor.getValue()[0].getUrlReport().getScans()); assertNotNull(reportCaptor.getValue()[0].getUrlReport().getScans().get("whalebone")); assertEquals("malware", reportCaptor.getValue()[0].getUrlReport().getScans().get("whalebone").getResult()); assertTrue(reportCaptor.getValue()[0].getUrlReport().getScans().get("whalebone").isDetected()); verify(archiveService, times(2)).archiveEventLogRecord(logRecordCaptor.capture()); assertNotNull(logRecordCaptor.getValue()); assertNotNull(logRecordCaptor.getValue().getVirusTotalRequest()); assertEquals(VirusTotalRequestStatus.FINISHED, logRecordCaptor.getValue().getVirusTotalRequest().getStatus()); verifyNoMoreInteractions(archiveService, virusTotalService); } @Test public void testFailedScan() throws Exception { IoCRecord ioc = createIoCRecord("unique", null); EventLogRecord logRecord = createLogRecord(null, "whalebone.io", VirusTotalRequestStatus.WAITING, new String[]{"unique"}); when(archiveService.getLogRecordWaitingForVTScan(2)) .thenReturn(logRecord) // first try .thenReturn(null) .thenReturn(logRecord) // second try .thenReturn(null) .thenReturn(logRecord) // third try .thenReturn(null); when(virusTotalService.scanUrl("http://whalebone.io/")) .thenThrow(new IOException("Connection failed")) // first fail .thenThrow(new UnauthorizedAccessException("Wrong API key")) // second fail .thenThrow(new InvalidArguentsException("Muhaha")); // third fail when(archiveService.getIoCRecordByUniqueRef("unique")).thenReturn(ioc); // first try Date before = Calendar.getInstance().getTime(); Thread.sleep(1); enricher.doEnrichment(); verify(archiveService).archiveEventLogRecord(logRecordCaptor.capture()); assertNotNull(logRecordCaptor.getValue()); assertNotNull(logRecordCaptor.getValue().getVirusTotalRequest()); assertEquals(VirusTotalRequestStatus.WAITING, logRecordCaptor.getValue().getVirusTotalRequest().getStatus()); assertEquals("java.io.IOException: Connection failed", logRecordCaptor.getValue().getVirusTotalRequest().getCauseOfFailure()); assertEquals(new Integer(1), logRecordCaptor.getValue().getVirusTotalRequest().getFailedAttempts()); assertNotNull(logRecordCaptor.getValue().getVirusTotalRequest().getFailed()); assertTrue(before.before(logRecordCaptor.getValue().getVirusTotalRequest().getFailed())); // second try before = Calendar.getInstance().getTime(); Thread.sleep(1); enricher.doEnrichment(); verify(archiveService, times(2)).archiveEventLogRecord(logRecordCaptor.capture()); assertNotNull(logRecordCaptor.getValue()); assertNotNull(logRecordCaptor.getValue().getVirusTotalRequest()); assertEquals(VirusTotalRequestStatus.WAITING, logRecordCaptor.getValue().getVirusTotalRequest().getStatus()); assertEquals("com.kanishka.virustotal.exception.UnauthorizedAccessException: Wrong API key", logRecordCaptor.getValue().getVirusTotalRequest().getCauseOfFailure()); assertEquals(new Integer(2), logRecordCaptor.getValue().getVirusTotalRequest().getFailedAttempts()); assertNotNull(logRecordCaptor.getValue().getVirusTotalRequest().getFailed()); assertTrue(before.before(logRecordCaptor.getValue().getVirusTotalRequest().getFailed())); // third try before = Calendar.getInstance().getTime(); Thread.sleep(1); enricher.doEnrichment(); verify(archiveService, times(3)).archiveEventLogRecord(logRecordCaptor.capture()); assertNotNull(logRecordCaptor.getValue()); assertNotNull(logRecordCaptor.getValue().getVirusTotalRequest()); assertEquals(VirusTotalRequestStatus.FAILED, logRecordCaptor.getValue().getVirusTotalRequest().getStatus()); assertEquals("com.kanishka.virustotal.exception.InvalidArguentsException: Muhaha", logRecordCaptor.getValue().getVirusTotalRequest().getCauseOfFailure()); assertEquals(new Integer(3), logRecordCaptor.getValue().getVirusTotalRequest().getFailedAttempts()); assertNotNull(logRecordCaptor.getValue().getVirusTotalRequest().getFailed()); assertTrue(before.before(logRecordCaptor.getValue().getVirusTotalRequest().getFailed())); } @Test public void testFailedReport() throws Exception { IoCRecord ioc = createIoCRecord("unique", null); EventLogRecord logRecord = createLogRecord(null, "whalebone.io", VirusTotalRequestStatus.WAITING_FOR_REPORT, new String[]{"unique"}); when(archiveService.getLogRecordWaitingForVTReport(2)) .thenReturn(logRecord) // first try .thenReturn(null) .thenReturn(logRecord) // second try .thenReturn(null) .thenReturn(logRecord) // third try .thenReturn(null); when(virusTotalService.getUrlScanReport("http://whalebone.io/")) .thenThrow(new IOException("Connection failed")) // first fail .thenReturn(createScanReport(null, "whalebone", "virus")) // second fail (null scan date) .thenThrow(new InvalidArguentsException("Muhaha")); // third fail when(archiveService.getIoCRecordByUniqueRef("unique")).thenReturn(ioc); // first try Date before = Calendar.getInstance().getTime(); Thread.sleep(1); enricher.doEnrichment(); verify(archiveService).archiveEventLogRecord(logRecordCaptor.capture()); assertNotNull(logRecordCaptor.getValue()); assertNotNull(logRecordCaptor.getValue().getVirusTotalRequest()); assertEquals(VirusTotalRequestStatus.WAITING_FOR_REPORT, logRecordCaptor.getValue().getVirusTotalRequest().getStatus()); assertEquals("java.io.IOException: Connection failed", logRecordCaptor.getValue().getVirusTotalRequest().getCauseOfFailure()); assertEquals(new Integer(1), logRecordCaptor.getValue().getVirusTotalRequest().getFailedAttempts()); assertNotNull(logRecordCaptor.getValue().getVirusTotalRequest().getFailed()); assertTrue(before.before(logRecordCaptor.getValue().getVirusTotalRequest().getFailed())); // second try before = Calendar.getInstance().getTime(); Thread.sleep(1); enricher.doEnrichment(); verify(archiveService, times(2)).archiveEventLogRecord(logRecordCaptor.capture()); assertNotNull(logRecordCaptor.getValue()); assertNotNull(logRecordCaptor.getValue().getVirusTotalRequest()); assertEquals(VirusTotalRequestStatus.WAITING_FOR_REPORT, logRecordCaptor.getValue().getVirusTotalRequest().getStatus()); assertEquals("Receiving scan report failed: received scan date is null, something is really wrong", logRecordCaptor.getValue().getVirusTotalRequest().getCauseOfFailure()); assertEquals(new Integer(2), logRecordCaptor.getValue().getVirusTotalRequest().getFailedAttempts()); assertNotNull(logRecordCaptor.getValue().getVirusTotalRequest().getFailed()); assertTrue(before.before(logRecordCaptor.getValue().getVirusTotalRequest().getFailed())); // third try before = Calendar.getInstance().getTime(); Thread.sleep(1); enricher.doEnrichment(); verify(archiveService, times(3)).archiveEventLogRecord(logRecordCaptor.capture()); assertNotNull(logRecordCaptor.getValue()); assertNotNull(logRecordCaptor.getValue().getVirusTotalRequest()); assertEquals(VirusTotalRequestStatus.FAILED, logRecordCaptor.getValue().getVirusTotalRequest().getStatus()); assertEquals("com.kanishka.virustotal.exception.InvalidArguentsException: Muhaha", logRecordCaptor.getValue().getVirusTotalRequest().getCauseOfFailure()); assertEquals(new Integer(3), logRecordCaptor.getValue().getVirusTotalRequest().getFailedAttempts()); assertNotNull(logRecordCaptor.getValue().getVirusTotalRequest().getFailed()); assertTrue(before.before(logRecordCaptor.getValue().getVirusTotalRequest().getFailed())); } private EventLogRecord createLogRecord(String ip, String fqdn, VirusTotalRequestStatus status, String[] matchedIoCUniqueRefs) { EventLogRecord request = new EventLogRecord(); request.setReason(new EventReason()); request.getReason().setIp(ip); request.getReason().setFqdn(fqdn); request.setVirusTotalRequest(new VirusTotalRequest()); request.getVirusTotalRequest().setStatus(status); IoCRecord[] matchedIoCs = new IoCRecord[matchedIoCUniqueRefs.length]; for (int i = 0; i < matchedIoCs.length; i++) { matchedIoCs[i] = new IoCRecord(); matchedIoCs[i].setUniqueRef(matchedIoCUniqueRefs[i]); } request.setMatchedIocs(matchedIoCs); return request; } private IoCRecord createIoCRecord(String uniqueRef, IoCVirusTotalReport[] reports) { IoCRecord ioc = new IoCRecord(); ioc.setUniqueRef(uniqueRef); ioc.setVirusTotalReports(reports); return ioc; } private FileScanReport createScanReport(String scanDate, String scanner, String result) { FileScanReport report = new FileScanReport(); report.setScanDate(scanDate); VirusScanInfo scanInfo = new VirusScanInfo(); scanInfo.setDetected(true); scanInfo.setResult(result); report.setScans(new HashMap<>()); report.getScans().put(scanner, scanInfo); return report; } }