From ef5b0a5a0f39c70721fc727a7f9f510393d1fae7 Mon Sep 17 00:00:00 2001 From: notshivansh Date: Mon, 30 Dec 2024 22:54:02 +0530 Subject: [PATCH] replace API severity graph with issue severity graph --- .../action/testing_issues/IssuesAction.java | 31 +++++++++++++++++-- apps/dashboard/src/main/resources/struts.xml | 21 +++++++++++++ .../pages/dashboard/HomeDashboard.jsx | 25 ++++++++++----- .../src/apps/dashboard/pages/testing/api.js | 7 +++++ .../vulnerability_report/ReportSummary.jsx | 2 +- .../VulnerabilityReport.jsx | 4 ++- .../testing/vulnerability_report/transform.js | 26 +++------------- .../TestingRunIssuesDao.java | 22 ++++++++++--- 8 files changed, 101 insertions(+), 37 deletions(-) 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 883aa251ce..cd9aa6f8fb 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 @@ -41,6 +41,7 @@ import com.mongodb.client.MongoCursor; import com.mongodb.client.model.*; import com.mongodb.client.result.InsertOneResult; +import com.opensymphony.xwork2.Action; import org.bson.Document; import org.bson.conversions.Bson; @@ -55,7 +56,7 @@ public class IssuesAction extends UserAction { - private static final LoggerMaker loggerMaker = new LoggerMaker(IssuesAction.class); + private static final LoggerMaker loggerMaker = new LoggerMaker(IssuesAction.class, LogDb.DASHBOARD); private static final Logger logger = LoggerFactory.getLogger(IssuesAction.class); private List issues; private TestingIssuesId issueId; @@ -74,6 +75,8 @@ public class IssuesAction extends UserAction { private List similarlyAffectedIssues; private int startEpoch; long endTimeStamp; + private Map> severityInfo = new HashMap<>(); + private Bson createFilters (boolean useFilterStatus) { Bson filters = Filters.empty(); if (useFilterStatus && filterStatus != null && !filterStatus.isEmpty()) { @@ -89,8 +92,12 @@ private Bson createFilters (boolean useFilterStatus) { filters = Filters.and(filters, Filters.in(ID + "." + TestingIssuesId.TEST_SUB_CATEGORY, filterSubCategory)); } - if (startEpoch != 0 && endTimeStamp != 0) { + + if (startEpoch != 0) { filters = Filters.and(filters, Filters.gte(TestingRunIssues.CREATION_TIME, startEpoch)); + } + + if(endTimeStamp != 0){ filters = Filters.and(filters, Filters.lt(TestingRunIssues.CREATION_TIME, endTimeStamp)); } @@ -628,6 +635,18 @@ public String getReportFilters () { return SUCCESS.toUpperCase(); } + public String fetchSeverityInfoForIssues() { + Bson filter = createFilters(true); + + if (issuesIds != null && !issuesIds.isEmpty()) { + filter = Filters.and(filter, Filters.in(Constants.ID, issuesIds)); + } + + this.severityInfo = TestingRunIssuesDao.instance.getSeveritiesMapForCollections(filter, false); + return Action.SUCCESS.toUpperCase(); + } + + public List getIssues() { return issues; } @@ -877,4 +896,12 @@ public void setIssuesIdsForReport(List issuesIdsForReport) { public BasicDBObject getResponse() { return response; } + + public Map> getSeverityInfo() { + return severityInfo; + } + + public void setSeverityInfo(Map> severityInfo) { + this.severityInfo = severityInfo; + } } diff --git a/apps/dashboard/src/main/resources/struts.xml b/apps/dashboard/src/main/resources/struts.xml index e9536f35af..26c53faa7f 100644 --- a/apps/dashboard/src/main/resources/struts.xml +++ b/apps/dashboard/src/main/resources/struts.xml @@ -4026,6 +4026,27 @@ + + + + + ISSUES + READ + + + + 403 + false + ^actionErrors.* + + + + 422 + false + ^actionErrors.* + + + diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/dashboard/HomeDashboard.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/dashboard/HomeDashboard.jsx index 8b35ca2168..d0611dec34 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/dashboard/HomeDashboard.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/dashboard/HomeDashboard.jsx @@ -134,7 +134,8 @@ function HomeDashboard() { observeApi.getUserEndpoints(), api.findTotalIssues(startTimestamp, endTimestamp), api.fetchApiStats(startTimestamp, endTimestamp), - api.fetchEndpointsCount(startTimestamp, endTimestamp) + api.fetchEndpointsCount(startTimestamp, endTimestamp), + testingApi.fetchSeverityInfoForIssues({ startEpoch: startTimestamp }, [], endTimestamp) ]; let results = await Promise.allSettled(apiPromises); @@ -143,6 +144,7 @@ function HomeDashboard() { let findTotalIssuesResp = results[1].status === 'fulfilled' ? results[1].value : {} let apisStatsResp = results[2].status === 'fulfilled' ? results[2].value : {} let fetchEndpointsCountResp = results[3].status === 'fulfilled' ? results[3].value : {} + let issueSeverityMap = results[4].status === 'fulfilled' ? results[4].value : {} setShowBannerComponent(!userEndpoints) @@ -153,7 +155,7 @@ function HomeDashboard() { buildAuthTypesData(apisStatsResp.apiStatsEnd) buildSetRiskScoreData(apisStatsResp.apiStatsEnd) //todo getCollectionsWithCoverage() - buildSeverityMap(apisStatsResp.apiStatsEnd) + buildSeverityMap(issueSeverityMap.severityInfo) buildIssuesSummary(findTotalIssuesResp) const fetchHistoricalDataResp = { "finalHistoricalData": finalHistoricalData, "initialHistoricalData": initialHistoricalData } @@ -447,8 +449,17 @@ function HomeDashboard() { /> ) : null - function buildSeverityMap(apiStats) { - const countMap = apiStats ? apiStats.criticalMap : {}; + function buildSeverityMap(severityInfo) { + const countMap = { HIGH: 0, MEDIUM: 0, LOW: 0 } + + if (severityInfo && severityInfo != undefined && severityInfo != null && severityInfo instanceof Object) { + for (const apiCollectionId in severityInfo) { + let temp = severityInfo[apiCollectionId] + for (const key in temp) { + countMap[key] += temp[key] + } + } + } const result = { "High": { @@ -508,11 +519,11 @@ function HomeDashboard() { /> } - title="Vulnerable APIs by Severity" - titleToolTip="Breakdown of vulnerable APIs categorized by severity level (High, Medium, Low). Click to see details for each category." + title="Issues by Severity" + titleToolTip="Breakdown of issues categorized by severity level (High, Medium, Low). Click to see details for each category." linkText="Fix critical issues" linkUrl="/dashboard/issues" - /> : No vulnerable APIs found: runTestEmptyCardComponent}/> + /> : No issues found for this time-frame: runTestEmptyCardComponent}/> const criticalUnsecuredAPIsOverTime = 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 abbb7a61ff..a3d530a321 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 @@ -487,5 +487,12 @@ export default { method: 'post', data: { generatedReportId } }) + }, + fetchSeverityInfoForIssues(filters, issueIds, endTimeStamp) { + return request({ + url: '/api/fetchSeverityInfoForIssues', + method: 'post', + data: {...filters, issueIds, endTimeStamp} + }) } } \ No newline at end of file diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/ReportSummary.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/ReportSummary.jsx index dbbba1b9c3..f8c85fa164 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/ReportSummary.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/ReportSummary.jsx @@ -18,7 +18,7 @@ const ReportSummary = ({ reportSummaryItems, severityMap, graphData, organizatio - Vulnerable APIs by Severity + Issues by Severity { }) issuesFilters.filterCollectionsId = issuesFilters.filterCollectionsId.filter((x) => x !== undefined) + setCurrentDate(func.getFormattedDate(Date.now()/1000)) + await issuesApi.fetchVulnerableTestingRunResultsFromIssues(issuesFilters, issueIdsFromFilter, resultsCount).then(resp => { vulnerableTestingRunResults = [...vulnerableTestingRunResults, ...resp.testingRunResults] testingRunCountsFromDB = resp.testingRunResults.length @@ -207,7 +209,7 @@ const VulnerabilityReport = () => { setAktoRecommendations(vulMap.aktoRecommendations) setGraphData(vulMap.graphData) - const severityMapRes = reportTransform.createVulnerableAPIsSeverity(vulnerableTestingRunResults, categoryMap) + const severityMapRes = reportTransform.createVulnerableAPIsSeverity(vulnerableTestingRunResults) setSeverityMap(severityMapRes) } diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/transform.js b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/transform.js index 787b77e1f1..5ca146ee18 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/transform.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/testing/vulnerability_report/transform.js @@ -2,33 +2,18 @@ import { Badge, Box, Link } from "@shopify/polaris" import func from "@/util/func" const reportTransform = { - createVulnerableAPIsSeverity: (vulnerableTestingRunResults, categoryMap) => { - - const severityOrder = func.getAktoSeverities().reverse() + createVulnerableAPIsSeverity: (vulnerableTestingRunResults) => { const countMap = { HIGH: 0, MEDIUM: 0, LOW: 0, } - const endpointMap = {} - vulnerableTestingRunResults.forEach(item => { - const endpointKey = `${item.apiInfoKey.apiCollectionId}-${item.apiInfoKey.method}-${item.apiInfoKey.url}` - const highestConfidence = item.testResults.reduce((max, result) => { - return severityOrder.indexOf(result.confidence) > severityOrder.indexOf(max) - ? result.confidence - : max - }, 'LOW') - if (!endpointMap[endpointKey] || severityOrder.indexOf(highestConfidence) > severityOrder.indexOf(endpointMap[endpointKey])) { - endpointMap[endpointKey] = highestConfidence - } - }) - - Object.values(endpointMap).forEach(severity => { - if(countMap[severity] !== undefined) { - countMap[severity]++ - } + const confidence = item.testResults.filter((result) => { + return result.vulnerable + }).map((result) => result.confidence)[0] + countMap[confidence]++ }) const result = { @@ -48,7 +33,6 @@ const reportTransform = { "filterKey": "Low" } } - return result }, diff --git a/libs/dao/src/main/java/com/akto/dao/testing_run_findings/TestingRunIssuesDao.java b/libs/dao/src/main/java/com/akto/dao/testing_run_findings/TestingRunIssuesDao.java index b3d507cd27..e43fef603e 100644 --- a/libs/dao/src/main/java/com/akto/dao/testing_run_findings/TestingRunIssuesDao.java +++ b/libs/dao/src/main/java/com/akto/dao/testing_run_findings/TestingRunIssuesDao.java @@ -64,10 +64,18 @@ public void createIndicesIfAbsent() { } public Map> getSeveritiesMapForCollections(){ + return getSeveritiesMapForCollections(null, true); + } + + public Map> getSeveritiesMapForCollections(Bson filter, boolean expandApiGroups){ Map> resultMap = new HashMap<>() ; List pipeline = new ArrayList<>(); pipeline.add(Aggregates.match(Filters.eq(TestingRunIssues.TEST_RUN_ISSUES_STATUS, "OPEN"))); + if(filter!=null){ + pipeline.add(Aggregates.match(filter)); + } + try { List collectionIds = UsersCollectionsList.getCollectionsIdForUser(Context.userId.get(), Context.accountId.get()); if(collectionIds != null) { @@ -76,12 +84,16 @@ public Map> getSeveritiesMapForCollections(){ } catch(Exception e){ } - UnwindOptions unwindOptions = new UnwindOptions(); - unwindOptions.preserveNullAndEmptyArrays(false); - pipeline.add(Aggregates.unwind("$collectionIds", unwindOptions)); + BasicDBObject groupedId = new BasicDBObject("apiCollectionId", "$_id.apiInfoKey.apiCollectionId") + .append("severity", "$severity"); - BasicDBObject groupedId = new BasicDBObject("apiCollectionId", "$collectionIds") - .append("severity", "$severity") ; + if (expandApiGroups) { + UnwindOptions unwindOptions = new UnwindOptions(); + unwindOptions.preserveNullAndEmptyArrays(false); + pipeline.add(Aggregates.unwind("$collectionIds", unwindOptions)); + groupedId = new BasicDBObject("apiCollectionId", "$collectionIds") + .append("severity", "$severity"); + } pipeline.add(Aggregates.group(groupedId, Accumulators.sum("count", 1)));