Skip to content

Commit

Permalink
Merge pull request #2062 from akto-api-security/feature/bugfixes-issu…
Browse files Browse the repository at this point in the history
…es-postures

fixing multiple issues on issues-page and api security posture page.
  • Loading branch information
shivam-rawat-akto authored Feb 10, 2025
2 parents 9e60d36 + f21a266 commit 92bcced
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 23 deletions.
30 changes: 25 additions & 5 deletions apps/dashboard/src/main/java/com/akto/action/DashboardAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -100,6 +102,7 @@ public String fetchHistoricalData() {
}

private List<String> severityToFetch;
private final Map<String, Map<Integer, Integer>> severityWiseTrendData= new HashMap<>();
private final Map<Integer, Integer> trendData = new HashMap<>();
public String fetchCriticalIssuesTrend(){
if(endTimeStamp == 0) endTimeStamp = Context.now();
Expand All @@ -115,7 +118,7 @@ public String fetchCriticalIssuesTrend(){

List<GlobalEnums.TestRunIssueStatus> 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),
Expand All @@ -135,24 +138,37 @@ 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<BasicDBObject> issuesCursor = TestingRunIssuesDao.instance.getMCollection().aggregate(pipeline, BasicDBObject.class).cursor();

while(issuesCursor.hasNext()){
BasicDBObject basicDBObject = issuesCursor.next();
BasicDBObject o = (BasicDBObject) basicDBObject.get("_id");
String severity = o.getString(KEY_SEVERITY, GlobalEnums.Severity.LOW.name());
Map<Integer, Integer> 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();
Expand Down Expand Up @@ -378,4 +394,8 @@ public String getOrganization() {
public void setOrganization(String organization) {
this.organization = organization;
}

public Map<String, Map<Integer, Integer>> getSeverityWiseTrendData() {
return severityWiseTrendData;
}
}
2 changes: 1 addition & 1 deletion apps/dashboard/src/main/resources/struts.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7685,7 +7685,7 @@
<param name="accessType">READ</param>
</interceptor-ref>
<result name="SUCCESS" type="json">
<param name="root">trendData</param>
<param name="root">severityWiseTrendData</param>
</result>
<result name="ERROR" type="json">
<param name="statusCode">422</param>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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 }) {
Expand All @@ -16,7 +17,7 @@ function ProgressBarChart({ data }) {
backgroundColor={item["backgroundColor"]}
/>
</div>
<Text>{item.text}</Text>
<Text>{parseInt(item.text) !== NaN ? transform.formatNumberWithCommas(parseInt(item.text)): item.text}</Text>
</HorizontalStack>,
]);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}


Expand All @@ -43,7 +54,7 @@ const CriticalUnsecuredAPIsOverTimeGraph = ({ linkText, linkUrl }) => {

const runTestEmptyCardComponent = <Text alignment='center' color='subdued'>There’s no data to show. <Link url="/dashboard/testing" target='_blank'>Run test</Link> to get data populated. </Text>

const criticalUnsecuredAPIsOverTime = (unsecuredAPIs && unsecuredAPIs.length > 0 && unsecuredAPIs[0].data && unsecuredAPIs[0].data.length > 0) ? <InfoCard
const criticalUnsecuredAPIsOverTime = (unsecuredAPIs && unsecuredAPIs.length > 0 && isDataAvailable) ? <InfoCard
component={
<StackedChart
type='column'
Expand All @@ -61,11 +72,11 @@ const CriticalUnsecuredAPIsOverTimeGraph = ({ linkText, linkUrl }) => {
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}
/> : <EmptyCard title="Critical Unsecured APIs Over Time" subTitleComponent={showTestingComponents ? <Text alignment='center' color='subdued'>No Unsecured APIs found</Text>: runTestEmptyCardComponent} />
/> : <EmptyCard title="Critical or high severity Unsecured APIs Over Time" subTitleComponent={showTestingComponents ? <Text alignment='center' color='subdued'>No Unsecured APIs found</Text>: runTestEmptyCardComponent} />

return (
{...criticalUnsecuredAPIsOverTime}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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";
}
Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit 92bcced

Please sign in to comment.