From 6a520e6ec83d09415a117f985539e1e4a2ca4b40 Mon Sep 17 00:00:00 2001 From: gauravmann Date: Fri, 31 Oct 2025 19:57:06 +0530 Subject: [PATCH 1/3] release dashboard for all added filter for dashboard graphs for ignored malicious events and successful exploits moved from ignored only to add filter for all status of threat detection remove from other pages, and UI fixes --- .../threat_detection/ThreatActorAction.java | 15 ++++ .../threat_detection/ThreatApiAction.java | 49 ++++++++++- .../components/layouts/leftnav/LeftNav.js | 2 +- .../threat_detection/ThreatActorPage.jsx | 26 +++--- .../pages/threat_detection/ThreatApiPage.jsx | 26 +++--- .../threat_detection/ThreatDashboardPage.jsx | 87 +++++++++++++++++-- .../threat_detection/ThreatDetectionPage.jsx | 11 ++- .../dashboard/pages/threat_detection/api.js | 78 ++++++++++++++--- .../components/ThreatCategoryStackedChart.jsx | 9 +- .../components/ThreatSankeyChart.jsx | 6 +- .../components/ThreatWorldMap.jsx | 6 +- .../backend/router/DashboardRouter.java | 12 ++- .../service/MaliciousEventService.java | 20 +++-- .../backend/service/ThreatActorService.java | 59 +++++++++++-- .../backend/service/ThreatApiService.java | 36 ++++++-- .../dashboard_service/v1/service.proto | 12 +++ 16 files changed, 372 insertions(+), 82 deletions(-) diff --git a/apps/dashboard/src/main/java/com/akto/action/threat_detection/ThreatActorAction.java b/apps/dashboard/src/main/java/com/akto/action/threat_detection/ThreatActorAction.java index 14deca0e06..d74a22cfef 100644 --- a/apps/dashboard/src/main/java/com/akto/action/threat_detection/ThreatActorAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/threat_detection/ThreatActorAction.java @@ -63,6 +63,7 @@ public class ThreatActorAction extends AbstractThreatDetectionAction { List host; int startTs; int endTs; + Boolean successfulExploit; String splunkUrl; String splunkToken; String actorIp; @@ -103,6 +104,12 @@ public String getActorsCountPerCounty() { put("start_ts", startTs); put("end_ts", endTs); put("latestAttack", templatesContext); + if (successfulExploit != null) { + put("successful_exploit", successfulExploit); + } + if (status != null && !status.isEmpty()) { + put("status", status); + } } }; String msg = objectMapper.valueToTree(body).toString(); @@ -769,4 +776,12 @@ public Map getSort() { public void setSort(Map sort) { this.sort = sort; } + + public Boolean getSuccessfulExploit() { + return successfulExploit; + } + + public void setSuccessfulExploit(Boolean successfulExploit) { + this.successfulExploit = successfulExploit; + } } diff --git a/apps/dashboard/src/main/java/com/akto/action/threat_detection/ThreatApiAction.java b/apps/dashboard/src/main/java/com/akto/action/threat_detection/ThreatApiAction.java index 733b0f4abb..fbc89ecdf7 100644 --- a/apps/dashboard/src/main/java/com/akto/action/threat_detection/ThreatApiAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/threat_detection/ThreatApiAction.java @@ -45,6 +45,8 @@ public class ThreatApiAction extends AbstractThreatDetectionAction { List latestAttack; int startTs; int endTs; + Boolean successfulExploit; + String status; // Can be "ACTIVE", "UNDER_REVIEW", or "IGNORED" @Getter int totalAnalysed; @Getter int totalAttacks; @@ -109,6 +111,12 @@ public String fetchThreatCategoryCount() { put("start_ts", startTs); put("end_ts", endTs); put("latestAttack", getTemplates(latestAttack)); + if (successfulExploit != null) { + put("successful_exploit", successfulExploit); + } + if (status != null && !status.isEmpty()) { + put("status", status); + } } }; String msg = objectMapper.valueToTree(body).toString(); @@ -153,6 +161,12 @@ public String fetchCountBySeverity() { put("start_ts", startTs); put("end_ts", endTs); put("latestAttack", getTemplates(latestAttack)); + if (successfulExploit != null) { + put("successful_exploit", successfulExploit); + } + if (status != null && !status.isEmpty()) { + put("status", status); + } } }; String msg = objectMapper.valueToTree(body).toString(); @@ -193,6 +207,12 @@ public String getDailyThreatActorsCount() { put("start_ts", startTs); put("end_ts", endTs); put("latestAttack", templatesContext); + if (successfulExploit != null) { + put("successful_exploit", successfulExploit); + } + if (status != null && !status.isEmpty()) { + put("status", status); + } } }; String msg = objectMapper.valueToTree(body).toString(); @@ -242,6 +262,12 @@ public String getThreatActivityTimeline() { put("start_ts", startTs); put("end_ts", endTs); put("latestAttack", templatesContext); + if (successfulExploit != null) { + put("successful_exploit", successfulExploit); + } + if (status != null && !status.isEmpty()) { + put("status", status); + } } }; String msg = objectMapper.valueToTree(body).toString(); @@ -342,6 +368,12 @@ public String fetchThreatTopNData() { put("end_ts", endTs); put("latestAttack", templatesContext); put("limit", 5); + if (successfulExploit != null) { + put("successful_exploit", successfulExploit); + } + if (status != null && !status.isEmpty()) { + put("status", status); + } } }; String msg = objectMapper.valueToTree(body).toString(); @@ -446,6 +478,21 @@ public void setEndTs(int endTs) { public void setLatestAttack(List latestAttack) { this.latestAttack = latestAttack; } + + public Boolean getSuccessfulExploit() { + return successfulExploit; + } + + public void setSuccessfulExploit(Boolean successfulExploit) { + this.successfulExploit = successfulExploit; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } } - diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/leftnav/LeftNav.js b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/leftnav/LeftNav.js index a9675010fd..6551f770cf 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/leftnav/LeftNav.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/leftnav/LeftNav.js @@ -351,7 +351,7 @@ export default function LeftNav() { url: "#", key: "7", subNavigationItems: [ - ...(dashboardCategory === "API Security" && func.isDemoAccount() ? [{ + ...(dashboardCategory === "API Security" ? [{ label: "Dashboard", onClick: () => { navigate("/dashboard/protection/threat-dashboard"); diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatActorPage.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatActorPage.jsx index 67d056dd53..3ff773ab34 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatActorPage.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatActorPage.jsx @@ -10,7 +10,7 @@ import { ActorDetails } from "./components/ActorDetails"; import ThreatWorldMap from "./components/ThreatWorldMap"; // import ThreatApiSubcategoryCount from "./components/ThreatApiSubcategoryCount"; -import { HorizontalGrid, VerticalStack } from "@shopify/polaris"; +import { HorizontalGrid, VerticalStack, HorizontalStack, Checkbox, Select } from "@shopify/polaris"; import { ThreatSummary } from "./components/ThreatSummary"; import ThreatActivityTimeline from "./components/ThreatActivityTimeline"; import React from "react"; @@ -82,17 +82,19 @@ function ThreatActorPage() { title={} isFirstPage={true} primaryAction={ - - dispatchCurrDateRange({ - type: "update", - period: dateObj.period, - title: dateObj.title, - alias: dateObj.alias, - }) - } - /> + + + dispatchCurrDateRange({ + type: "update", + period: dateObj.period, + title: dateObj.title, + alias: dateObj.alias, + }) + } + /> + } components={components} /> diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatApiPage.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatApiPage.jsx index 98807fcf94..2232dbb926 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatApiPage.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatApiPage.jsx @@ -10,7 +10,7 @@ import TopThreatTypeChart from "./components/TopThreatTypeChart"; import ThreatApiSubcategoryCount from "./components/ThreatApiSubcategoryCount"; import api from "./api"; -import { HorizontalGrid } from "@shopify/polaris"; +import { HorizontalGrid, HorizontalStack, Checkbox, Select } from "@shopify/polaris"; import threatDetectionFunc from "./transform"; import { getDashboardCategory, mapLabel } from "../../../main/labelHelper"; function ThreatApiPage() { @@ -60,17 +60,19 @@ function ThreatApiPage() { title={} isFirstPage={true} primaryAction={ - - dispatchCurrDateRange({ - type: "update", - period: dateObj.period, - title: dateObj.title, - alias: dateObj.alias, - }) - } - /> + + + dispatchCurrDateRange({ + type: "update", + period: dateObj.period, + title: dateObj.title, + alias: dateObj.alias, + }) + } + /> + } components={components} /> diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatDashboardPage.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatDashboardPage.jsx index b759fbf6cb..dc20107830 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatDashboardPage.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatDashboardPage.jsx @@ -1,6 +1,6 @@ import React, { useEffect, useReducer, useState, useCallback } from 'react' import PageWithMultipleCards from "../../components/layouts/PageWithMultipleCards" -import { Box, DataTable, HorizontalGrid, HorizontalStack, Icon, Text, VerticalStack, Badge } from '@shopify/polaris'; +import { Box, DataTable, HorizontalGrid, HorizontalStack, Icon, Text, VerticalStack, Badge, Checkbox, Button, Popover, ActionList } from '@shopify/polaris'; import SummaryCard from '../dashboard/new_components/SummaryCard'; import { ArrowUpMinor, ArrowDownMinor } from '@shopify/polaris-icons'; import InfoCard from '../dashboard/new_components/InfoCard'; @@ -22,6 +22,9 @@ import api from './api'; function ThreatDashboardPage() { const [loading, setLoading] = useState(true); + const [status, setStatus] = useState(''); // Default: show all + const [onlySuccessfulExploits, setOnlySuccessfulExploits] = useState(false); // Default: show all + const [statusPopoverActive, setStatusPopoverActive] = useState(false); // Summary metrics state const [summaryMetrics, setSummaryMetrics] = useState({ @@ -65,7 +68,7 @@ function ThreatDashboardPage() { // Row 1: Summary metrics - Use getDailyThreatActorsCount API let summaryResponse = null try { - summaryResponse = await api.getDailyThreatActorsCount(startTimestamp, endTimestamp, []) + summaryResponse = await api.getDailyThreatActorsCount(startTimestamp, endTimestamp, [], onlySuccessfulExploits, status) if (summaryResponse) { // Use actorsCounts latest entry for active actors similar to ThreatSummary.jsx let activeActorsValue = summaryResponse.totalActive || 0 @@ -110,7 +113,7 @@ function ThreatDashboardPage() { // Severity Distribution - Use API try { - const severityResponse = await api.fetchCountBySeverity(startTimestamp, endTimestamp) + const severityResponse = await api.fetchCountBySeverity(startTimestamp, endTimestamp, [], onlySuccessfulExploits, status) if (severityResponse?.categoryCounts && Array.isArray(severityResponse.categoryCounts)) { const categoryCounts = severityResponse.categoryCounts @@ -162,7 +165,7 @@ function ThreatDashboardPage() { // Row 4: Top Attacked Hosts and APIs via common API try { - const topResponse = await api.fetchThreatTopNData(startTimestamp, endTimestamp, [], 5) + const topResponse = await api.fetchThreatTopNData(startTimestamp, endTimestamp, [], 5, onlySuccessfulExploits, status) if (topResponse?.topApis && Array.isArray(topResponse.topApis)) { setTopAttackedApis(topResponse.topApis) } else { @@ -193,7 +196,7 @@ function ThreatDashboardPage() { } finally { setLoading(false) } - }, [startTimestamp, endTimestamp]) + }, [startTimestamp, endTimestamp, onlySuccessfulExploits, status]) useEffect(() => { @@ -220,7 +223,7 @@ function ThreatDashboardPage() { const summaryCards = [ { - title: 'Total Analysed', + title: 'Total Attacks', data: observeFunc.formatNumberWithCommas(summaryMetrics.currentPeriod.totalAnalysed), variant: 'heading2xl', byLineComponent: generateChangeIndicator( @@ -230,7 +233,7 @@ function ThreatDashboardPage() { smoothChartComponent: (), }, { - title: 'Total Attacks', + title: 'Successful Attacks', data: observeFunc.formatNumberWithCommas(summaryMetrics.currentPeriod.totalAttacks), variant: 'heading2xl', color: 'critical', @@ -275,6 +278,8 @@ function ThreatDashboardPage() { ) @@ -283,6 +288,8 @@ function ThreatDashboardPage() { + ) const dashboardRows = [ @@ -457,7 +469,64 @@ function ThreatDashboardPage() { } isFirstPage={true} components={pageContent} - primaryAction={ dispatchCurrDateRange({ type: "update", period: dateObj.period, title: dateObj.title, alias: dateObj.alias })} />} + primaryAction={ + + + setStatusPopoverActive(!statusPopoverActive)} disclosure> + {status === '' ? 'All Statuses' : status === 'ACTIVE' ? 'Active' : status === 'UNDER_REVIEW' ? 'Under Review' : 'Ignored'} + + } + onClose={() => setStatusPopoverActive(false)} + > + + { + setStatus(''); + setStatusPopoverActive(false); + } + }, + { + content: 'Active', + onAction: () => { + setStatus('ACTIVE'); + setStatusPopoverActive(false); + } + }, + { + content: 'Under Review', + onAction: () => { + setStatus('UNDER_REVIEW'); + setStatusPopoverActive(false); + } + }, + { + content: 'Ignored', + onAction: () => { + setStatus('IGNORED'); + setStatusPopoverActive(false); + } + } + ]} + /> + + + dispatchCurrDateRange({ type: "update", period: dateObj.period, title: dateObj.title, alias: dateObj.alias })} + /> + + } /> } diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatDetectionPage.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatDetectionPage.jsx index 53c74fcaf2..e2148207ee 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatDetectionPage.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatDetectionPage.jsx @@ -10,7 +10,7 @@ import SampleDetails from "./components/SampleDetails"; import threatDetectionRequests from "./api"; import tempFunc from "./dummyData"; import NormalSampleDetails from "./components/NormalSampleDetails"; -import { HorizontalGrid, VerticalStack, HorizontalStack, Popover, Button, ActionList, Box, Icon, Badge, Text} from "@shopify/polaris"; +import { HorizontalGrid, VerticalStack, HorizontalStack, Popover, Button, ActionList, Box, Icon, Badge, Text, Checkbox, Select} from "@shopify/polaris"; import { FileMinor } from '@shopify/polaris-icons'; import TopThreatTypeChart from "./components/TopThreatTypeChart"; import api from "./api"; @@ -519,7 +519,14 @@ function ThreatDetectionPage() { /> } isFirstPage={true} - primaryAction={ dispatchCurrDateRange({ type: "update", period: dateObj.period, title: dateObj.title, alias: dateObj.alias })} />} + primaryAction={ + + dispatchCurrDateRange({ type: "update", period: dateObj.period, title: dateObj.title, alias: dateObj.alias })} + /> + + } components={components} secondaryActions={secondaryActionsComp} /> diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/api.js b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/api.js index 2d27b68642..f1e274c90e 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/api.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/api.js @@ -80,11 +80,21 @@ const threatDetectionRequests = { } }) }, - getActorsCountPerCounty(startTs, endTs) { + getActorsCountPerCounty(startTs, endTs, latestAttack, successfulExploit, status) { + const data = {startTs, endTs} + if (latestAttack && latestAttack.length > 0) { + data.latestAttack = latestAttack + } + if (typeof successfulExploit === 'boolean') { + data.successfulExploit = successfulExploit + } + if (typeof status === 'string' && status.trim() !== '') { + data.status = status + } return request({ url: '/api/getActorsCountPerCounty', method: 'post', - data: {startTs, endTs} + data }) }, fetchThreatConfiguration() { @@ -100,11 +110,21 @@ const threatDetectionRequests = { data: { threatConfiguration: data} }) }, - fetchThreatCategoryCount(startTs, endTs) { + fetchThreatCategoryCount(startTs, endTs, latestAttack, successfulExploit, status) { + const data = {startTs, endTs} + if (latestAttack && latestAttack.length > 0) { + data.latestAttack = latestAttack + } + if (typeof successfulExploit === 'boolean') { + data.successfulExploit = successfulExploit + } + if (typeof status === 'string' && status.trim() !== '') { + data.status = status + } return request({ url: '/api/fetchThreatCategoryCount', method: 'post', - data: {startTs, endTs} + data }) }, fetchMaliciousRequest(refId, eventType, actor, filterId) { @@ -114,25 +134,52 @@ const threatDetectionRequests = { data: {refId, eventType, actor, filterId} }) }, - fetchCountBySeverity(startTs, endTs) { + fetchCountBySeverity(startTs, endTs, latestAttack, successfulExploit, status) { + const data = {startTs, endTs} + if (latestAttack && latestAttack.length > 0) { + data.latestAttack = latestAttack + } + if (typeof successfulExploit === 'boolean') { + data.successfulExploit = successfulExploit + } + if (typeof status === 'string' && status.trim() !== '') { + data.status = status + } return request({ url: '/api/fetchCountBySeverity', method: 'post', - data: {startTs, endTs} + data }) }, - getThreatActivityTimeline(startTs, endTs) { + getThreatActivityTimeline(startTs, endTs, latestAttack, successfulExploit, status) { + const data = {startTs, endTs} + if (latestAttack && latestAttack.length > 0) { + data.latestAttack = latestAttack + } + if (typeof successfulExploit === 'boolean') { + data.successfulExploit = successfulExploit + } + if (typeof status === 'string' && status.trim() !== '') { + data.status = status + } return request({ url: '/api/getThreatActivityTimeline', method: 'post', - data: {startTs, endTs} + data }) }, - getDailyThreatActorsCount(startTs, endTs, latestAttack) { + getDailyThreatActorsCount(startTs, endTs, latestAttack, successfulExploit, status) { + const data = {startTs, endTs, latestAttack: latestAttack || []} + if (typeof successfulExploit === 'boolean') { + data.successfulExploit = successfulExploit + } + if (typeof status === 'string' && status.trim() !== '') { + data.status = status + } return request({ url: '/api/getDailyThreatActorsCount', method: 'post', - data: {startTs, endTs, latestAttack: latestAttack || []} + data }) }, fetchSensitiveParamsForEndpoints (urls) { @@ -188,11 +235,18 @@ const threatDetectionRequests = { data: data }) }, - fetchThreatTopNData(startTs, endTs, latestAttack, limit = 5) { + fetchThreatTopNData(startTs, endTs, latestAttack, limit = 5, successfulExploit, status) { + const data = {startTs, endTs, latestAttack: latestAttack || [], limit} + if (typeof successfulExploit === 'boolean') { + data.successfulExploit = successfulExploit + } + if (typeof status === 'string' && status.trim() !== '') { + data.status = status + } return request({ url: '/api/fetchThreatTopNData', method: 'post', - data: {startTs, endTs, latestAttack: latestAttack || [], limit} + data }) } } diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/ThreatCategoryStackedChart.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/ThreatCategoryStackedChart.jsx index ab40846c19..e0a8857e45 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/ThreatCategoryStackedChart.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/ThreatCategoryStackedChart.jsx @@ -277,7 +277,7 @@ const ChartLegend = ({ items, onToggle }) => { /** * Stacked Percent Area Chart for threat categories over time. */ -function ThreatCategoryStackedChart({ startTimestamp, endTimestamp }) { +function ThreatCategoryStackedChart({ startTimestamp, endTimestamp, successfulExploit, status }) { const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [chartData, setChartData] = useState([]); @@ -296,7 +296,10 @@ function ThreatCategoryStackedChart({ startTimestamp, endTimestamp }) { // Fetch time-series threat activity for given range const resp = await api.getThreatActivityTimeline( startTimestamp, - endTimestamp + endTimestamp, + [], + successfulExploit, + status ); if (!mounted) return; @@ -361,7 +364,7 @@ function ThreatCategoryStackedChart({ startTimestamp, endTimestamp }) { return () => { mounted = false; }; - }, [startTimestamp, endTimestamp]); + }, [startTimestamp, endTimestamp, successfulExploit, status]); // When visibleSeries changes, update chart series visibility but keep percentages constant useEffect(() => { diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/ThreatSankeyChart.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/ThreatSankeyChart.jsx index 7210b47946..cbc3bea8a2 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/ThreatSankeyChart.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/ThreatSankeyChart.jsx @@ -3,7 +3,7 @@ import InfoCard from "../../dashboard/new_components/InfoCard"; import SankeyChart from "../../../components/charts/SankeyChart"; import api from "../api"; -function ThreatSankeyChart({ startTimestamp, endTimestamp }) { +function ThreatSankeyChart({ startTimestamp, endTimestamp, successfulExploit, status }) { const [chartData, setChartData] = useState([]); const formatCategoryName = (category) => { @@ -21,7 +21,7 @@ function ThreatSankeyChart({ startTimestamp, endTimestamp }) { let mounted = true; const fetchData = async () => { try { - const res = await api.fetchThreatCategoryCount(startTimestamp, endTimestamp); + const res = await api.fetchThreatCategoryCount(startTimestamp, endTimestamp, [], successfulExploit, status); if (!mounted) return; @@ -65,7 +65,7 @@ function ThreatSankeyChart({ startTimestamp, endTimestamp }) { fetchData(); return () => { mounted = false; }; - }, [startTimestamp, endTimestamp]); + }, [startTimestamp, endTimestamp, successfulExploit, status]); return ( { // setLoading(true); - const res = await api.getActorsCountPerCounty(startTimestamp, endTimestamp); + const res = await api.getActorsCountPerCounty(startTimestamp, endTimestamp, [], successfulExploit, status); if (res?.actorsCountPerCountry) { setData( res.actorsCountPerCountry.map((x) => { @@ -136,7 +136,7 @@ function ThreatWorldMap({ startTimestamp, endTimestamp, style}) { useEffect(() => { fetchActorsPerCountry(); fetchMapData(); - }, [startTimestamp, endTimestamp]); + }, [startTimestamp, endTimestamp, successfulExploit, status]); if (loading) { return ; diff --git a/apps/threat-detection-backend/src/main/java/com/akto/threat/backend/router/DashboardRouter.java b/apps/threat-detection-backend/src/main/java/com/akto/threat/backend/router/DashboardRouter.java index 0ae6655835..048889bc7e 100644 --- a/apps/threat-detection-backend/src/main/java/com/akto/threat/backend/router/DashboardRouter.java +++ b/apps/threat-detection-backend/src/main/java/com/akto/threat/backend/router/DashboardRouter.java @@ -380,7 +380,9 @@ public Router setup(Vertx vertx) { ctx.get("accountId"), req.getStartTs(), req.getEndTs(), - req.getLatestAttackList() + req.getLatestAttackList(), + req.hasSuccessfulExploit() ? req.getSuccessfulExploit() : null, + req.hasStatus() && !req.getStatus().isEmpty() ? req.getStatus() : null ) ).ifPresent(s -> ctx.response().setStatusCode(200).end(s)); }); @@ -406,7 +408,9 @@ public Router setup(Vertx vertx) { ctx.get("accountId"), req.getStartTs(), req.getEndTs(), - req.getLatestAttackList() + req.getLatestAttackList(), + req.hasSuccessfulExploit() ? req.getSuccessfulExploit() : null, + req.hasStatus() && !req.getStatus().isEmpty() ? req.getStatus() : null ) ).ifPresent(s -> ctx.response().setStatusCode(200).end(s)); }); @@ -535,7 +539,9 @@ public Router setup(Vertx vertx) { req.getStartTs(), req.getEndTs(), req.getLatestAttackList(), - req.getLimit() + req.getLimit(), + req.hasSuccessfulExploit() ? req.getSuccessfulExploit() : null, + req.hasStatus() && !req.getStatus().isEmpty() ? req.getStatus() : null ) ).ifPresent(s -> ctx.response().setStatusCode(200).end(s)); }); diff --git a/apps/threat-detection-backend/src/main/java/com/akto/threat/backend/service/MaliciousEventService.java b/apps/threat-detection-backend/src/main/java/com/akto/threat/backend/service/MaliciousEventService.java index e4a4b86ee9..ec143dac4c 100644 --- a/apps/threat-detection-backend/src/main/java/com/akto/threat/backend/service/MaliciousEventService.java +++ b/apps/threat-detection-backend/src/main/java/com/akto/threat/backend/service/MaliciousEventService.java @@ -170,17 +170,19 @@ private Set findDistinctFields( public ThreatActorFilterResponse fetchThreatActorFilters( String accountId, ThreatActorFilterRequest request) { + Bson statusFilter = Filters.ne("status", "IGNORED"); + Set latestAttack = - this.findDistinctFields(accountId, "filterId", String.class, Filters.empty()); + this.findDistinctFields(accountId, "filterId", String.class, statusFilter); Set countries = - this.findDistinctFields(accountId, "country", String.class, Filters.empty()); + this.findDistinctFields(accountId, "country", String.class, statusFilter); Set actorIds = - this.findDistinctFields(accountId, "actor", String.class, Filters.empty()); + this.findDistinctFields(accountId, "actor", String.class, statusFilter); Set hosts = - this.findDistinctFields(accountId, "host", String.class, Filters.empty()); + this.findDistinctFields(accountId, "host", String.class, statusFilter); return ThreatActorFilterResponse.newBuilder().addAllSubCategories(latestAttack).addAllCountries(countries).addAllActorId(actorIds).addAllHost(hosts).build(); } @@ -188,14 +190,16 @@ public ThreatActorFilterResponse fetchThreatActorFilters( public FetchAlertFiltersResponse fetchAlertFilters( String accountId, FetchAlertFiltersRequest request) { + Bson statusFilter = Filters.ne("status", "IGNORED"); + Set actors = - this.findDistinctFields(accountId, "actor", String.class, Filters.empty()); + this.findDistinctFields(accountId, "actor", String.class, statusFilter); Set urls = - this.findDistinctFields(accountId, "latestApiEndpoint", String.class, Filters.empty()); + this.findDistinctFields(accountId, "latestApiEndpoint", String.class, statusFilter); Set subCategories = - this.findDistinctFields(accountId, "filterId", String.class, Filters.empty()); + this.findDistinctFields(accountId, "filterId", String.class, statusFilter); Set hosts = - this.findDistinctFields(accountId, "host", String.class, Filters.empty()); + this.findDistinctFields(accountId, "host", String.class, statusFilter); return FetchAlertFiltersResponse.newBuilder().addAllActors(actors).addAllUrls(urls).addAllSubCategory(subCategories).addAllHosts(hosts).build(); } diff --git a/apps/threat-detection-backend/src/main/java/com/akto/threat/backend/service/ThreatActorService.java b/apps/threat-detection-backend/src/main/java/com/akto/threat/backend/service/ThreatActorService.java index ec2e10f2b8..aeaba3a418 100644 --- a/apps/threat-detection-backend/src/main/java/com/akto/threat/backend/service/ThreatActorService.java +++ b/apps/threat-detection-backend/src/main/java/com/akto/threat/backend/service/ThreatActorService.java @@ -174,6 +174,9 @@ public ListThreatActorResponse listThreatActors(String accountId, ListThreatActo match.append("detectedAt", new Document("$gte", request.getStartTs()).append("$lte", request.getEndTs())); } + // Exclude IGNORED status events + match.append("status", new Document("$ne", "IGNORED")); + List pipeline = new ArrayList<>(); if (!match.isEmpty()) pipeline.add(new Document("$match", match)); @@ -251,7 +254,7 @@ public ListThreatActorResponse listThreatActors(String accountId, ListThreatActo return ListThreatActorResponse.newBuilder().addAllActors(actors).setTotal(total).build(); } - public DailyActorsCountResponse getDailyActorCounts(String accountId, long startTs, long endTs, List latestAttackList) { + public DailyActorsCountResponse getDailyActorCounts(String accountId, long startTs, long endTs, List latestAttackList, Boolean successfulExploit, String status) { if(latestAttackList == null || latestAttackList.isEmpty()) { return DailyActorsCountResponse.newBuilder().build(); @@ -271,6 +274,17 @@ public DailyActorsCountResponse getDailyActorCounts(String accountId, long start if (startTs > 0) { matchConditions.get("detectedAt", Document.class).append("$gte", startTs); } + + // Filter by status if specified + if (status != null && !status.isEmpty()) { + matchConditions.append("status", status); + } + + // Filter by successfulExploit if specified + if (successfulExploit != null) { + matchConditions.append("successfulExploit", successfulExploit); + } + pipeline.add(new Document("$match", matchConditions)); pipeline.add(new Document("$project", @@ -360,13 +374,13 @@ public DailyActorsCountResponse getDailyActorCounts(String accountId, long start try (MongoCursor cursor = maliciousEventDao.aggregateRaw(accountId, statusPipeline).cursor()) { while (cursor.hasNext()) { Document d = cursor.next(); - String status = d.getString("_id"); + String statusValue = d.getString("_id"); int c = d.getInteger("count", 0); - if ("ACTIVE".equalsIgnoreCase(status)) { + if ("ACTIVE".equalsIgnoreCase(statusValue)) { totalActive = c; - } else if (ThreatDetectionConstants.IGNORED.equalsIgnoreCase(status)) { + } else if (ThreatDetectionConstants.IGNORED.equalsIgnoreCase(statusValue)) { totalIgnored = c; - } else if ("UNDER_REVIEW".equalsIgnoreCase(status)) { + } else if ("UNDER_REVIEW".equalsIgnoreCase(statusValue)) { totalUnderReview = c; } } @@ -383,7 +397,7 @@ public DailyActorsCountResponse getDailyActorCounts(String accountId, long start .build(); } - public ThreatActivityTimelineResponse getThreatActivityTimeline(String accountId, long startTs, long endTs, List latestAttackList) { + public ThreatActivityTimelineResponse getThreatActivityTimeline(String accountId, long startTs, long endTs, List latestAttackList, Boolean successfulExploit, String status) { if(latestAttackList == null || latestAttackList.isEmpty()) { return ThreatActivityTimelineResponse.newBuilder().build(); @@ -403,6 +417,16 @@ public ThreatActivityTimelineResponse getThreatActivityTimeline(String accountId // Stage 1: Match documents within the startTs and endTs range match.append("detectedAt", new Document("$gte", startTs).append("$lte", endTs)); + + // Filter by status if specified + if (status != null && !status.isEmpty()) { + match.append("status", status); + } + + // Filter by successfulExploit if specified + if (successfulExploit != null) { + match.append("successfulExploit", successfulExploit); + } List pipeline = Arrays.asList( new Document("$match", match), @@ -547,6 +571,16 @@ public ThreatActorByCountryResponse getThreatActorByCountry( .append("$lte", request.getEndTs())); } + // Filter by status if specified + if (request.hasStatus() && !request.getStatus().isEmpty()) { + match.append("status", request.getStatus()); + } + + // Filter by successfulExploit if specified + if (request.hasSuccessfulExploit()) { + match.append("successfulExploit", request.getSuccessfulExploit()); + } + pipeline.add(new Document("$match", match)); // 2. Project only necessary fields @@ -644,7 +678,7 @@ public ModifyThreatActorStatusResponse modifyThreatActorStatus( } public FetchTopNDataResponse fetchTopNData( - String accountId, long startTs, long endTs, List latestAttackList, int limit) { + String accountId, long startTs, long endTs, List latestAttackList, int limit, Boolean successfulExploit, String status) { List pipeline = new ArrayList<>(); @@ -661,6 +695,17 @@ public FetchTopNDataResponse fetchTopNData( if (endTs > 0) tsRange.append("$lte", endTs); match.append("detectedAt", tsRange); } + + // Filter by status if specified + if (status != null && !status.isEmpty()) { + match.append("status", status); + } + + // Filter by successfulExploit if specified + if (successfulExploit != null) { + match.append("successfulExploit", successfulExploit); + } + if (!match.isEmpty()) { pipeline.add(new Document("$match", match)); } diff --git a/apps/threat-detection-backend/src/main/java/com/akto/threat/backend/service/ThreatApiService.java b/apps/threat-detection-backend/src/main/java/com/akto/threat/backend/service/ThreatApiService.java index 8cd0904f53..abd376c624 100644 --- a/apps/threat-detection-backend/src/main/java/com/akto/threat/backend/service/ThreatApiService.java +++ b/apps/threat-detection-backend/src/main/java/com/akto/threat/backend/service/ThreatApiService.java @@ -62,6 +62,9 @@ public ListThreatApiResponse listThreatApis(String accountId, ListThreatApiReque match.append("detectedAt", new Document("$gte", start).append("$lte", end)); } + // Exclude IGNORED status events + match.append("status", new Document("$ne", "IGNORED")); + if (!match.isEmpty()) { base.add(new Document("$match", match)); } @@ -145,6 +148,16 @@ public ThreatCategoryWiseCountResponse getSubCategoryWiseCount( match.append("detectedAt", new Document("$gte", req.getStartTs()).append("$lte", req.getEndTs())); } + // Filter by status if specified + if (req.hasStatus() && !req.getStatus().isEmpty()) { + match.append("status", req.getStatus()); + } + + // Filter by successfulExploit if specified + if (req.hasSuccessfulExploit()) { + match.append("successfulExploit", req.getSuccessfulExploit()); + } + pipeline.add(new Document("$match", match)); // 3. Group by category and subCategory @@ -195,12 +208,23 @@ public ThreatSeverityWiseCountResponse getSeverityWiseCount( String[] severities = { "CRITICAL", "HIGH", "MEDIUM", "LOW" }; for (String severity : severities) { - Bson filter = Filters.and( - Filters.eq("severity", severity), - Filters.gte("detectedAt", req.getStartTs()), - Filters.lte("detectedAt", req.getEndTs()), - Filters.in("filterId", req.getLatestAttackList()) - ); + List filters = new ArrayList<>(); + filters.add(Filters.eq("severity", severity)); + filters.add(Filters.gte("detectedAt", req.getStartTs())); + filters.add(Filters.lte("detectedAt", req.getEndTs())); + filters.add(Filters.in("filterId", req.getLatestAttackList())); + + // Filter by status if specified + if (req.hasStatus() && !req.getStatus().isEmpty()) { + filters.add(Filters.eq("status", req.getStatus())); + } + + // Filter by successfulExploit if specified + if (req.hasSuccessfulExploit()) { + filters.add(Filters.eq("successfulExploit", req.getSuccessfulExploit())); + } + + Bson filter = Filters.and(filters); long count = maliciousEventDao.countDocuments(accountId, filter); diff --git a/protobuf/threat_detection/service/dashboard_service/v1/service.proto b/protobuf/threat_detection/service/dashboard_service/v1/service.proto index bbcda94a74..a9d5baae63 100644 --- a/protobuf/threat_detection/service/dashboard_service/v1/service.proto +++ b/protobuf/threat_detection/service/dashboard_service/v1/service.proto @@ -162,6 +162,8 @@ message ThreatActorByCountryRequest { uint32 start_ts = 1; uint32 end_ts = 2; repeated string latest_attack = 3; + optional bool successful_exploit = 4; + optional string status = 5; // Filter by status: "ACTIVE", "UNDER_REVIEW", or "IGNORED" } message ThreatActorByCountryResponse { @@ -177,6 +179,8 @@ message ThreatCategoryWiseCountRequest { uint32 start_ts = 1; uint32 end_ts = 2; repeated string latest_attack = 3; + optional bool successful_exploit = 4; + optional string status = 5; // Filter by status: "ACTIVE", "UNDER_REVIEW", or "IGNORED" } message ThreatActorFilterRequest { @@ -203,6 +207,8 @@ message ThreatSeverityWiseCountRequest { uint32 start_ts = 1; uint32 end_ts = 2; repeated string latest_attack = 3; + optional bool successful_exploit = 4; + optional string status = 5; // Filter by status: "ACTIVE", "UNDER_REVIEW", or "IGNORED" } message ThreatSeverityWiseCountResponse { @@ -218,6 +224,8 @@ message DailyActorsCountRequest { uint32 start_ts = 1; uint32 end_ts = 2; repeated string latest_attack = 3; + optional bool successful_exploit = 4; + optional string status = 5; // Filter by status: "ACTIVE", "UNDER_REVIEW", or "IGNORED" } message DailyActorsCountResponse { @@ -240,6 +248,8 @@ message ThreatActivityTimelineRequest { uint32 start_ts = 1; uint32 end_ts = 2; repeated string latest_attack = 3; + optional bool successful_exploit = 4; + optional string status = 5; // Filter by status: "ACTIVE", "UNDER_REVIEW", or "IGNORED" } message ThreatActivityTimelineResponse { @@ -382,6 +392,8 @@ message FetchTopNDataRequest { uint32 end_ts = 2; repeated string latest_attack = 3; uint32 limit = 4; + optional bool successful_exploit = 5; + optional string status = 6; // Filter by status: "ACTIVE", "UNDER_REVIEW", or "IGNORED" } message FetchTopNDataResponse { From f8484b217b8ba6503a10d9d09c5e672d0f690442 Mon Sep 17 00:00:00 2001 From: gauravmann Date: Mon, 3 Nov 2025 09:02:19 +0530 Subject: [PATCH 2/3] remove not required changes --- .../apps/dashboard/pages/threat_detection/ThreatActorPage.jsx | 4 +--- .../apps/dashboard/pages/threat_detection/ThreatApiPage.jsx | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatActorPage.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatActorPage.jsx index 3ff773ab34..c5b42b9607 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatActorPage.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatActorPage.jsx @@ -10,7 +10,7 @@ import { ActorDetails } from "./components/ActorDetails"; import ThreatWorldMap from "./components/ThreatWorldMap"; // import ThreatApiSubcategoryCount from "./components/ThreatApiSubcategoryCount"; -import { HorizontalGrid, VerticalStack, HorizontalStack, Checkbox, Select } from "@shopify/polaris"; +import { HorizontalGrid, VerticalStack} from "@shopify/polaris"; import { ThreatSummary } from "./components/ThreatSummary"; import ThreatActivityTimeline from "./components/ThreatActivityTimeline"; import React from "react"; @@ -82,7 +82,6 @@ function ThreatActorPage() { title={} isFirstPage={true} primaryAction={ - @@ -94,7 +93,6 @@ function ThreatActorPage() { }) } /> - } components={components} /> diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatApiPage.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatApiPage.jsx index 2232dbb926..ffd1c0626e 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatApiPage.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatApiPage.jsx @@ -10,7 +10,7 @@ import TopThreatTypeChart from "./components/TopThreatTypeChart"; import ThreatApiSubcategoryCount from "./components/ThreatApiSubcategoryCount"; import api from "./api"; -import { HorizontalGrid, HorizontalStack, Checkbox, Select } from "@shopify/polaris"; +import { HorizontalGrid } from "@shopify/polaris"; import threatDetectionFunc from "./transform"; import { getDashboardCategory, mapLabel } from "../../../main/labelHelper"; function ThreatApiPage() { @@ -60,7 +60,6 @@ function ThreatApiPage() { title={} isFirstPage={true} primaryAction={ - @@ -72,7 +71,6 @@ function ThreatApiPage() { }) } /> - } components={components} /> From c7b7820b3e88380da7b4707cc318fe6802c8c389 Mon Sep 17 00:00:00 2001 From: gauravmann Date: Mon, 3 Nov 2025 09:04:26 +0530 Subject: [PATCH 3/3] remove spacing --- .../threat_detection/ThreatActorPage.jsx | 24 +++++++++---------- .../pages/threat_detection/ThreatApiPage.jsx | 22 ++++++++--------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatActorPage.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatActorPage.jsx index c5b42b9607..67d056dd53 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatActorPage.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatActorPage.jsx @@ -10,7 +10,7 @@ import { ActorDetails } from "./components/ActorDetails"; import ThreatWorldMap from "./components/ThreatWorldMap"; // import ThreatApiSubcategoryCount from "./components/ThreatApiSubcategoryCount"; -import { HorizontalGrid, VerticalStack} from "@shopify/polaris"; +import { HorizontalGrid, VerticalStack } from "@shopify/polaris"; import { ThreatSummary } from "./components/ThreatSummary"; import ThreatActivityTimeline from "./components/ThreatActivityTimeline"; import React from "react"; @@ -82,17 +82,17 @@ function ThreatActorPage() { title={} isFirstPage={true} primaryAction={ - - dispatchCurrDateRange({ - type: "update", - period: dateObj.period, - title: dateObj.title, - alias: dateObj.alias, - }) - } - /> + + dispatchCurrDateRange({ + type: "update", + period: dateObj.period, + title: dateObj.title, + alias: dateObj.alias, + }) + } + /> } components={components} /> diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatApiPage.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatApiPage.jsx index ffd1c0626e..98807fcf94 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatApiPage.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatApiPage.jsx @@ -60,17 +60,17 @@ function ThreatApiPage() { title={} isFirstPage={true} primaryAction={ - - dispatchCurrDateRange({ - type: "update", - period: dateObj.period, - title: dateObj.title, - alias: dateObj.alias, - }) - } - /> + + dispatchCurrDateRange({ + type: "update", + period: dateObj.period, + title: dateObj.title, + alias: dateObj.alias, + }) + } + /> } components={components} />