diff --git a/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java b/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java index 8f2a26fdc8..4270554599 100644 --- a/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/testing/StartTestAction.java @@ -510,26 +510,8 @@ private List prepareTestRunResultsFilters(ObjectId testingRunResultSummary List filterList = new ArrayList<>(); filterList.add(Filters.eq(TestingRunResult.TEST_RUN_RESULT_SUMMARY_ID, testingRunResultSummaryId)); - if(filters != null && !filters.isEmpty()) { - for(Map.Entry filterEntry : filters.entrySet()) { - String key = filterEntry.getKey(); - switch (key) { - case "severityStatus": - filterList.add(Filters.in(TestingRunResult.TEST_RESULTS+"."+GenericTestResult._CONFIDENCE, filterEntry.getValue())); - break; - case "apiCollectionId": - case "collectionIds": - filterList.add(Filters.in(TestingRunResult.API_INFO_KEY+"."+ApiInfo.ApiInfoKey.API_COLLECTION_ID, filterEntry.getValue())); - break; - case "method": - filterList.add(Filters.in(TestingRunResult.API_INFO_KEY+"."+ApiInfo.ApiInfoKey.METHOD, filterEntry.getValue())); - break; - case "categoryFilter": - filterList.add(Filters.in(TestingRunResult.TEST_SUPER_TYPE, filterEntry.getValue())); - break; - } - } - } + Bson filtersForTestingRunResults = com.akto.action.testing.Utils.createFiltersForTestingReport(reportFilterList); + if(!filtersForTestingRunResults.equals(Filters.empty())) filterList.add(filtersForTestingRunResults); if(queryMode == null) { if(fetchOnlyVulnerable) { @@ -569,9 +551,28 @@ private List prepareTestRunResultsFilters(ObjectId testingRunResultSummary } } + if(filterList.isEmpty()) { + filterList.add(Filters.empty()); + } + return filterList; } + public static Bson prepareTestingRunResultCustomSorting(String sortKey, int sortOrder) { + Bson sortStage = null; + if (TestingRunIssues.KEY_SEVERITY.equals(sortKey)) { + sortStage = (sortOrder == 1) ? + Aggregates.sort(Sorts.ascending("severityValue", TestingRunResult.END_TIMESTAMP)) : + Aggregates.sort(Sorts.descending("severityValue", TestingRunResult.END_TIMESTAMP)); + } else if ("time".equals(sortKey)) { + sortStage = (sortOrder == 1) ? + Aggregates.sort(Sorts.ascending(TestingRunResult.END_TIMESTAMP)) : + Aggregates.sort(Sorts.descending(TestingRunResult.END_TIMESTAMP)); + } + + return sortStage; + } + String testingRunResultSummaryHexId; List testingRunResults; private boolean fetchOnlyVulnerable; @@ -595,6 +596,12 @@ public String fetchTestingRunResults() { if (testingRunResultSummaryHexId != null) loggerMaker.infoAndAddToDb("fetchTestingRunResults called for hexId=" + testingRunResultSummaryHexId); if (queryMode != null) loggerMaker.infoAndAddToDb("fetchTestingRunResults called for queryMode="+queryMode); + Bson ignoredIssuesFilters = Filters.and( + Filters.in(TestingRunIssues.TEST_RUN_ISSUES_STATUS, "IGNORED"), + Filters.in(TestingRunIssues.LATEST_TESTING_RUN_SUMMARY_ID, testingRunResultSummaryId) + ); + List issueslist = TestingRunIssuesDao.instance.findAll(ignoredIssuesFilters, Projections.include("_id")); + List testingRunResultFilters = prepareTestRunResultsFilters(testingRunResultSummaryId, queryMode); if(queryMode == QueryMode.SKIPPED_EXEC || queryMode == QueryMode.SKIPPED_EXEC_NEED_CONFIG){ @@ -609,19 +616,13 @@ public String fetchTestingRunResults() { try { int pageLimit = limit <= 0 ? 150 : limit; - Bson sortStage = null; - if (TestingRunIssues.KEY_SEVERITY.equals(sortKey)) { - sortStage = (sortOrder == 1) ? - Aggregates.sort(Sorts.ascending("severityValue", TestingRunResult.END_TIMESTAMP)) : - Aggregates.sort(Sorts.descending("severityValue", TestingRunResult.END_TIMESTAMP)); - } else if ("time".equals(sortKey)) { - sortStage = (sortOrder == 1) ? - Aggregates.sort(Sorts.ascending(TestingRunResult.END_TIMESTAMP)) : - Aggregates.sort(Sorts.descending(TestingRunResult.END_TIMESTAMP)); - } + Bson sortStage = prepareTestingRunResultCustomSorting(sortKey, sortOrder); + Bson filters = testingRunResultFilters.isEmpty() ? Filters.empty() : Filters.and(testingRunResultFilters); this.testingRunResults = TestingRunResultDao.instance - .fetchLatestTestingRunResultWithCustomAggregations(Filters.and(testingRunResultFilters), pageLimit, skip, sortStage); + .fetchLatestTestingRunResultWithCustomAggregations(filters, pageLimit, skip, sortStage); + + removeTestingRunResultsByIssues(testingRunResults, issueslist, false); testCountMap = new HashMap<>(); for(QueryMode qm : QueryMode.values()) { @@ -635,13 +636,8 @@ public String fetchTestingRunResults() { testCountMap.put(qm.toString(), count); } - Bson ignoredIssuesFilters = Filters.and( - Filters.in(TestingRunIssues.TEST_RUN_ISSUES_STATUS, Arrays.asList("IGNORED", "FIXED")), - Filters.in(TestingRunIssues.LATEST_TESTING_RUN_SUMMARY_ID, testingRunResultSummaryId) - ); - int count = (int) TestingRunIssuesDao.instance.count(ignoredIssuesFilters); - testCountMap.put(QueryMode.VULNERABLE.name(), Math.abs(testCountMap.getOrDefault(QueryMode.VULNERABLE.name(), 0)-count)); - testCountMap.put("IGNORED_ISSUES", count); + testCountMap.put(QueryMode.VULNERABLE.name(), Math.abs(testCountMap.getOrDefault(QueryMode.VULNERABLE.name(), 0)- issueslist.size())); + testCountMap.put("IGNORED_ISSUES", issueslist.size()); } catch (Exception e) { loggerMaker.errorAndAddToDb(e, "error in fetchLatestTestingRunResult: " + e); } @@ -649,6 +645,33 @@ public String fetchTestingRunResults() { return SUCCESS.toUpperCase(); } + public static void removeTestingRunResultsByIssues(List testingRunResults, + List testingRunIssues, + boolean retainByIssues) { + Set issuesSet = new HashSet<>(); + for (TestingRunIssues issue : testingRunIssues) { + String apiInfoKeyString = issue.getId().getApiInfoKey().toString(); + String key = apiInfoKeyString + "|" + issue.getId().getTestSubCategory(); + issuesSet.add(key); + } + + Iterator resultIterator = testingRunResults.iterator(); + + while (resultIterator.hasNext()) { + TestingRunResult result = resultIterator.next(); + String apiInfoKeyString = result.getApiInfoKey().toString(); + String resultKey = apiInfoKeyString + "|" + result.getTestSubType(); + + boolean matchFound = issuesSet.contains(resultKey); + + if (retainByIssues && !matchFound) { + resultIterator.remove(); + } else if (!retainByIssues && matchFound) { + resultIterator.remove(); + } + } + } + private Map> reportFilterList; public String fetchVulnerableTestRunResults() { diff --git a/apps/dashboard/src/main/java/com/akto/action/testing/Utils.java b/apps/dashboard/src/main/java/com/akto/action/testing/Utils.java index 7a17902d60..aca9c0e0dc 100644 --- a/apps/dashboard/src/main/java/com/akto/action/testing/Utils.java +++ b/apps/dashboard/src/main/java/com/akto/action/testing/Utils.java @@ -20,7 +20,7 @@ public static Bson createFiltersForTestingReport(Map> filte String key = entry.getKey(); List value = entry.getValue(); - if (value.size() == 0) continue; + if (value.isEmpty()) continue; List collectionIds = new ArrayList<>(); if(key.equals(SingleTypeInfo._API_COLLECTION_ID)){ for(String str: value){ @@ -32,6 +32,7 @@ public static Bson createFiltersForTestingReport(Map> filte case SingleTypeInfo._METHOD: filterList.add(Filters.in(TestingRunResult.API_INFO_KEY + "." + ApiInfoKey.METHOD, value)); break; + case SingleTypeInfo._COLLECTION_IDS: case SingleTypeInfo._API_COLLECTION_ID: filterList.add(Filters.in(TestingRunResult.API_INFO_KEY + "." + ApiInfoKey.API_COLLECTION_ID, collectionIds)); break; diff --git a/apps/dashboard/src/main/java/com/akto/action/testing_issues/IssuesAction.java b/apps/dashboard/src/main/java/com/akto/action/testing_issues/IssuesAction.java index b831f63d8e..883aa251ce 100644 --- a/apps/dashboard/src/main/java/com/akto/action/testing_issues/IssuesAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/testing_issues/IssuesAction.java @@ -2,6 +2,7 @@ import com.akto.action.ExportSampleDataAction; import com.akto.action.UserAction; +import com.akto.action.testing.Utils; import com.akto.dao.HistoricalDataDao; import com.akto.dao.RBACDao; import com.akto.action.testing.StartTestAction; @@ -527,12 +528,38 @@ public String bulkUpdateIssueStatus () { String latestTestingRunSummaryId; List issueStatusQuery; + List testingRunResultList; + private Map> filters; public String fetchIssuesByStatusAndSummaryId() { - Bson filters = Filters.and( + Bson triFilters = Filters.and( Filters.in(TestingRunIssues.TEST_RUN_ISSUES_STATUS, issueStatusQuery), Filters.in(TestingRunIssues.LATEST_TESTING_RUN_SUMMARY_ID, new ObjectId(latestTestingRunSummaryId)) ); - issues = TestingRunIssuesDao.instance.findAll(filters); + issues = TestingRunIssuesDao.instance.findAll(triFilters, Projections.include("_id")); + + List testingRunResultsFilterList = new ArrayList<>(); + for(TestingRunIssues issue: issues) { + testingRunResultsFilterList.add(Filters.and( + Filters.eq(TestingRunResult.TEST_RUN_RESULT_SUMMARY_ID, new ObjectId(latestTestingRunSummaryId)), + Filters.eq(TestingRunResult.VULNERABLE, true), + Filters.eq(TestingRunResult.API_INFO_KEY, issue.getId().getApiInfoKey()), + Filters.eq(TestingRunResult.TEST_SUB_TYPE, issue.getId().getTestSubCategory()) + )); + } + + List filtersList = new ArrayList<>(); + if(!testingRunResultsFilterList.isEmpty()) filtersList.add(Filters.or(testingRunResultsFilterList)); + Bson filtersForTestingRunResults = Utils.createFiltersForTestingReport(filters); + if(!filtersForTestingRunResults.equals(Filters.empty())) filtersList.add(filtersForTestingRunResults); + Bson sortStage = StartTestAction.prepareTestingRunResultCustomSorting(sortKey, sortOrder); + + if(filtersList.isEmpty()) { + testingRunResultList = new ArrayList<>(); + return SUCCESS.toUpperCase(); + } + + testingRunResultList = TestingRunResultDao.instance.fetchLatestTestingRunResultWithCustomAggregations(Filters.and(filtersList), limit, skip, sortStage); + return SUCCESS.toUpperCase(); } @@ -824,6 +851,14 @@ public TestingRunResultSummary getTestingRunResultSummary() { return testingRunResultSummary; } + public List getTestingRunResultList() { + return testingRunResultList; + } + + public void setFilters(Map> filters) { + this.filters = filters; + } + public void setReportFilterList(Map> reportFilterList) { this.reportFilterList = reportFilterList; } diff --git a/apps/dashboard/src/main/resources/struts.xml b/apps/dashboard/src/main/resources/struts.xml index 0381d57e7b..e9536f35af 100644 --- a/apps/dashboard/src/main/resources/struts.xml +++ b/apps/dashboard/src/main/resources/struts.xml @@ -7538,9 +7538,7 @@ false ^actionErrors.* - - issues - + 422 false diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/SingleTestRunPage/SingleTestRunPage.js b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/SingleTestRunPage/SingleTestRunPage.js index 7a8013c8f1..52392bad1d 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/SingleTestRunPage/SingleTestRunPage.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/SingleTestRunPage/SingleTestRunPage.js @@ -246,6 +246,7 @@ function SingleTestRunPage() { const fetchTableData = async (sortKey, sortOrder, skip, limit, filters, filterOperators, queryValue) => { let testRunResultsRes = [] let testRunCountMap = [] + let totalIgnoredIssuesCount = 0 const { testingRun, workflowTest, testingRunType } = testingRunResultSummariesObj if(testingRun === undefined){ return {value: [], total: 0} @@ -261,35 +262,14 @@ function SingleTestRunPage() { setSelectedTestRun(localSelectedTestRun); if(localSelectedTestRun.testingRunResultSummaryHexId) { - if(selectedTab === 'ignored_issues' || selectedTab === 'vulnerable') { - let vulnerableTestingRunResults = [] - await api.fetchTestingRunResults(localSelectedTestRun.testingRunResultSummaryHexId, "VULNERABLE", sortKey, sortOrder, skip, limit, filters, queryValue).then(({ testingRunResults, testCountMap }) => { - testRunCountMap = testCountMap - vulnerableTestingRunResults = testingRunResults - testRunResultsRes = transform.prepareTestRunResults(hexId, testingRunResults, subCategoryMap, subCategoryFromSourceConfigMap) - const orderedValues = tableTabsOrder.map(key => testCountMap[tableTabMap[key]] || 0) - setTestRunResultsCount(orderedValues) - }) + if(selectedTab === 'ignored_issues') { let ignoredTestRunResults = [] - await api.fetchIssuesByStatusAndSummaryId(localSelectedTestRun.testingRunResultSummaryHexId, ["IGNORED", "FIXED"]).then((issues) => { - const ignoredTestingResults = vulnerableTestingRunResults.filter(result => { - return issues.some(issue => - issue.id.apiInfoKey.apiCollectionId === result.apiInfoKey.apiCollectionId && - issue.id.apiInfoKey.method === result.apiInfoKey.method && - issue.id.apiInfoKey.url === result.apiInfoKey.url && - issue.id.testSubCategory === result.testSubType - ) - }) - - ignoredTestRunResults = transform.prepareTestRunResults(hexId, ignoredTestingResults, subCategoryMap, subCategoryFromSourceConfigMap) - }) - - const updatedVulnerableTestRunResults = testRunResultsRes.filter(result => { - return !ignoredTestRunResults.some(ignoredResult => { - return JSON.stringify(result) === JSON.stringify(ignoredResult) - }) + await api.fetchIssuesByStatusAndSummaryId(localSelectedTestRun.testingRunResultSummaryHexId, ["IGNORED"], sortKey, sortOrder, skip, limit, filters).then((resp) => { + const ignoredIssuesTestingResult = resp?.testingRunResultList || []; + ignoredTestRunResults = transform.prepareTestRunResults(hexId, ignoredIssuesTestingResult, subCategoryMap, subCategoryFromSourceConfigMap) }) - testRunResultsRes = selectedTab === 'vulnerable' ? updatedVulnerableTestRunResults : ignoredTestRunResults + testRunResultsRes = ignoredTestRunResults + totalIgnoredIssuesCount = ignoredTestRunResults.length } else { await api.fetchTestingRunResults(localSelectedTestRun.testingRunResultSummaryHexId, tableTabMap[selectedTab], sortKey, sortOrder, skip, limit, filters, queryValue).then(({ testingRunResults, testCountMap, errorEnums }) => { testRunCountMap = testCountMap @@ -305,7 +285,7 @@ function SingleTestRunPage() { } } fillTempData(testRunResultsRes, selectedTab) - return {value: transform.getPrettifiedTestRunResults(testRunResultsRes), total: testRunCountMap[tableTabMap[selectedTab]]} + return {value: transform.getPrettifiedTestRunResults(testRunResultsRes), total: selectedTab === 'ignored_issues' ? totalIgnoredIssuesCount : testRunCountMap[tableTabMap[selectedTab]]} } useEffect(() => { diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/api.js b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/api.js index df331de45c..be973bfa69 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/api.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/api.js @@ -24,12 +24,12 @@ export default { }) return resp }, - async fetchTestingRunResults(testingRunResultSummaryHexId, queryMode, sortKey, sortOrder, skip, limit, filters, queryValue) { + async fetchTestingRunResults(testingRunResultSummaryHexId, queryMode, sortKey, sortOrder, skip, limit, reportFilterList, queryValue) { const resp = await request({ url: '/api/fetchTestingRunResults', method: 'post', data: { - testingRunResultSummaryHexId, queryMode, sortKey, sortOrder, skip, limit, filters, queryValue + testingRunResultSummaryHexId, queryMode, sortKey, sortOrder, skip, limit, reportFilterList, queryValue } }) return resp @@ -450,11 +450,11 @@ export default { data: {deltaTimeForScheduledSummaries} }) }, - fetchIssuesByStatusAndSummaryId(latestTestingRunSummaryId, issueStatusQuery) { + fetchIssuesByStatusAndSummaryId(latestTestingRunSummaryId, issueStatusQuery, sortKey, sortOrder, skip, limit, filters) { return request({ url: '/api/fetchIssuesByStatusAndSummaryId', method: 'post', - data: { latestTestingRunSummaryId, issueStatusQuery } + data: { latestTestingRunSummaryId, issueStatusQuery, sortKey, sortOrder, skip, limit, filters } }) }, modifyTestingRunConfig(testingRunConfigId, testConfigsAdvancedSettings){ diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/VulnerabilityReport.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/VulnerabilityReport.jsx index bfb8cfcc93..c2ba3f14b2 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/VulnerabilityReport.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/VulnerabilityReport.jsx @@ -81,18 +81,20 @@ const VulnerabilityReport = () => { if (window.location.pathname.includes('testing')) { const testingRunSummaryId = currentFilters.testingRunResultSummaryId[0] await api.fetchTestingRunResultsSummary(testingRunSummaryId).then((resp) => { - let startTimestamp = '-' if(resp == null) { setScanTime('-') } else if(resp?.state === 'COMPLETED') { const time = func.getTimeTakenByTest(resp?.startTimestamp, resp?.endTimestamp) setScanTime(time || '-') - startTimestamp = func.getFormattedDate(resp?.startTimestamp) } else { setScanTime("-") - startTimestamp = func.getFormattedDate(resp?.startTimestamp) } - setCurrentDate(startTimestamp) + + if(resp?.startTimestamp) { + setCurrentDate(func.getFormattedDate(resp?.startTimestamp)) + } else { + setCurrentDate('-') + } }) while (true) { diff --git a/libs/dao/src/main/java/com/akto/dao/testing/TestingRunResultDao.java b/libs/dao/src/main/java/com/akto/dao/testing/TestingRunResultDao.java index 6c1afcbab7..cb915a4f0f 100644 --- a/libs/dao/src/main/java/com/akto/dao/testing/TestingRunResultDao.java +++ b/libs/dao/src/main/java/com/akto/dao/testing/TestingRunResultDao.java @@ -225,6 +225,9 @@ public void createIndicesIfAbsent() { fieldNames = new String[]{TestingRunResult.TEST_RUN_RESULT_SUMMARY_ID, TestingRunResult.TEST_SUPER_TYPE}; MCollection.createIndexIfAbsent(getDBName(), getCollName(), fieldNames, false); + + fieldNames = new String[]{TestingRunResult.TEST_RUN_RESULT_SUMMARY_ID, TestingRunResult.VULNERABLE, TestingRunResult.API_INFO_KEY, TestingRunResult.TEST_SUB_TYPE}; + MCollection.createIndexIfAbsent(getDBName(), getCollName(), fieldNames, false); } public void convertToCappedCollection() {