From f21a266844eb227bc85afe33c8e0892474bf57ee Mon Sep 17 00:00:00 2001 From: SHIVAM RAWAT Date: Fri, 7 Feb 2025 09:27:29 +0530 Subject: [PATCH] fixing multiple issues on issues-page and api security posture page. --- .../java/com/akto/action/DashboardAction.java | 30 +++++++++++++++--- apps/dashboard/src/main/resources/struts.xml | 2 +- .../pages/dashboard/HomeDashboard.jsx | 4 +-- .../new_components/ProgressBarChart.jsx | 3 +- .../CriticalUnsecuredAPIsOverTimeGraph.jsx | 31 +++++++++++++------ .../pages/observe/api_collections/RunTest.jsx | 8 ++--- 6 files changed, 55 insertions(+), 23 deletions(-) diff --git a/apps/dashboard/src/main/java/com/akto/action/DashboardAction.java b/apps/dashboard/src/main/java/com/akto/action/DashboardAction.java index 121806bc22..e22b50b528 100644 --- a/apps/dashboard/src/main/java/com/akto/action/DashboardAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/DashboardAction.java @@ -28,6 +28,8 @@ import com.mongodb.client.MongoCursor; import com.opensymphony.xwork2.Action; +import static com.akto.dto.test_run_findings.TestingRunIssues.KEY_SEVERITY; + public class DashboardAction extends UserAction { private int startTimeStamp; @@ -100,6 +102,7 @@ public String fetchHistoricalData() { } private List severityToFetch; + private final Map> severityWiseTrendData= new HashMap<>(); private final Map trendData = new HashMap<>(); public String fetchCriticalIssuesTrend(){ if(endTimeStamp == 0) endTimeStamp = Context.now(); @@ -115,7 +118,7 @@ public String fetchCriticalIssuesTrend(){ List allowedStatus = Arrays.asList(GlobalEnums.TestRunIssueStatus.OPEN, GlobalEnums.TestRunIssueStatus.FIXED); Bson issuesFilter = Filters.and( - Filters.in(TestingRunIssues.KEY_SEVERITY, severityToFetch), + Filters.in(KEY_SEVERITY, severityToFetch), Filters.gte(TestingRunIssues.CREATION_TIME, startTimeStamp), Filters.lte(TestingRunIssues.CREATION_TIME, endTimeStamp), Filters.in(TestingRunIssues.TEST_RUN_ISSUES_STATUS, allowedStatus), @@ -135,14 +138,23 @@ public String fetchCriticalIssuesTrend(){ } } catch(Exception e){ } - pipeline.add(Aggregates.project(Projections.computed(dayOfYearFloat, new BasicDBObject("$divide", new Object[]{"$" + TestingRunIssues.CREATION_TIME, 86400})))); - - pipeline.add(Aggregates.project(Projections.computed(dayOfYear, new BasicDBObject("$floor", new Object[]{"$" + dayOfYearFloat})))); + pipeline.add(Aggregates.project( + Projections.fields( + Projections.computed(dayOfYearFloat, new BasicDBObject("$divide", new Object[]{"$" + TestingRunIssues.CREATION_TIME, 86400})), + Projections.include(KEY_SEVERITY) + ))); + + pipeline.add(Aggregates.project( + Projections.fields( + Projections.computed(dayOfYear, new BasicDBObject("$floor", new Object[]{"$" + dayOfYearFloat})), + Projections.include(KEY_SEVERITY) + ))); BasicDBObject groupedId = new BasicDBObject(dayOfYear, "$"+dayOfYear) .append("url", "$_id.apiInfoKey.url") .append("method", "$_id.apiInfoKey.method") - .append("apiCollectionId", "$_id.apiInfoKey.apiCollectionId"); + .append("apiCollectionId", "$_id.apiInfoKey.apiCollectionId") + .append(KEY_SEVERITY, "$" + KEY_SEVERITY); pipeline.add(Aggregates.group(groupedId, Accumulators.sum("count", 1))); MongoCursor issuesCursor = TestingRunIssuesDao.instance.getMCollection().aggregate(pipeline, BasicDBObject.class).cursor(); @@ -150,9 +162,13 @@ public String fetchCriticalIssuesTrend(){ while(issuesCursor.hasNext()){ BasicDBObject basicDBObject = issuesCursor.next(); BasicDBObject o = (BasicDBObject) basicDBObject.get("_id"); + String severity = o.getString(KEY_SEVERITY, GlobalEnums.Severity.LOW.name()); + Map trendData = severityWiseTrendData.computeIfAbsent(severity, k -> new HashMap<>()); int date = o.getInt(dayOfYear); int count = trendData.getOrDefault(date,0); trendData.put(date, count+1); + count = this.trendData.getOrDefault(date,0); + this.trendData.put(date, count+1); } return SUCCESS.toUpperCase(); @@ -378,4 +394,8 @@ public String getOrganization() { public void setOrganization(String organization) { this.organization = organization; } + + public Map> getSeverityWiseTrendData() { + return severityWiseTrendData; + } } diff --git a/apps/dashboard/src/main/resources/struts.xml b/apps/dashboard/src/main/resources/struts.xml index 8bb6eb56f0..5b85b05d20 100644 --- a/apps/dashboard/src/main/resources/struts.xml +++ b/apps/dashboard/src/main/resources/struts.xml @@ -7601,7 +7601,7 @@ READ - trendData + severityWiseTrendData 422 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 7759dd89bf..a56f464a52 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 @@ -237,9 +237,9 @@ function HomeDashboard() { totalTestedApis += x.apisTested }) - const tempRiskScore = totalAPIs ? (totalRiskScore / totalApis).toFixed(2) : 0 + const tempRiskScore = totalApis ? (totalRiskScore / totalApis).toFixed(2) : 0 setOldRiskScore(parseFloat(tempRiskScore)) - const tempTestCoverate = totalAPIs ? (100 * totalTestedApis / totalApis).toFixed(2) : 0 + const tempTestCoverate = totalApis ? (100 * totalTestedApis / totalApis).toFixed(2) : 0 setOldTestCoverage(parseFloat(tempTestCoverate)) } diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/dashboard/new_components/ProgressBarChart.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/dashboard/new_components/ProgressBarChart.jsx index e4893cef68..a6ddb3a6b8 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/dashboard/new_components/ProgressBarChart.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/dashboard/new_components/ProgressBarChart.jsx @@ -1,5 +1,6 @@ import { Badge, DataTable, HorizontalStack, Text } from '@shopify/polaris'; import React from 'react'; +import transform from '../../observe/transform'; import CustomProgressBar from './CustomProgressBar'; function ProgressBarChart({ data }) { @@ -16,7 +17,7 @@ function ProgressBarChart({ data }) { backgroundColor={item["backgroundColor"]} /> - {item.text} + {parseInt(item.text) !== NaN ? transform.formatNumberWithCommas(parseInt(item.text)): item.text} , ]); }; diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/issues/IssuesPage/CriticalUnsecuredAPIsOverTimeGraph.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/issues/IssuesPage/CriticalUnsecuredAPIsOverTimeGraph.jsx index c0feb24b19..afc1670e69 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/issues/IssuesPage/CriticalUnsecuredAPIsOverTimeGraph.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/issues/IssuesPage/CriticalUnsecuredAPIsOverTimeGraph.jsx @@ -8,18 +8,29 @@ import dashboardApi from "../../dashboard/api.js" const CriticalUnsecuredAPIsOverTimeGraph = ({ linkText, linkUrl }) => { const [unsecuredAPIs, setUnsecuredAPIs] = useState([]) const [showTestingComponents, setShowTestingComponents] = useState(false) + const [isDataAvailable, setIsDataAvailable] = useState(false) function buildUnsecuredAPIs(input) { - const CRITICAL_COLOR = "#E45357" + + const SEVERITY_CONFIG = { + CRITICAL: { color: "#E45357", name: "Critical Issues", data: [] }, + HIGH: { color: "#EF864C", name: "High Issues", data: [] } + }; + const transformed = [] - const criticalData = { data: [], color: CRITICAL_COLOR, name: "Critical Issues" } - for (const epoch in input) { - const epochMillis = Number(epoch) * 86400000 - criticalData.data.push([epochMillis, input[epoch]]) + let dataAvailability = false + for (const [severity, epochs] of Object.entries(input)) { + const dataset = SEVERITY_CONFIG[severity] || SEVERITY_CONFIG.HIGH; + + for (const epoch in epochs) { + dataset.data.push([Number(epoch) * 86400000, epochs[epoch]]); + dataAvailability = true + } } - transformed.push(criticalData) + transformed.push(SEVERITY_CONFIG.CRITICAL, SEVERITY_CONFIG.HIGH); setUnsecuredAPIs(transformed) + setIsDataAvailable(dataAvailability) } @@ -43,7 +54,7 @@ const CriticalUnsecuredAPIsOverTimeGraph = ({ linkText, linkUrl }) => { const runTestEmptyCardComponent = There’s no data to show. Run test to get data populated. - const criticalUnsecuredAPIsOverTime = (unsecuredAPIs && unsecuredAPIs.length > 0 && unsecuredAPIs[0].data && unsecuredAPIs[0].data.length > 0) ? 0 && isDataAvailable) ? { exportingDisabled={true} /> } - title="Critical Unsecured APIs Over Time" - titleToolTip="Chart showing the number of APIs detected(risk score >= 4) each month over the past year. Helps track security trends over time." + title="Critical or high severity Unsecured APIs Over Time" + titleToolTip="Chart showing the number of APIs detected(risk score >= 3) each month over the past year. Helps track security trends over time." linkText={linkText} linkUrl={linkUrl} - /> : No Unsecured APIs found: runTestEmptyCardComponent} /> + /> : No Unsecured APIs found: runTestEmptyCardComponent} /> return ( {...criticalUnsecuredAPIsOverTime} diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx index 9cd30ad6ea..7d1458dc06 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api_collections/RunTest.jsx @@ -31,7 +31,7 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu hourlyLabel: "Now", testRunTime: -1, testRunTimeLabel: "30 minutes", - runTypeLabel: "Now", + runTypeLabel: "Once", maxConcurrentRequests: -1, testName: "", authMechanismPresent: false, @@ -205,8 +205,8 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu handleAddSettings(parentAdvanceSettingsConfig); const getRunTypeLabel = (runType) => { - if (!runType) return "Now"; - if (runType === "CI-CD" || runType === "ONE_TIME") return "Now"; + if (!runType) return "Once"; + if (runType === "CI-CD" || runType === "ONE_TIME") return "Once"; else if (runType === "RECURRING") return "Daily"; else if (runType === "CONTINUOUS_TESTING") return "Continuously"; } @@ -430,7 +430,7 @@ function RunTest({ endpoints, filtered, apiCollectionId, disabled, runTestFromOu const testRunTimeOptions = [...runTimeMinutes, ...runTimeHours] - const runTypeOptions = [{ label: "Daily", value: "Daily" }, { label: "Continuously", value: "Continuously" }, { label: "Now", value: "Now" }] + const runTypeOptions = [{ label: "Daily", value: "Daily" }, { label: "Continuously", value: "Continuously" }, { label: "Once", value: "Once" }] const maxRequests = hours.reduce((abc, x) => { if (x < 11) {