-
- Quick Start,
- icon: AppsFilledMajor,
- onClick: ()=>{
- handleSelect("dashboard_quick_start")
- setActive("normal")
- navigate("/dashboard/quick-start")
- },
- selected: leftNavSelected === 'dashboard_quick_start',
- key: '1',
- },
- {
- label: 'API Security Posture',
- icon: ReportFilledMinor,
- onClick: ()=>{
- handleSelect("dashboard_home")
- navigate("/dashboard/home")
- setActive("normal")
- },
- selected: leftNavSelected === 'dashboard_home',
- key: '2',
- },
- {
- url: '#',
- label: API Discovery,
- icon: InventoryFilledMajor,
- onClick: ()=>{
- handleSelect("dashboard_observe_inventory")
- navigate('/dashboard/observe/inventory')
- setActive("normal")
- },
- selected: leftNavSelected.includes('_observe'),
- subNavigationItems:[
- {
- label: 'API Collections',
- onClick: ()=>{
- navigate('/dashboard/observe/inventory')
- handleSelect("dashboard_observe_inventory")
- setActive('active')
- },
- selected: leftNavSelected === "dashboard_observe_inventory"
- },
- {
- label: 'API Changes',
- onClick: ()=>{
- navigate('/dashboard/observe/changes')
- handleSelect("dashboard_observe_changes")
- setActive('active')
- },
- selected: leftNavSelected === "dashboard_observe_changes"
- },
- {
- label: 'Sensitive Data',
- onClick: ()=>{
- navigate('/dashboard/observe/sensitive')
- handleSelect("dashboard_observe_sensitive")
- setActive('active')
- },
- selected: leftNavSelected === "dashboard_observe_sensitive"
- }
- ],
- key: '3',
- },
- {
- url: '#',
- label: Testing,
- icon: MarketingFilledMinor,
- onClick: ()=>{
- navigate('/dashboard/testing/')
- handleSelect('dashboard_testing')
- setActive("normal")
- },
- selected: leftNavSelected.includes('_testing'),
- subNavigationItems:[
- {
- label: 'Results',
- onClick: ()=>{
- navigate('/dashboard/testing/')
- handleSelect('dashboard_testing')
- setActive('active')
- },
- selected: leftNavSelected === 'dashboard_testing'
- },
- {
- label: 'Test Roles',
- onClick: ()=>{
- navigate('/dashboard/testing/roles')
- handleSelect('dashboard_testing_roles')
- setActive('active')
- },
- selected: leftNavSelected === 'dashboard_testing_roles'
- },
- {
- label: 'User Config',
- onClick: ()=>{
- navigate('/dashboard/testing/user-config')
- handleSelect('dashboard_testing_user_config')
- setActive('active')
- },
- selected: leftNavSelected === 'dashboard_testing_user_config'
- }
- ],
- key: '4',
- },
- {
- label: Test Editor,
- icon: FileFilledMinor,
- onClick: ()=>{
- handleSelect("dashboard_test_editor")
- navigate("/dashboard/test-editor/REMOVE_TOKENS")
- setActive("normal")
- },
- selected: leftNavSelected.includes("dashboard_test_editor"),
- key: '5',
- },
- {
- label: Issues,
- icon: AnalyticsFilledMinor,
- onClick: ()=>{
- handleSelect("dashboard_issues")
- navigate("/dashboard/issues")
- setActive("normal")
- },
- selected: leftNavSelected === 'dashboard_issues',
- key: '6',
- },
- window?.STIGG_FEATURE_WISE_ALLOWED?.THREAT_DETECTION?.isGranted ?
- {
- label: API Runtime Threats,
- icon: DiamondAlertMinor,
- onClick: () => {
- handleSelect("dashboard_threat_detection")
- navigate("/dashboard/threat-detection")
- setActive("normal")
- },
- selected: leftNavSelected === 'dashboard_threat_detection',
- key: '7',
- } : {}
- ]}
- />
- Settings,
- icon: SettingsFilledMinor,
- onClick: ()=>{
- navigate("/dashboard/settings/about")
- setActive("normal")
- },
- selected: currPathString === 'settings',
- key: '7',
- }
- ]}
- />
-
+
+
+
+ Quick Start
+
+ ),
+ icon: AppsFilledMajor,
+ onClick: () => {
+ handleSelect("dashboard_quick_start");
+ setActive("normal");
+ navigate("/dashboard/quick-start");
+ },
+ selected: leftNavSelected === "dashboard_quick_start",
+ key: "1",
+ },
+ {
+ label: "API Security Posture",
+ icon: ReportFilledMinor,
+ onClick: () => {
+ handleSelect("dashboard_home");
+ navigate("/dashboard/home");
+ setActive("normal");
+ },
+ selected: leftNavSelected === "dashboard_home",
+ key: "2",
+ },
+ {
+ url: "#",
+ label: (
+
+ API Discovery
+
+ ),
+ icon: InventoryFilledMajor,
+ onClick: () => {
+ handleSelect("dashboard_observe_inventory");
+ navigate("/dashboard/observe/inventory");
+ setActive("normal");
+ },
+ selected: leftNavSelected.includes("_observe"),
+ subNavigationItems: [
+ {
+ label: "API Collections",
+ onClick: () => {
+ navigate("/dashboard/observe/inventory");
+ handleSelect("dashboard_observe_inventory");
+ setActive("active");
+ },
+ selected: leftNavSelected === "dashboard_observe_inventory",
+ },
+ {
+ label: "API Changes",
+ onClick: () => {
+ navigate("/dashboard/observe/changes");
+ handleSelect("dashboard_observe_changes");
+ setActive("active");
+ },
+ selected: leftNavSelected === "dashboard_observe_changes",
+ },
+ {
+ label: "Sensitive Data",
+ onClick: () => {
+ navigate("/dashboard/observe/sensitive");
+ handleSelect("dashboard_observe_sensitive");
+ setActive("active");
+ },
+ selected: leftNavSelected === "dashboard_observe_sensitive",
+ },
+ ],
+ key: "3",
+ },
+ {
+ url: "#",
+ label: (
+
+ Testing
+
+ ),
+ icon: MarketingFilledMinor,
+ onClick: () => {
+ navigate("/dashboard/testing/");
+ handleSelect("dashboard_testing");
+ setActive("normal");
+ },
+ selected: leftNavSelected.includes("_testing"),
+ subNavigationItems: [
+ {
+ label: "Results",
+ onClick: () => {
+ navigate("/dashboard/testing/");
+ handleSelect("dashboard_testing");
+ setActive("active");
+ },
+ selected: leftNavSelected === "dashboard_testing",
+ },
+ {
+ label: "Test Roles",
+ onClick: () => {
+ navigate("/dashboard/testing/roles");
+ handleSelect("dashboard_testing_roles");
+ setActive("active");
+ },
+ selected: leftNavSelected === "dashboard_testing_roles",
+ },
+ {
+ label: "User Config",
+ onClick: () => {
+ navigate("/dashboard/testing/user-config");
+ handleSelect("dashboard_testing_user_config");
+ setActive("active");
+ },
+ selected: leftNavSelected === "dashboard_testing_user_config",
+ },
+ ],
+ key: "4",
+ },
+ {
+ label: (
+
+ Test Editor
+
+ ),
+ icon: FileFilledMinor,
+ onClick: () => {
+ handleSelect("dashboard_test_editor");
+ navigate("/dashboard/test-editor/REMOVE_TOKENS");
+ setActive("normal");
+ },
+ selected: leftNavSelected.includes("dashboard_test_editor"),
+ key: "5",
+ },
+ {
+ label: (
+
+ Issues
+
+ ),
+ icon: AnalyticsFilledMinor,
+ onClick: () => {
+ handleSelect("dashboard_issues");
+ navigate("/dashboard/issues");
+ setActive("normal");
+ },
+ selected: leftNavSelected === "dashboard_issues",
+ key: "6",
+ },
+ window?.STIGG_FEATURE_WISE_ALLOWED?.THREAT_DETECTION?.isGranted
+ ? {
+ label: (
+
+ API Protection
+
+ ),
+ icon: DiamondAlertMinor,
+ onClick: () => {
+ handleSelect("dashboard_threat_activity");
+ navigate("/dashboard/protection/threat-activity");
+ setActive("normal");
+ },
+ selected: leftNavSelected.includes("_threat"),
+ url: "#",
+ key: "7",
+ subNavigationItems: [
+ {
+ label: "Threat Activity",
+ onClick: () => {
+ navigate("/dashboard/protection/threat-activity");
+ handleSelect("dashboard_threat_activity");
+ setActive("active");
+ },
+ selected:
+ leftNavSelected === "dashboard_threat_activity",
+ },
+ {
+ label: "Threat Actors",
+ onClick: () => {
+ navigate("/dashboard/protection/threat-actor");
+ handleSelect("dashboard_threat_actor");
+ setActive("active");
+ },
+ selected: leftNavSelected === "dashboard_threat_actor",
+ },
+ {
+ label: "APIs Under Threat",
+ onClick: () => {
+ navigate("/dashboard/protection/threat-api");
+ handleSelect("dashboard_threat_api");
+ setActive("active");
+ },
+ selected:
+ leftNavSelected === "dashboard_threat_api",
+ },
+ {
+ label: "Threat Policy",
+ onClick: () => {
+ navigate("/dashboard/protection/threat-policy");
+ handleSelect("dashboard_threat_policy");
+ setActive("active");
+ },
+ selected:
+ leftNavSelected === "dashboard_threat_policy",
+ },
+ ],
+ }
+ : {},
+ ]}
+ />
+
+ Settings
+
+ ),
+ icon: SettingsFilledMinor,
+ onClick: () => {
+ navigate("/dashboard/settings/about");
+ setActive("normal");
+ },
+ selected: currPathString === "settings",
+ key: "7",
+ },
+ ]}
+ />
+
- );
+ );
- return(
- navigationMarkup
- )
+ return navigationMarkup;
}
-
-
diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/GetPrettifyEndpoint.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/GetPrettifyEndpoint.jsx
index 28d627aa74..f52f0574cd 100644
--- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/GetPrettifyEndpoint.jsx
+++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/GetPrettifyEndpoint.jsx
@@ -5,38 +5,80 @@ import transform from '../onboarding/transform'
import observeFunc from "./transform"
function GetPrettifyEndpoint({method,url, isNew, maxWidth}){
const ref = useRef(null)
+ const localUrl = url || "/"
const [copyActive, setCopyActive] = useState(false)
- return(
-
setCopyActive(true)} onMouseLeave={() => setCopyActive(false)}>
-
-
- {method}
-
-
-
-
-
-
- {observeFunc.getTruncatedUrl(url)}
-
- {copyActive ?
-
{e.stopPropagation();func.copyToClipboard(method + " " + url, ref, "URL copied");}}>
-
-
-
-
-
- :null}
-
-
- {isNew ? New : null}
-
+ return (
+
setCopyActive(true)}
+ onMouseLeave={() => setCopyActive(false)}
+ >
+
+
+
+ {method}
+
+
+
+
+
+
+
+
+ {observeFunc.getTruncatedUrl(localUrl)}
+
+
+ {copyActive ? (
+
{
+ e.stopPropagation();
+ func.copyToClipboard(
+ method + " " + localUrl,
+ ref,
+ "URL copied"
+ );
+ }}
+ >
+
+
+
+
-
-
- )
+ ) : null}
+
+ {isNew ? New : null}
+
+
+
+ );
}
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 d762e307e7..d33f9594a7 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
@@ -33,19 +33,15 @@ function ThreatDetectionPage() {
const components = [
- horizontalComponent,
,
-
]
return
}
diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatPolicyPage.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatPolicyPage.jsx
new file mode 100644
index 0000000000..0b9db6ddd1
--- /dev/null
+++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatPolicyPage.jsx
@@ -0,0 +1,45 @@
+import {useReducer, useState} from "react";
+import DateRangeFilter from "../../components/layouts/DateRangeFilter";
+import PageWithMultipleCards from "../../components/layouts/PageWithMultipleCards";
+import TitleWithInfo from "../../components/shared/TitleWithInfo";
+import FilterComponent from "./components/FilterComponent";
+import SusDataTable from "./components/SusDataTable";
+import values from "@/util/values";
+import {produce} from "immer"
+import func from "@/util/func";
+import transform from "../observe/transform";
+import {HorizontalGrid} from "@shopify/polaris";
+import SampleDetails from "./components/SampleDetails";
+
+function ThreatPolicyPage() {
+
+ const initialVal = values.ranges[3]
+ const [currDateRange, dispatchCurrDateRange] = useReducer(produce((draft, action) => func.dateRangeReducer(draft, action)), initialVal);
+
+ const horizontalComponent =
+
+
+
+ const components = [
+ horizontalComponent,
+ ]
+
+ return
+ }
+ isFirstPage={true}
+ primaryAction={ dispatchCurrDateRange({
+ type: "update",
+ period: dateObj.period,
+ title: dateObj.title,
+ alias: dateObj.alias
+ })}/>}
+ components={components}
+ />
+}
+
+export default ThreatPolicyPage;
\ No newline at end of file
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 61379cdc98..bdf879d7bc 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
@@ -38,21 +38,23 @@ const threatDetectionRequests = {
data: {}
})
},
- fetchThreatActors(skip) {
+ fetchThreatActors(skip, sort) {
return request({
url: '/api/fetchThreatActors',
method: 'post',
data: {
- skip: skip
+ skip: skip,
+ sort: sort
}
})
},
- fetchThreatApis(skip) {
+ fetchThreatApis(skip, sort) {
return request({
url: '/api/fetchThreatApis',
method: 'post',
data: {
- skip: skip
+ skip: skip,
+ sort: sort
}
})
},
diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/SusDataTable.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/SusDataTable.jsx
index 4af459eab5..8b1755786f 100644
--- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/SusDataTable.jsx
+++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/SusDataTable.jsx
@@ -46,26 +46,22 @@ const headers = [
title: "Source IP",
value: "sourceIPComponent",
},
- {
- title: "",
- type: CellType.ACTION,
- },
];
const sortOptions = [
{
label: "Discovered time",
- value: "discovered asc",
+ value: "detectedAt asc",
directionLabel: "Newest",
- sortKey: "discovered",
- columnIndex: 3,
+ sortKey: "detectedAt",
+ columnIndex: 5,
},
{
label: "Discovered time",
- value: "discovered desc",
+ value: "detectedAt desc",
directionLabel: "Oldest",
- sortKey: "discovered",
- columnIndex: 3,
+ sortKey: "detectedAt",
+ columnIndex: 5,
},
];
@@ -118,6 +114,7 @@ function SusDataTable({ currDateRange, rowClicked }) {
let ret = res?.maliciousEvents.map((x) => {
return {
...x,
+ id: x.id,
actorComp: x?.actor,
endpointComp: (
@@ -152,12 +149,6 @@ function SusDataTable({ currDateRange, rowClicked }) {
});
filters = [
- {
- key: "apiCollectionId",
- label: "Collection",
- title: "Collection",
- choices: apiCollectionFilterChoices,
- },
{
key: "sourceIps",
label: "Source IP",
@@ -186,24 +177,6 @@ function SusDataTable({ currDateRange, rowClicked }) {
}
}
- const getActions = (item) => {
- return [
- {
- items: [
- {
- content: "View in collection",
- onAction: () => {
- window.open(
- `/dashboard/observe/inventory/${item.apiCollectionId}`,
- "_blank"
- );
- },
- },
- ],
- },
- ];
- };
-
const key = startTimestamp + endTimestamp;
return (
rowClicked(data)} [For now removing on row click functionality]
fetchData={fetchData}
filters={filters}
selectable={false}
hasRowActions={true}
- getActions={getActions}
+ getActions={() => []}
hideQueryField={true}
headings={headers}
useNewRow={true}
diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/ThreatActorsTable.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/ThreatActorsTable.jsx
index a4ff602489..ff3ca78ae5 100644
--- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/ThreatActorsTable.jsx
+++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/ThreatActorsTable.jsx
@@ -35,7 +35,22 @@ const headers = [
},
];
-const sortOptions = [];
+const sortOptions = [
+ {
+ label: "Discovered time",
+ value: "discoveredAt asc",
+ directionLabel: "Newest",
+ sortKey: "discoveredAt",
+ columnIndex: 4,
+ },
+ {
+ label: "Discovered time",
+ value: "discoveredAt desc",
+ directionLabel: "Oldest",
+ sortKey: "discoveredAt",
+ columnIndex: 4,
+ },
+];
let filters = [];
@@ -55,7 +70,7 @@ function ThreatActorTable({ data, currDateRange, rowClicked }) {
async function fetchData(sortKey, sortOrder, skip) {
setLoading(true);
const sort = { [sortKey]: sortOrder };
- const res = await api.fetchThreatActors(skip);
+ const res = await api.fetchThreatActors(skip, sort);
let total = res.total;
let ret = res?.actors?.map((x) => {
return {
diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/ThreatApisTable.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/ThreatApisTable.jsx
index 0e7ae16d47..04cb5eb90c 100644
--- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/ThreatApisTable.jsx
+++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/ThreatApisTable.jsx
@@ -36,7 +36,22 @@ const headers = [
},
];
-const sortOptions = [];
+const sortOptions = [
+ {
+ label: "Discovered time",
+ value: "discoveredAt asc",
+ directionLabel: "Newest",
+ sortKey: "discoveredAt",
+ columnIndex: 4,
+ },
+ {
+ label: "Discovered time",
+ value: "discoveredAt desc",
+ directionLabel: "Oldest",
+ sortKey: "discoveredAt",
+ columnIndex: 4,
+ },
+];
let filters = [];
@@ -60,7 +75,7 @@ function ThreatApiTable({ currDateRange, rowClicked }) {
async function fetchData(sortKey, sortOrder, skip) {
setLoading(true);
const sort = { [sortKey]: sortOrder };
- const res = await api.fetchThreatApis(skip);
+ const res = await api.fetchThreatApis(skip, sort);
let total = res.total;
let ret = res?.apis?.map((x) => {
return {
diff --git a/apps/dashboard/web/polaris_web/web/src/apps/main/App.js b/apps/dashboard/web/polaris_web/web/src/apps/main/App.js
index 42bad35beb..c533d47eb4 100644
--- a/apps/dashboard/web/polaris_web/web/src/apps/main/App.js
+++ b/apps/dashboard/web/polaris_web/web/src/apps/main/App.js
@@ -4,15 +4,15 @@ import SingleTestRunPage from "../dashboard/pages/testing/SingleTestRunPage/Sing
import AllSensitiveData from "../dashboard/pages/observe/AllSensitiveData/AllSensitiveData";
import ApiCollections from "../dashboard/pages/observe/api_collections/ApiCollections";
import ApiQuery from "../dashboard/pages/observe/api_collections/APIQuery";
-import ApiEndpoints from "../dashboard/pages/observe/api_collections/ApiEndpoints";
+import ApiEndpoints from "../dashboard/pages/observe/api_collections/ApiEndpoints";
import SensitiveDataExposure from "../dashboard/pages/observe/SensitiveDataExposure/SensitiveDataExposure";
import SingleRequest from "../dashboard/pages/observe/SingleRequest/SingleRequest";
import PageObserve from "../dashboard/pages/observe/PageObserve"
import PageTesting from "../dashboard/pages/testing/PageTesting";
import {
- createBrowserRouter,
- RouterProvider,
- Navigate,
+ createBrowserRouter,
+ RouterProvider,
+ Navigate,
} from "react-router-dom";
import BurpSuite from "../dashboard/pages/settings/integrations/BurpSuite";
import Integrations from "../dashboard/pages/settings/integrations/Integrations";
@@ -49,8 +49,8 @@ import Slack from "../dashboard/pages/settings/integrations/Slack";
import ApiChanges from "../dashboard/pages/observe/api_collections/ApiChanges";
import Store from "../dashboard/store";
-import { generateSearchData } from "@/util/searchItems"
-import { useEffect } from "react";
+import {generateSearchData} from "@/util/searchItems"
+import {useEffect} from "react";
import CICD from "../dashboard/pages/settings/integrations/CICD";
import ErrorComponent from "../dashboard/components/shared/ErrorComponent";
import OktaIntegration from "../dashboard/pages/settings/integrations/OktaIntegration";
@@ -58,18 +58,18 @@ import AzureSso from "../dashboard/pages/settings/integrations/sso/AzureSso";
import HomeDashboard from "../dashboard/pages/dashboard/HomeDashboard";
import TestLibrary from "../dashboard/pages/settings/test_library/TestLibrary";
-import { useStiggContext } from '@stigg/react-sdk';
+import {useStiggContext} from '@stigg/react-sdk';
import DependencyTable from "../dashboard/pages/testing/DependencyTable/DependencyTable";
import TestRoleAccessMatrix from "../dashboard/pages/testing/TestRoleAccessMatrix/TestRoleAccessMatrix";
import SignupPage from "../signup/pages/SignupPage";
import PageCheckInbox from "../signup/pages/PageCheckInbox"
import PageBusinessEmail from "../signup/pages/PageBusinessEmail"
import TokenValidator from "./TokenValidator"
-import { TableContextProvider } from "@/apps/dashboard/components/tables/TableContext";
+import {TableContextProvider} from "@/apps/dashboard/components/tables/TableContext";
import VulnerabilityReport from "../dashboard/pages/testing/vulnerability_report/VulnerabilityReport";
import ThreatDetectionPage from "../dashboard/pages/threat_detection/ThreatDetectionPage";
-import { PollingProvider } from "./PollingProvider";
+import {PollingProvider} from "./PollingProvider";
import Help from "../dashboard/pages/settings/help_and_support/Help";
import AdvancedTrafficFilters from "../dashboard/pages/settings/traffic-conditions/AdvancedTrafficFilters";
import GoogleSamlSso from "../dashboard/pages/settings/integrations/sso/GoogleSamlSso";
@@ -80,328 +80,338 @@ import TeamsWebhook from "../dashboard/pages/settings/integrations/teamsWebhooks
import AuditLogs from "../dashboard/pages/settings/audit_logs/AuditLogs";
import ThreatApiPage from "../dashboard/pages/threat_detection/ThreatApiPage";
import ThreatActorPage from "../dashboard/pages/threat_detection/ThreatActorPage";
+import ThreatPolicyPage from "../dashboard/pages/threat_detection/ThreatPolicyPage";
// if you add a component in a new path, please verify the search implementation in function -> 'getSearchItemsArr' in func.js
const router = createBrowserRouter([
- {
- path: "/dashboard",
- element: ,
- children: [
- {
- path: "",
- element: ,
+ {
+ path: "/dashboard",
+ element: ,
children: [
- {
- path: "home",
- element: ,
- },
- {
- path: "testing",
- element: ,
- children:[
- ...(["", "active", "cicd", "inactive"].map((i) => {
- return {
- path: i,
- element:
- }
- })),
- {
- path: ":hexId",
- element:
- },
- {
- path:"roles",
- element:
- },
- {
- path:"roles/details",
- element:
- },
- {
- path:"roles/access-matrix",
- element:
- },
- {
- path:"user-config",
- element:
- },
- {
- path:"dependency",
- element:
- }
- ]
- },
- {
- path: "observe",
- element: ,
- children: [
- {
- path: "sensitive",
- element:
- },
- {
- path: "inventory",
- element:
- },
- {
- path: "query_mode",
- element:
- },
- {
- path: "changes",
- element:
- },
- {
- path: "inventory/:apiCollectionId",
- element:
- },
- {
- path: "data-types",
- element:
- },
- {
- path: "sensitive/:subType",
- element:
- },
- {
- path: "sensitive/:subType/:apiCollectionId/:urlAndMethod",
- element:
- }
- ]
- },
- {
- path:"issues",
- element:
- },
- {
- path: "quick-start",
- element: ,
- },
- {
- path:"threat-detection",
- element:
- },
- {
- path: "threat-api",
- element:
- },
- {
- path: "threat-actor",
- element:
- }
- ]
- },
- {
- path: "settings",
- element: ,
- children: [
- {
- path: "users",
- element:
- },
- {
- path: "Help",
- element:
- },
- {
- path: "integrations",
- element: ,
- },
- {
- path: "about",
- element: ,
- },
- {
- path: "metrics",
- element: ,
- },
- {
- path: "integrations/burp",
- element: ,
- },
- {
- path: "integrations/ci-cd",
- element: ,
- },
- {
- path: "integrations/postman",
- element: ,
- },
- {
- path: "integrations/jira",
- element: ,
- },
- {
- path: "integrations/akto_apis",
- element: ,
- },
- {
- path: "integrations/akto_gpt",
- element: ,
- },
- {
- path: "integrations/github_sso",
- element:
- },
- {
- path: "integrations/okta_sso",
- element:
- },
- {
- path: "integrations/azure_sso",
- element:
- },
- {
- path: "integrations/google_workspace_sso",
- element:
- },
- {
- path: "integrations/github_app",
- element:
- },
- {
- path: "integrations/slack",
- element: ,
- },
- {
- path: "integrations/webhooks",
- element: ,
- },
- {
- path: "integrations/webhooks/:webhookId",
- element: ,
- },
- {
- path: "integrations/webhooks/create_custom_webhook",
- element: ,
- },
- {
- path: "integrations/teamsWebhooks",
- element: ,
- },
- {
- path: "integrations/teamsWebhooks/:webhookId",
- element: ,
- },
- {
- path: "integrations/teamsWebhooks/create_custom_webhook",
- element: ,
- },
+ {
+ path: "",
+ element: ,
+ children: [
+ {
+ path: "home",
+ element: ,
+ },
+ {
+ path: "testing",
+ element: ,
+ children: [
+ ...(["", "active", "cicd", "inactive"].map((i) => {
+ return {
+ path: i,
+ element:
+ }
+ })),
+ {
+ path: ":hexId",
+ element:
+ },
+ {
+ path: "roles",
+ element:
+ },
+ {
+ path: "roles/details",
+ element:
+ },
+ {
+ path: "roles/access-matrix",
+ element:
+ },
+ {
+ path: "user-config",
+ element:
+ },
+ {
+ path: "dependency",
+ element:
+ }
+ ]
+ },
+ {
+ path: "observe",
+ element: ,
+ children: [
+ {
+ path: "sensitive",
+ element:
+ },
+ {
+ path: "inventory",
+ element:
+ },
+ {
+ path: "query_mode",
+ element:
+ },
+ {
+ path: "changes",
+ element:
+ },
+ {
+ path: "inventory/:apiCollectionId",
+ element:
+ },
+ {
+ path: "data-types",
+ element:
+ },
+ {
+ path: "sensitive/:subType",
+ element:
+ },
+ {
+ path: "sensitive/:subType/:apiCollectionId/:urlAndMethod",
+ element:
+ }
+ ]
+ },
+ {
+ path: "issues",
+ element:
+ },
+ {
+ path: "protection",
+ children: [
+ {
+ path: "threat-activity",
+ element:
+ },
+ {
+ path: "threat-api",
+ element:
+ },
+ {
+ path: "threat-actor",
+ element:
+ },
+ {
+ path: "threat-policy",
+ element:
+ }
+ ]
+ },
+ {
+ path: "quick-start",
+ element: ,
+ },
+ ]
+ },
+ {
+ path: "settings",
+ element: ,
+ children: [
+ {
+ path: "users",
+ element:
+ },
+ {
+ path: "Help",
+ element:
+ },
+ {
+ path: "integrations",
+ element: ,
+ },
+ {
+ path: "about",
+ element: ,
+ },
+ {
+ path: "metrics",
+ element: ,
+ },
+ {
+ path: "integrations/burp",
+ element: ,
+ },
+ {
+ path: "integrations/ci-cd",
+ element: ,
+ },
+ {
+ path: "integrations/postman",
+ element: ,
+ },
+ {
+ path: "integrations/jira",
+ element: ,
+ },
+ {
+ path: "integrations/akto_apis",
+ element: ,
+ },
+ {
+ path: "integrations/akto_gpt",
+ element: ,
+ },
+ {
+ path: "integrations/github_sso",
+ element:
+ },
+ {
+ path: "integrations/okta_sso",
+ element:
+ },
+ {
+ path: "integrations/azure_sso",
+ element:
+ },
+ {
+ path: "integrations/google_workspace_sso",
+ element:
+ },
+ {
+ path: "integrations/github_app",
+ element:
+ },
+ {
+ path: "integrations/slack",
+ element: ,
+ },
+ {
+ path: "integrations/webhooks",
+ element: ,
+ },
+ {
+ path: "integrations/webhooks/:webhookId",
+ element: ,
+ },
+ {
+ path: "integrations/webhooks/create_custom_webhook",
+ element: ,
+ },
+ {
+ path: "integrations/teamsWebhooks",
+ element: ,
+ },
+ {
+ path: "integrations/teamsWebhooks/:webhookId",
+ element: ,
+ },
+ {
+ path: "integrations/teamsWebhooks/create_custom_webhook",
+ element: ,
+ },
- {
- path: "logs",
- element: ,
- },
- {
- path: "auth-types",
- element:
- },
- {
- path: "default-payloads",
- element:
- },
- {
- path: 'advanced-filters',
- element:
- },
- {
- path: "auth-types/details",
- element:
- },
- {
- path: "tags",
- element:
- },
- {
- path: "tags/details",
- element:
- },
- {
- path: "test-library",
- element:
- },
- {
- path: "billing",
- element:
- },
- {
- path: "self-hosted",
- element:
- },
- {
- path: 'audit-logs',
- element:
- }
- ]
- },
- {
- path: "test-editor/:testId",
- element:
- },
- {
- path: "test-editor",
- element:
- },
- {
- path: "onboarding",
- element:
- },
- {
- path: "testing/summary/:reportId",
- element:
- },
- {
- path: "issues/summary/:reportId",
- element:
- }
- ],
- errorElement:
- },
- {
- path: "/login",
- element: ,
- },
- {
- path: "/",
- element: ,
- },
- {
- path: "/signup",
- element: ,
- },
- {
- path: "/check-inbox",
- element:
- },
- {
- path: "/business-email",
- element:
- },
- {
- path: "/sso-login",
- element:
- },
- // catches all undefined paths and redirects to homepage.
- {
- path: "*",
- element: ,
- },
+ {
+ path: "logs",
+ element: ,
+ },
+ {
+ path: "auth-types",
+ element:
+ },
+ {
+ path: "default-payloads",
+ element:
+ },
+ {
+ path: 'advanced-filters',
+ element:
+ },
+ {
+ path: "auth-types/details",
+ element:
+ },
+ {
+ path: "tags",
+ element:
+ },
+ {
+ path: "tags/details",
+ element:
+ },
+ {
+ path: "test-library",
+ element:
+ },
+ {
+ path: "billing",
+ element:
+ },
+ {
+ path: "self-hosted",
+ element:
+ },
+ {
+ path: 'audit-logs',
+ element:
+ }
+ ]
+ },
+ {
+ path: "test-editor/:testId",
+ element:
+ },
+ {
+ path: "test-editor",
+ element:
+ },
+ {
+ path: "onboarding",
+ element:
+ },
+ {
+ path: "testing/summary/:reportId",
+ element:
+ },
+ {
+ path: "issues/summary/:reportId",
+ element:
+ }
+ ],
+ errorElement:
+ },
+ {
+ path: "/login",
+ element: ,
+ },
+ {
+ path: "/",
+ element: ,
+ },
+ {
+ path: "/signup",
+ element: ,
+ },
+ {
+ path: "/check-inbox",
+ element:
+ },
+ {
+ path: "/business-email",
+ element:
+ },
+ {
+ path: "/sso-login",
+ element:
+ },
+ // catches all undefined paths and redirects to homepage.
+ {
+ path: "*",
+ element: ,
+ },
])
function App() {
- const setAllRoutes = Store(state => state.setAllRoutes)
- const searchData= generateSearchData(router.routes)
- const { stigg } = useStiggContext();
- useEffect(() => {
- stigg.setCustomerId(window.STIGG_CUSTOMER_ID, window.STIGG_CUSTOMER_TOKEN)
-
- })
+ const setAllRoutes = Store(state => state.setAllRoutes)
+ const searchData = generateSearchData(router.routes)
+ const {stigg} = useStiggContext();
+ useEffect(() => {
+ stigg.setCustomerId(window.STIGG_CUSTOMER_ID, window.STIGG_CUSTOMER_TOKEN)
+
+ })
- useEffect(() => {
- const script = document.createElement('script')
- const scriptText = document.createTextNode(`
+ useEffect(() => {
+ const script = document.createElement('script')
+ const scriptText = document.createTextNode(`
self.MonacoEnvironment = {
getWorkerUrl: function (moduleId, label) {
if (label === 'json') {
@@ -411,18 +421,18 @@ function App() {
}
};
`);
- setAllRoutes(searchData)
- script.appendChild(scriptText);
- document.body.appendChild(script)
- }, [])
+ setAllRoutes(searchData)
+ script.appendChild(scriptText);
+ document.body.appendChild(script)
+ }, [])
- return (
-
-
-
-
-
- );
+ return (
+
+
+
+
+
+ );
}
export default App;
\ No newline at end of file
diff --git a/apps/pom.xml b/apps/pom.xml
index 20dcc1cb26..d6c4886121 100644
--- a/apps/pom.xml
+++ b/apps/pom.xml
@@ -1,7 +1,8 @@
-
+
4.0.0
@@ -148,4 +149,4 @@
-
+
\ No newline at end of file
diff --git a/apps/api-threat-detection/.gitignore b/apps/threat-detection/.gitignore
similarity index 100%
rename from apps/api-threat-detection/.gitignore
rename to apps/threat-detection/.gitignore
diff --git a/apps/threat-detection/Dockerfile b/apps/threat-detection/Dockerfile
new file mode 100644
index 0000000000..41b66dbbac
--- /dev/null
+++ b/apps/threat-detection/Dockerfile
@@ -0,0 +1,4 @@
+FROM openjdk
+WORKDIR /app
+COPY ./target/threat-detection-1.0-SNAPSHOT-jar-with-dependencies.jar /app/threat-detection-1.0-SNAPSHOT-jar-with-dependencies.jar
+CMD "java" "-XX:+ExitOnOutOfMemoryError" "-jar" "/app/threat-detection-1.0-SNAPSHOT-jar-with-dependencies.jar"
\ No newline at end of file
diff --git a/apps/api-threat-detection/README.md b/apps/threat-detection/README.md
similarity index 100%
rename from apps/api-threat-detection/README.md
rename to apps/threat-detection/README.md
diff --git a/apps/threat-detection/pom.xml b/apps/threat-detection/pom.xml
new file mode 100644
index 0000000000..bd143c2dc5
--- /dev/null
+++ b/apps/threat-detection/pom.xml
@@ -0,0 +1,260 @@
+
+
+ 4.0.0
+
+
+ com.akto.apps
+ apps
+ ${revision}
+
+
+ com.akto.apps.threat-detection
+ threat-detection
+ jar
+
+
+
+ confluent
+ https://packages.confluent.io/maven/
+
+
+
+
+
+ org.apache.commons
+ commons-lang3
+ 3.12.0
+
+
+ com.akto.libs.dao
+ dao
+ ${project.version}
+
+
+ com.akto.libs.utils
+ utils
+ ${project.version}
+
+
+ com.akto.apps.mini-runtime
+ mini-runtime
+ ${project.version}
+
+
+ com.akto.apps.testing
+ testing
+ ${project.version}
+
+
+ org.apache.httpcomponents
+ httpclient
+ 4.5.13
+
+
+ org.jetbrains
+ annotations
+ RELEASE
+ compile
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ 5.4.2
+ test
+
+
+ org.apache.kafka
+ kafka-clients
+ 3.0.0
+
+
+ com.akto.libs.utils
+ utils
+ test-jar
+ ${project.version}
+ test
+
+
+ io.confluent
+ kafka-protobuf-serializer
+ 7.5.0
+
+
+
+ com.akto.libs.protobuf
+ protobuf
+ 1.0-SNAPSHOT
+ compile
+
+
+
+ io.lettuce
+ lettuce-core
+ 6.4.0.RELEASE
+
+
+
+ com.github.ben-manes.caffeine
+ caffeine
+ 2.9.3
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ 2.16.1
+ compile
+
+
+
+ org.postgresql
+ postgresql
+ 42.7.4
+
+
+
+ org.flywaydb
+ flyway-core
+ 9.22.3
+
+
+
+ org.hibernate
+ hibernate-core
+ 5.6.15.Final
+
+
+
+ org.apache.logging.log4j
+ log4j-core
+ 2.24.2
+
+
+
+ org.apache.logging.log4j
+ log4j-api
+ 2.24.2
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.1
+
+
+ 8
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+ 3.0.1
+
+
+ copy-dependencies
+ package
+
+ copy-dependencies
+
+
+
+
+
+
+ src/main/java
+ src/test/java
+
+
+ src/main/resources
+ false
+
+
+
+
+
+
+
+ normal
+
+ true
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+ package
+
+ single
+
+
+
+
+
+ com.akto.threat.detection.Main
+
+
+
+
+ jar-with-dependencies
+
+
+
+
+
+
+
+
+
+
+ devcontainer
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+ threat-detection-1.0-SNAPSHOT-jar-with-dependencies
+
+
+ true
+ com.akto.threat.detection.Main
+ dependency-jars/
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ copy-dependencies
+ package
+
+ copy-dependencies
+
+
+ ${project.build.directory}/dependency-jars/
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/threat-detection/src/main/java/com/akto/threat/detection/Main.java b/apps/threat-detection/src/main/java/com/akto/threat/detection/Main.java
new file mode 100644
index 0000000000..0ea77e9470
--- /dev/null
+++ b/apps/threat-detection/src/main/java/com/akto/threat/detection/Main.java
@@ -0,0 +1,87 @@
+package com.akto.threat.detection;
+
+import com.akto.DaoInit;
+import com.akto.kafka.KafkaConfig;
+import com.akto.kafka.KafkaConsumerConfig;
+import com.akto.kafka.KafkaProducerConfig;
+import com.akto.kafka.Serializer;
+import com.akto.threat.detection.constants.KafkaTopic;
+import com.akto.threat.detection.session_factory.SessionFactoryUtils;
+import com.akto.threat.detection.tasks.CleanupTask;
+import com.akto.threat.detection.tasks.FlushSampleDataTask;
+import com.akto.threat.detection.tasks.MaliciousTrafficDetectorTask;
+import com.akto.threat.detection.tasks.SendMaliciousEventsToBackend;
+import com.mongodb.ConnectionString;
+import io.lettuce.core.RedisClient;
+import org.flywaydb.core.Flyway;
+import org.hibernate.SessionFactory;
+
+public class Main {
+
+ private static final String CONSUMER_GROUP_ID = "akto.threat_detection";
+
+ public static void main(String[] args) {
+ runMigrations();
+
+ SessionFactory sessionFactory = SessionFactoryUtils.createFactory();
+
+ // TODO: Remove this before merging. Will be using cyborg for fetching templates
+ DaoInit.init(new ConnectionString(System.getenv("AKTO_MONGO_CONN")));
+ KafkaConfig trafficKafka =
+ KafkaConfig.newBuilder()
+ .setGroupId(CONSUMER_GROUP_ID)
+ .setBootstrapServers(System.getenv("AKTO_TRAFFIC_KAFKA_BOOTSTRAP_SERVER"))
+ .setConsumerConfig(
+ KafkaConsumerConfig.newBuilder()
+ .setMaxPollRecords(100)
+ .setPollDurationMilli(100)
+ .build())
+ .setProducerConfig(
+ KafkaProducerConfig.newBuilder().setBatchSize(100).setLingerMs(100).build())
+ .setKeySerializer(Serializer.STRING)
+ .setValueSerializer(Serializer.BYTE_ARRAY)
+ .build();
+
+ KafkaConfig internalKafka =
+ KafkaConfig.newBuilder()
+ .setGroupId(CONSUMER_GROUP_ID)
+ .setBootstrapServers(System.getenv("AKTO_INTERNAL_KAFKA_BOOTSTRAP_SERVER"))
+ .setConsumerConfig(
+ KafkaConsumerConfig.newBuilder()
+ .setMaxPollRecords(100)
+ .setPollDurationMilli(100)
+ .build())
+ .setProducerConfig(
+ KafkaProducerConfig.newBuilder().setBatchSize(100).setLingerMs(100).build())
+ .setKeySerializer(Serializer.STRING)
+ .setValueSerializer(Serializer.BYTE_ARRAY)
+ .build();
+
+ new MaliciousTrafficDetectorTask(trafficKafka, internalKafka, createRedisClient()).run();
+ new FlushSampleDataTask(
+ sessionFactory, internalKafka, KafkaTopic.ThreatDetection.MALICIOUS_EVENTS)
+ .run();
+ new SendMaliciousEventsToBackend(
+ sessionFactory, internalKafka, KafkaTopic.ThreatDetection.ALERTS)
+ .run();
+ new CleanupTask(sessionFactory).run();
+ }
+
+ public static RedisClient createRedisClient() {
+ return RedisClient.create(System.getenv("AKTO_THREAT_DETECTION_REDIS_URI"));
+ }
+
+ public static void runMigrations() {
+ String url = System.getenv("AKTO_THREAT_DETECTION_POSTGRES");
+ String user = System.getenv("AKTO_THREAT_DETECTION_POSTGRES_USER");
+ String password = System.getenv("AKTO_THREAT_DETECTION_POSTGRES_PASSWORD");
+ Flyway flyway =
+ Flyway.configure()
+ .dataSource(url, user, password)
+ .locations("classpath:db/migration")
+ .schemas("flyway")
+ .load();
+
+ flyway.migrate();
+ }
+}
diff --git a/apps/threat-detection/src/main/java/com/akto/threat/detection/actor/ActorGenerator.java b/apps/threat-detection/src/main/java/com/akto/threat/detection/actor/ActorGenerator.java
new file mode 100644
index 0000000000..9f27be49bc
--- /dev/null
+++ b/apps/threat-detection/src/main/java/com/akto/threat/detection/actor/ActorGenerator.java
@@ -0,0 +1,10 @@
+package com.akto.threat.detection.actor;
+
+import com.akto.dto.HttpResponseParams;
+
+import java.util.Optional;
+
+public interface ActorGenerator {
+
+ Optional generate(HttpResponseParams responseParams);
+}
diff --git a/apps/threat-detection/src/main/java/com/akto/threat/detection/actor/SourceIPActorGenerator.java b/apps/threat-detection/src/main/java/com/akto/threat/detection/actor/SourceIPActorGenerator.java
new file mode 100644
index 0000000000..ebe444a2f1
--- /dev/null
+++ b/apps/threat-detection/src/main/java/com/akto/threat/detection/actor/SourceIPActorGenerator.java
@@ -0,0 +1,24 @@
+package com.akto.threat.detection.actor;
+
+import com.akto.dto.HttpResponseParams;
+import com.akto.runtime.policies.ApiAccessTypePolicy;
+
+import java.util.List;
+import java.util.Optional;
+
+public class SourceIPActorGenerator implements ActorGenerator {
+
+ private SourceIPActorGenerator() {}
+
+ public static SourceIPActorGenerator instance = new SourceIPActorGenerator();
+
+ @Override
+ public Optional generate(HttpResponseParams responseParams) {
+ List sourceIPs = ApiAccessTypePolicy.getSourceIps(responseParams);
+ if (sourceIPs.isEmpty()) {
+ return Optional.of(responseParams.getSourceIP());
+ }
+
+ return Optional.of(sourceIPs.get(0));
+ }
+}
diff --git a/apps/threat-detection/src/main/java/com/akto/threat/detection/cache/CounterCache.java b/apps/threat-detection/src/main/java/com/akto/threat/detection/cache/CounterCache.java
new file mode 100644
index 0000000000..f1eaeffa90
--- /dev/null
+++ b/apps/threat-detection/src/main/java/com/akto/threat/detection/cache/CounterCache.java
@@ -0,0 +1,14 @@
+package com.akto.threat.detection.cache;
+
+public interface CounterCache {
+
+ void incrementBy(String key, long val);
+
+ void increment(String key);
+
+ long get(String key);
+
+ boolean exists(String key);
+
+ void clear(String key);
+}
diff --git a/apps/threat-detection/src/main/java/com/akto/threat/detection/cache/LongValueCodec.java b/apps/threat-detection/src/main/java/com/akto/threat/detection/cache/LongValueCodec.java
new file mode 100644
index 0000000000..54879be184
--- /dev/null
+++ b/apps/threat-detection/src/main/java/com/akto/threat/detection/cache/LongValueCodec.java
@@ -0,0 +1,32 @@
+package com.akto.threat.detection.cache;
+
+import io.lettuce.core.codec.RedisCodec;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+
+public class LongValueCodec implements RedisCodec {
+
+ @Override
+ public String decodeKey(ByteBuffer bytes) {
+ return StandardCharsets.UTF_8.decode(bytes).toString();
+ }
+
+ @Override
+ public Long decodeValue(ByteBuffer bytes) {
+ if (!bytes.hasRemaining()) return null;
+ return bytes.getLong();
+ }
+
+ @Override
+ public ByteBuffer encodeKey(String key) {
+ return StandardCharsets.UTF_8.encode(key);
+ }
+
+ @Override
+ public ByteBuffer encodeValue(Long value) {
+ ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
+ buffer.putLong(value);
+ buffer.flip();
+ return buffer;
+ }
+}
diff --git a/apps/threat-detection/src/main/java/com/akto/threat/detection/cache/RedisBackedCounterCache.java b/apps/threat-detection/src/main/java/com/akto/threat/detection/cache/RedisBackedCounterCache.java
new file mode 100644
index 0000000000..2182be09c6
--- /dev/null
+++ b/apps/threat-detection/src/main/java/com/akto/threat/detection/cache/RedisBackedCounterCache.java
@@ -0,0 +1,126 @@
+package com.akto.threat.detection.cache;
+
+import com.github.benmanes.caffeine.cache.Cache;
+import com.github.benmanes.caffeine.cache.Caffeine;
+import io.lettuce.core.ExpireArgs;
+import io.lettuce.core.RedisClient;
+import io.lettuce.core.api.StatefulRedisConnection;
+import java.util.HashSet;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.*;
+
+public class RedisBackedCounterCache implements CounterCache {
+
+ static class Op {
+ private final String key;
+ private final long value;
+
+ public Op(String key, long value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public long getValue() {
+ return value;
+ }
+ }
+
+ private final StatefulRedisConnection redis;
+
+ private final Cache localCache;
+
+ private final ConcurrentLinkedQueue pendingIncOps;
+ private final ConcurrentMap deletedKeys;
+ private final String prefix;
+
+ public RedisBackedCounterCache(RedisClient redisClient, String prefix) {
+ this.prefix = prefix;
+ this.redis = redisClient.connect(new LongValueCodec());
+ this.localCache = Caffeine.newBuilder().maximumSize(100000).expireAfterWrite(3, TimeUnit.HOURS).build();
+
+ ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
+ executor.scheduleAtFixedRate(this::syncToRedis, 60, 5, TimeUnit.SECONDS);
+
+ this.pendingIncOps = new ConcurrentLinkedQueue<>();
+ this.deletedKeys = new ConcurrentHashMap<>();
+ }
+
+ private String addPrefixToKey(String key) {
+ return new StringBuilder().append(prefix).append("|").append(key).toString();
+ }
+
+ @Override
+ public void increment(String key) {
+ incrementBy(key, 1);
+ }
+
+ @Override
+ public void incrementBy(String key, long val) {
+ String _key = addPrefixToKey(key);
+ localCache.asMap().merge(_key, val, Long::sum);
+ pendingIncOps.add(new Op(_key, val));
+ }
+
+ @Override
+ public long get(String key) {
+ return Optional.ofNullable(this.localCache.getIfPresent(addPrefixToKey(key))).orElse(0L);
+ }
+
+ @Override
+ public boolean exists(String key) {
+ return localCache.asMap().containsKey(addPrefixToKey(key));
+ }
+
+ @Override
+ public void clear(String key) {
+ String _key = addPrefixToKey(key);
+ localCache.invalidate(_key);
+ this.deletedKeys.put(_key, true);
+ redis.async().del(_key);
+ }
+
+ private void setExpiryIfNotSet(String key, long seconds) {
+ // We only set expiry for redis entry. For local cache we have lower expiry for
+ // all entries.
+ ExpireArgs args = ExpireArgs.Builder.nx();
+ redis.async().expire(addPrefixToKey(key), seconds, args);
+ }
+
+ private void syncToRedis() {
+ Set _keys = new HashSet<>();
+ while (!pendingIncOps.isEmpty()) {
+ Op op = pendingIncOps.poll();
+ String key = op.getKey();
+ long val = op.getValue();
+
+ if (this.deletedKeys.containsKey(key)) {
+ continue;
+ }
+
+ redis
+ .async()
+ .incrby(key, val)
+ .whenComplete(
+ (result, ex) -> {
+ if (ex != null) {
+ ex.printStackTrace();
+ }
+
+ _keys.add(key);
+
+ if (result != null) {
+ localCache.asMap().put(key, result);
+ }
+ });
+ }
+
+ _keys.forEach(key -> setExpiryIfNotSet(key, 3 * 60 * 60));
+
+ this.deletedKeys.clear();
+ }
+}
diff --git a/apps/threat-detection/src/main/java/com/akto/threat/detection/constants/KafkaTopic.java b/apps/threat-detection/src/main/java/com/akto/threat/detection/constants/KafkaTopic.java
new file mode 100644
index 0000000000..7c13df8d87
--- /dev/null
+++ b/apps/threat-detection/src/main/java/com/akto/threat/detection/constants/KafkaTopic.java
@@ -0,0 +1,8 @@
+package com.akto.threat.detection.constants;
+
+public class KafkaTopic {
+ public static class ThreatDetection {
+ public static final String MALICIOUS_EVENTS = "akto.threat_detection.malicious_events";
+ public static final String ALERTS = "akto.threat_detection.alerts";
+ }
+}
\ No newline at end of file
diff --git a/apps/threat-detection/src/main/java/com/akto/threat/detection/db/entity/MaliciousEventEntity.java b/apps/threat-detection/src/main/java/com/akto/threat/detection/db/entity/MaliciousEventEntity.java
new file mode 100644
index 0000000000..41bf2422ee
--- /dev/null
+++ b/apps/threat-detection/src/main/java/com/akto/threat/detection/db/entity/MaliciousEventEntity.java
@@ -0,0 +1,209 @@
+package com.akto.threat.detection.db.entity;
+
+import com.akto.dto.type.URLMethods;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.UUID;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.PrePersist;
+import javax.persistence.Table;
+import org.hibernate.annotations.GenericGenerator;
+
+@Entity
+@Table(name = "malicious_event", schema = "threat_detection")
+public class MaliciousEventEntity {
+
+ @Id
+ @GeneratedValue(generator = "UUID")
+ @GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
+ private UUID id;
+
+ @Column(name = "actor")
+ private String actor;
+
+ @Column(name = "filter_id")
+ private String filterId;
+
+ @Column(name = "url")
+ private String url;
+
+ @Column(name = "method")
+ @Enumerated(EnumType.STRING)
+ private URLMethods.Method method;
+
+ @Column(name = "timestamp")
+ private long timestamp;
+
+ @Column(name = "orig")
+ private String orig;
+
+ // Geo location data
+ @Column(name = "ip")
+ private String ip;
+
+ @Column(name = "api_collection_id")
+ private int apiCollectionId;
+
+ @Column(name = "created_at", updatable = false)
+ private LocalDateTime createdAt;
+
+ @Column(name = "_alerted_to_backend")
+ private boolean alertedToBackend;
+
+ public MaliciousEventEntity() {}
+
+ @PrePersist
+ protected void onCreate() {
+ this.createdAt = LocalDateTime.now(ZoneOffset.UTC);
+ }
+
+ public MaliciousEventEntity(Builder builder) {
+ this.actor = builder.actorId;
+ this.filterId = builder.filterId;
+ this.url = builder.url;
+ this.method = builder.method;
+ this.timestamp = builder.timestamp;
+ this.orig = builder.orig;
+ this.ip = builder.ip;
+ this.apiCollectionId = builder.apiCollectionId;
+ }
+
+ public static class Builder {
+ private String actorId;
+ private String filterId;
+ private String url;
+ private URLMethods.Method method;
+ private long timestamp;
+ private String orig;
+ private String ip;
+ private int apiCollectionId;
+
+ public Builder setActor(String actorId) {
+ this.actorId = actorId;
+ return this;
+ }
+
+ public Builder setFilterId(String filterId) {
+ this.filterId = filterId;
+ return this;
+ }
+
+ public Builder setUrl(String url) {
+ this.url = url;
+ return this;
+ }
+
+ public Builder setMethod(URLMethods.Method method) {
+ this.method = method;
+ return this;
+ }
+
+ public Builder setTimestamp(long timestamp) {
+ this.timestamp = timestamp;
+ return this;
+ }
+
+ public Builder setOrig(String orig) {
+ this.orig = orig;
+ return this;
+ }
+
+ public Builder setIp(String ip) {
+ this.ip = ip;
+ return this;
+ }
+
+ public Builder setApiCollectionId(int apiCollectionId) {
+ this.apiCollectionId = apiCollectionId;
+ return this;
+ }
+
+ public MaliciousEventEntity build() {
+ return new MaliciousEventEntity(this);
+ }
+ }
+
+ public static Builder newBuilder() {
+ return new Builder();
+ }
+
+ public UUID getId() {
+ return id;
+ }
+
+ public String getActor() {
+ return actor;
+ }
+
+ public String getFilterId() {
+ return filterId;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public URLMethods.Method getMethod() {
+ return method;
+ }
+
+ public long getTimestamp() {
+ return timestamp;
+ }
+
+ public String getOrig() {
+ return orig;
+ }
+
+ public String getIp() {
+ return ip;
+ }
+
+ public int getApiCollectionId() {
+ return apiCollectionId;
+ }
+
+ public LocalDateTime getCreatedAt() {
+ return createdAt;
+ }
+
+ public boolean isAlertedToBackend() {
+ return alertedToBackend;
+ }
+
+ @Override
+ public String toString() {
+ return "MaliciousEventEntity{"
+ + "createdAt="
+ + createdAt
+ + ", apiCollectionId="
+ + apiCollectionId
+ + ", ip='"
+ + ip
+ + '\''
+ + ", orig='"
+ + orig
+ + '\''
+ + ", timestamp="
+ + timestamp
+ + ", method="
+ + method
+ + ", url='"
+ + url
+ + '\''
+ + ", filterId='"
+ + filterId
+ + '\''
+ + ", actor='"
+ + actor
+ + '\''
+ + ", id="
+ + id
+ + '}';
+ }
+}
diff --git a/apps/threat-detection/src/main/java/com/akto/threat/detection/dto/MessageEnvelope.java b/apps/threat-detection/src/main/java/com/akto/threat/detection/dto/MessageEnvelope.java
new file mode 100644
index 0000000000..06fe644be4
--- /dev/null
+++ b/apps/threat-detection/src/main/java/com/akto/threat/detection/dto/MessageEnvelope.java
@@ -0,0 +1,78 @@
+package com.akto.threat.detection.dto;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.protobuf.InvalidProtocolBufferException;
+import com.google.protobuf.Message;
+import com.google.protobuf.util.JsonFormat;
+import java.util.Optional;
+
+// Kafka Message Wrapper for suspect data
+public class MessageEnvelope {
+ private String accountId;
+ private String data;
+ private String actor;
+
+ private static final ObjectMapper objectMapper = new ObjectMapper();
+
+ public MessageEnvelope() {}
+
+ public MessageEnvelope(String accountId, String actor, String data) {
+ this.accountId = accountId;
+ this.actor = actor;
+ this.data = data;
+ }
+
+ public String getAccountId() {
+ return accountId;
+ }
+
+ public void setAccountId(String accountId) {
+ this.accountId = accountId;
+ }
+
+ public String getData() {
+ return data;
+ }
+
+ public void setData(String data) {
+ this.data = data;
+ }
+
+ public Optional marshal() {
+ try {
+ return Optional.ofNullable(objectMapper.writeValueAsString(this));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ return Optional.empty();
+ }
+
+ public static Optional unmarshal(String message) {
+ try {
+ return Optional.ofNullable(objectMapper.readValue(message, MessageEnvelope.class));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ return Optional.empty();
+ }
+
+ public static MessageEnvelope generateEnvelope(String accountId, String actor, Message msg)
+ throws InvalidProtocolBufferException {
+ String data = JsonFormat.printer().print(msg);
+ return new MessageEnvelope(accountId, actor, data);
+ }
+
+ public String getActor() {
+ return actor;
+ }
+
+ public void setActor(String actor) {
+ this.actor = actor;
+ }
+
+ public static ObjectMapper getObjectmapper() {
+ return objectMapper;
+ }
+}
diff --git a/apps/threat-detection/src/main/java/com/akto/threat/detection/kafka/KafkaProtoProducer.java b/apps/threat-detection/src/main/java/com/akto/threat/detection/kafka/KafkaProtoProducer.java
new file mode 100644
index 0000000000..913eec9a78
--- /dev/null
+++ b/apps/threat-detection/src/main/java/com/akto/threat/detection/kafka/KafkaProtoProducer.java
@@ -0,0 +1,49 @@
+package com.akto.threat.detection.kafka;
+
+import com.akto.kafka.KafkaConfig;
+import com.akto.kafka.Serializer;
+import com.google.protobuf.Message;
+import java.time.Duration;
+import java.util.Properties;
+import org.apache.kafka.clients.producer.*;
+
+public class KafkaProtoProducer {
+ private final KafkaProducer producer;
+ public boolean producerReady;
+
+ public KafkaProtoProducer(KafkaConfig kafkaConfig) {
+ this.producer =
+ generateProducer(
+ kafkaConfig.getBootstrapServers(),
+ kafkaConfig.getProducerConfig().getLingerMs(),
+ kafkaConfig.getProducerConfig().getBatchSize());
+ }
+
+ public void send(String topic, Message message) {
+ byte[] messageBytes = message.toByteArray();
+ this.producer.send(new ProducerRecord<>(topic, messageBytes));
+ }
+
+ public void close() {
+ this.producerReady = false;
+ producer.close(Duration.ofMillis(0)); // close immediately
+ }
+
+ private KafkaProducer generateProducer(
+ String brokerIP, int lingerMS, int batchSize) {
+ if (producer != null) close(); // close existing producer connection
+
+ int requestTimeoutMs = 5000;
+ Properties kafkaProps = new Properties();
+ kafkaProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, brokerIP);
+ kafkaProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, Serializer.STRING.getSerializer());
+ kafkaProps.put(
+ ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, Serializer.BYTE_ARRAY.getSerializer());
+ kafkaProps.put(ProducerConfig.BATCH_SIZE_CONFIG, batchSize);
+ kafkaProps.put(ProducerConfig.LINGER_MS_CONFIG, lingerMS);
+ kafkaProps.put(ProducerConfig.RETRIES_CONFIG, 0);
+ kafkaProps.put(ProducerConfig.REQUEST_TIMEOUT_MS_CONFIG, requestTimeoutMs);
+ kafkaProps.put(ProducerConfig.DELIVERY_TIMEOUT_MS_CONFIG, lingerMS + requestTimeoutMs);
+ return new KafkaProducer<>(kafkaProps);
+ }
+}
diff --git a/apps/threat-detection/src/main/java/com/akto/threat/detection/session_factory/SessionFactoryUtils.java b/apps/threat-detection/src/main/java/com/akto/threat/detection/session_factory/SessionFactoryUtils.java
new file mode 100644
index 0000000000..7bf80f0f7b
--- /dev/null
+++ b/apps/threat-detection/src/main/java/com/akto/threat/detection/session_factory/SessionFactoryUtils.java
@@ -0,0 +1,29 @@
+package com.akto.threat.detection.session_factory;
+
+import com.akto.threat.detection.db.entity.MaliciousEventEntity;
+import org.hibernate.SessionFactory;
+import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
+import org.hibernate.cfg.Configuration;
+
+public class SessionFactoryUtils {
+
+ public static SessionFactory createFactory() {
+ final String url = System.getenv("AKTO_THREAT_DETECTION_POSTGRES");
+ final String user = System.getenv("AKTO_THREAT_DETECTION_POSTGRES_USER");
+ final String password = System.getenv("AKTO_THREAT_DETECTION_POSTGRES_PASSWORD");
+
+ final Configuration cfg = new Configuration();
+ cfg.setProperty("hibernate.connection.url", url);
+ cfg.setProperty("hibernate.connection.user", user);
+ cfg.setProperty("hibernate.connection.password", password);
+ cfg.setProperty("dialect", "org.hibernate.dialect.PostgreSQL92Dialect");
+ cfg.setProperty("connection.driver_class", "org.postgresql.Driver");
+ cfg.setProperty("show_sql", "false");
+ cfg.setProperty("format_sql", "false");
+
+ cfg.addAnnotatedClass(MaliciousEventEntity.class);
+
+ return cfg.buildSessionFactory(
+ new StandardServiceRegistryBuilder().applySettings(cfg.getProperties()).build());
+ }
+}
diff --git a/apps/threat-detection/src/main/java/com/akto/threat/detection/smart_event_detector/window_based/Bin.java b/apps/threat-detection/src/main/java/com/akto/threat/detection/smart_event_detector/window_based/Bin.java
new file mode 100644
index 0000000000..fa1202f2dd
--- /dev/null
+++ b/apps/threat-detection/src/main/java/com/akto/threat/detection/smart_event_detector/window_based/Bin.java
@@ -0,0 +1,19 @@
+package com.akto.threat.detection.smart_event_detector.window_based;
+
+public class Bin {
+ int binId;
+ long count;
+
+ public Bin(int binId, long count) {
+ this.binId = binId;
+ this.count = count;
+ }
+
+ public int getBinId() {
+ return binId;
+ }
+
+ public long getCount() {
+ return count;
+ }
+}
diff --git a/apps/threat-detection/src/main/java/com/akto/threat/detection/smart_event_detector/window_based/Data.java b/apps/threat-detection/src/main/java/com/akto/threat/detection/smart_event_detector/window_based/Data.java
new file mode 100644
index 0000000000..43f928a6ab
--- /dev/null
+++ b/apps/threat-detection/src/main/java/com/akto/threat/detection/smart_event_detector/window_based/Data.java
@@ -0,0 +1,49 @@
+package com.akto.threat.detection.smart_event_detector.window_based;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.ArrayList;
+import java.util.List;
+
+public class Data {
+ @JsonProperty("ln")
+ public long lastNotifiedAt = 0;
+
+ @JsonProperty("rq")
+ public List requests = new ArrayList<>();
+
+ public static class Request {
+ private long receivedAt;
+
+ public Request() {}
+
+ public Request(long receivedAt) {
+ this.receivedAt = receivedAt;
+ }
+
+ public long getReceivedAt() {
+ return receivedAt;
+ }
+
+ public void setReceivedAt(long receivedAt) {
+ this.receivedAt = receivedAt;
+ }
+ }
+
+ public Data() {}
+
+ public long getLastNotifiedAt() {
+ return lastNotifiedAt;
+ }
+
+ public void setLastNotifiedAt(long lastNotifiedAt) {
+ this.lastNotifiedAt = lastNotifiedAt;
+ }
+
+ public List getRequests() {
+ return requests;
+ }
+
+ public void setRequests(List requests) {
+ this.requests = requests;
+ }
+}
diff --git a/apps/threat-detection/src/main/java/com/akto/threat/detection/smart_event_detector/window_based/WindowBasedThresholdNotifier.java b/apps/threat-detection/src/main/java/com/akto/threat/detection/smart_event_detector/window_based/WindowBasedThresholdNotifier.java
new file mode 100644
index 0000000000..01d3b531a7
--- /dev/null
+++ b/apps/threat-detection/src/main/java/com/akto/threat/detection/smart_event_detector/window_based/WindowBasedThresholdNotifier.java
@@ -0,0 +1,90 @@
+package com.akto.threat.detection.smart_event_detector.window_based;
+
+import com.akto.dto.api_protection_parse_layer.Rule;
+import com.akto.proto.generated.threat_detection.message.sample_request.v1.SampleMaliciousRequest;
+import com.akto.threat.detection.cache.CounterCache;
+import java.util.ArrayList;
+import java.util.List;
+
+public class WindowBasedThresholdNotifier {
+
+ private final Config config;
+
+ public static class Config {
+ private final int threshold;
+ private final int windowSizeInMinutes;
+ private int notificationCooldownInSeconds = 60 * 30; // 30 mins
+
+ public Config(int threshold, int windowInSeconds) {
+ this.threshold = threshold;
+ this.windowSizeInMinutes = windowInSeconds;
+ }
+
+ public int getThreshold() {
+ return threshold;
+ }
+
+ public int getWindowSizeInMinutes() {
+ return windowSizeInMinutes;
+ }
+
+ public int getNotificationCooldownInSeconds() {
+ return notificationCooldownInSeconds;
+ }
+ }
+
+ public static class Result {
+ private final boolean shouldNotify;
+
+ public Result(boolean shouldNotify) {
+ this.shouldNotify = shouldNotify;
+ }
+
+ public boolean shouldNotify() {
+ return shouldNotify;
+ }
+ }
+
+ public Config getConfig() {
+ return config;
+ }
+
+ private final CounterCache cache;
+
+ public WindowBasedThresholdNotifier(CounterCache cache, Config config) {
+ this.cache = cache;
+ this.config = config;
+ }
+
+ public Result shouldNotify(String aggKey, SampleMaliciousRequest maliciousEvent, Rule rule) {
+ int binId = (int) maliciousEvent.getTimestamp() / 60;
+ String cacheKey = aggKey + "|" + binId;
+ this.cache.increment(cacheKey);
+
+ long windowCount = 0L;
+ List bins = getBins(aggKey, binId - rule.getCondition().getWindowThreshold() + 1, binId);
+ for (Bin data : bins) {
+ windowCount += data.getCount();
+ }
+
+ boolean thresholdBreached = windowCount >= rule.getCondition().getMatchCount();
+
+ if (thresholdBreached) {
+ this.cache.clear(cacheKey);
+ }
+
+ return new Result(thresholdBreached);
+ }
+
+ public List getBins(String aggKey, int binStart, int binEnd) {
+ List binData = new ArrayList<>();
+ for (int i = binStart; i <= binEnd; i++) {
+ String key = aggKey + "|" + i;
+ if (!this.cache.exists(key)) {
+ continue;
+ }
+ binData.add(new Bin(i, this.cache.get(key)));
+ }
+ return binData;
+ }
+}
diff --git a/apps/threat-detection/src/main/java/com/akto/threat/detection/tasks/AbstractKafkaConsumerTask.java b/apps/threat-detection/src/main/java/com/akto/threat/detection/tasks/AbstractKafkaConsumerTask.java
new file mode 100644
index 0000000000..dd344ba281
--- /dev/null
+++ b/apps/threat-detection/src/main/java/com/akto/threat/detection/tasks/AbstractKafkaConsumerTask.java
@@ -0,0 +1,74 @@
+package com.akto.threat.detection.tasks;
+
+import com.akto.kafka.KafkaConfig;
+
+import java.time.Duration;
+import java.util.Collections;
+import java.util.Properties;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import org.apache.kafka.clients.consumer.Consumer;
+import org.apache.kafka.clients.consumer.ConsumerRecords;
+import org.apache.kafka.clients.consumer.KafkaConsumer;
+import org.apache.kafka.clients.consumer.ConsumerConfig;
+
+public abstract class AbstractKafkaConsumerTask implements Task {
+
+ protected Consumer kafkaConsumer;
+ protected KafkaConfig kafkaConfig;
+ protected String kafkaTopic;
+
+ public AbstractKafkaConsumerTask(KafkaConfig kafkaConfig, String kafkaTopic) {
+ this.kafkaTopic = kafkaTopic;
+ this.kafkaConfig = kafkaConfig;
+
+ Properties properties = new Properties();
+ properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaConfig.getBootstrapServers());
+ properties.put(
+ ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG,
+ kafkaConfig.getValueSerializer().getDeserializer());
+ properties.put(
+ ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG,
+ kafkaConfig.getValueSerializer().getDeserializer());
+ properties.put(
+ ConsumerConfig.MAX_POLL_RECORDS_CONFIG,
+ kafkaConfig.getConsumerConfig().getMaxPollRecords());
+ properties.put(ConsumerConfig.GROUP_ID_CONFIG, kafkaConfig.getGroupId());
+ properties.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
+ properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false);
+
+ this.kafkaConsumer = new KafkaConsumer<>(properties);
+ }
+
+ @Override
+ public void run() {
+ this.kafkaConsumer.subscribe(Collections.singletonList(this.kafkaTopic));
+
+ ExecutorService pollingExecutor = Executors.newSingleThreadExecutor();
+
+ pollingExecutor.execute(
+ () -> {
+ // Poll data from Kafka topic
+ while (true) {
+ ConsumerRecords records =
+ kafkaConsumer.poll(
+ Duration.ofMillis(kafkaConfig.getConsumerConfig().getPollDurationMilli()));
+ if (records.isEmpty()) {
+ continue;
+ }
+
+ try {
+ processRecords(records);
+
+ if (!records.isEmpty()) {
+ kafkaConsumer.commitSync();
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ });
+ }
+
+ abstract void processRecords(ConsumerRecords records);
+}
diff --git a/apps/threat-detection/src/main/java/com/akto/threat/detection/tasks/CleanupTask.java b/apps/threat-detection/src/main/java/com/akto/threat/detection/tasks/CleanupTask.java
new file mode 100644
index 0000000000..93a6d7c906
--- /dev/null
+++ b/apps/threat-detection/src/main/java/com/akto/threat/detection/tasks/CleanupTask.java
@@ -0,0 +1,39 @@
+package com.akto.threat.detection.tasks;
+
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.hibernate.Transaction;
+
+public class CleanupTask implements Task {
+
+ private final SessionFactory sessionFactory;
+
+ public CleanupTask(SessionFactory sessionFactory) {
+ this.sessionFactory = sessionFactory;
+ }
+
+ @Override
+ public void run() {
+ ScheduledExecutorService cronExecutorService = Executors.newScheduledThreadPool(1);
+ cronExecutorService.scheduleAtFixedRate(this::cleanup, 5, 30, TimeUnit.MINUTES);
+ }
+
+ private void cleanup() {
+ try (Session session = this.sessionFactory.openSession()) {
+ Transaction txn = session.beginTransaction();
+ int deletedCount =
+ session
+ .createQuery("delete from MaliciousEventEntity m where m.createdAt < :startDate")
+ .setParameter("startDate", LocalDateTime.now(ZoneOffset.UTC).minusDays(7))
+ .executeUpdate();
+
+ txn.commit();
+ System.out.println("Number of rows deleted: " + deletedCount);
+ }
+ }
+}
diff --git a/apps/threat-detection/src/main/java/com/akto/threat/detection/tasks/FlushSampleDataTask.java b/apps/threat-detection/src/main/java/com/akto/threat/detection/tasks/FlushSampleDataTask.java
new file mode 100644
index 0000000000..4b857c389e
--- /dev/null
+++ b/apps/threat-detection/src/main/java/com/akto/threat/detection/tasks/FlushSampleDataTask.java
@@ -0,0 +1,76 @@
+package com.akto.threat.detection.tasks;
+
+import com.akto.dto.type.URLMethods;
+import com.akto.kafka.KafkaConfig;
+import com.akto.proto.generated.threat_detection.message.sample_request.v1.SampleMaliciousRequest;
+import com.akto.proto.generated.threat_detection.message.sample_request.v1.SampleRequestKafkaEnvelope;
+import com.akto.threat.detection.db.entity.MaliciousEventEntity;
+import com.akto.threat.detection.dto.MessageEnvelope;
+import com.google.protobuf.InvalidProtocolBufferException;
+import com.google.protobuf.util.JsonFormat;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.kafka.clients.consumer.ConsumerRecords;
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.hibernate.Transaction;
+
+/*
+This will read sample malicious data from kafka topic and save it to DB.
+ */
+public class FlushSampleDataTask extends AbstractKafkaConsumerTask {
+
+ private final SessionFactory sessionFactory;
+
+ public FlushSampleDataTask(
+ SessionFactory sessionFactory, KafkaConfig trafficConfig, String topic) {
+ super(trafficConfig, topic);
+ this.sessionFactory = sessionFactory;
+ }
+
+ protected void processRecords(ConsumerRecords records) {
+ List events = new ArrayList<>();
+ records.forEach(
+ r -> {
+ SampleRequestKafkaEnvelope envelope;
+ try {
+ envelope = SampleRequestKafkaEnvelope.parseFrom(r.value());
+ SampleMaliciousRequest evt = envelope.getMaliciousRequest();
+
+ events.add(
+ MaliciousEventEntity.newBuilder()
+ .setActor(envelope.getActor())
+ .setFilterId(evt.getFilterId())
+ .setUrl(evt.getUrl())
+ .setMethod(URLMethods.Method.fromString(evt.getMethod()))
+ .setTimestamp(evt.getTimestamp())
+ .setOrig(evt.getPayload())
+ .setApiCollectionId(evt.getApiCollectionId())
+ .setIp(evt.getIp())
+ .build());
+ } catch (InvalidProtocolBufferException e) {
+ e.printStackTrace();
+ }
+ });
+
+ Session session = this.sessionFactory.openSession();
+ Transaction txn = session.beginTransaction();
+ try {
+ // Commit these events in 2 batches
+ for (int i = 0; i < events.size(); i++) {
+ session.persist(events.get(i));
+ if (i % 50 == 0) {
+ session.flush();
+ session.clear();
+ }
+ }
+
+ txn.commit();
+ } catch (Exception e) {
+ e.printStackTrace();
+ txn.rollback();
+ } finally {
+ session.close();
+ }
+ }
+}
diff --git a/apps/threat-detection/src/main/java/com/akto/threat/detection/tasks/MaliciousTrafficDetectorTask.java b/apps/threat-detection/src/main/java/com/akto/threat/detection/tasks/MaliciousTrafficDetectorTask.java
new file mode 100644
index 0000000000..3ba3603d50
--- /dev/null
+++ b/apps/threat-detection/src/main/java/com/akto/threat/detection/tasks/MaliciousTrafficDetectorTask.java
@@ -0,0 +1,346 @@
+package com.akto.threat.detection.tasks;
+
+import com.akto.dao.context.Context;
+import com.akto.dao.monitoring.FilterYamlTemplateDao;
+import com.akto.data_actor.DataActor;
+import com.akto.data_actor.DataActorFactory;
+import com.akto.dto.ApiInfo;
+import com.akto.dto.HttpRequestParams;
+import com.akto.dto.HttpResponseParams;
+import com.akto.dto.RawApi;
+import com.akto.dto.api_protection_parse_layer.AggregationRules;
+import com.akto.dto.api_protection_parse_layer.Condition;
+import com.akto.dto.api_protection_parse_layer.Rule;
+import com.akto.dto.monitoring.FilterConfig;
+import com.akto.dto.test_editor.YamlTemplate;
+import com.akto.dto.type.URLMethods;
+import com.akto.hybrid_parsers.HttpCallParser;
+import com.akto.kafka.KafkaConfig;
+import com.akto.proto.generated.threat_detection.message.malicious_event.event_type.v1.EventType;
+import com.akto.proto.generated.threat_detection.message.malicious_event.v1.MaliciousEventKafkaEnvelope;
+import com.akto.proto.generated.threat_detection.message.malicious_event.v1.MaliciousEventMessage;
+import com.akto.proto.generated.threat_detection.message.sample_request.v1.SampleMaliciousRequest;
+import com.akto.proto.generated.threat_detection.message.sample_request.v1.SampleRequestKafkaEnvelope;
+import com.akto.proto.http_response_param.v1.HttpResponseParam;
+import com.akto.rules.TestPlugin;
+import com.akto.test_editor.execution.VariableResolver;
+import com.akto.test_editor.filter.data_operands_impl.ValidationResult;
+import com.akto.threat.detection.actor.SourceIPActorGenerator;
+import com.akto.threat.detection.cache.RedisBackedCounterCache;
+import com.akto.threat.detection.constants.KafkaTopic;
+import com.akto.threat.detection.kafka.KafkaProtoProducer;
+import com.akto.threat.detection.smart_event_detector.window_based.WindowBasedThresholdNotifier;
+import com.akto.util.HttpRequestResponseUtils;
+import io.lettuce.core.RedisClient;
+import java.time.Duration;
+import java.util.*;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import org.apache.commons.lang3.math.NumberUtils;
+import org.apache.kafka.clients.consumer.*;
+
+/*
+Class is responsible for consuming traffic data from the Kafka topic.
+Pass data through filters and identify malicious traffic.
+ */
+public class MaliciousTrafficDetectorTask implements Task {
+
+ private final Consumer kafkaConsumer;
+ private final KafkaConfig kafkaConfig;
+ private final HttpCallParser httpCallParser;
+ private final WindowBasedThresholdNotifier windowBasedThresholdNotifier;
+
+ private Map apiFilters;
+ private int filterLastUpdatedAt = 0;
+ private int filterUpdateIntervalSec = 900;
+
+ private final KafkaProtoProducer internalKafka;
+
+ private static final DataActor dataActor = DataActorFactory.fetchInstance();
+
+ public MaliciousTrafficDetectorTask(
+ KafkaConfig trafficConfig, KafkaConfig internalConfig, RedisClient redisClient) {
+ this.kafkaConfig = trafficConfig;
+
+ Properties properties = new Properties();
+ properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, trafficConfig.getBootstrapServers());
+ properties.put(
+ ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG,
+ trafficConfig.getKeySerializer().getDeserializer());
+ properties.put(
+ ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG,
+ trafficConfig.getValueSerializer().getDeserializer());
+ properties.put(
+ ConsumerConfig.MAX_POLL_RECORDS_CONFIG,
+ trafficConfig.getConsumerConfig().getMaxPollRecords());
+ properties.put(ConsumerConfig.GROUP_ID_CONFIG, trafficConfig.getGroupId());
+ properties.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
+ properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false);
+ this.kafkaConsumer = new KafkaConsumer<>(properties);
+
+ this.httpCallParser = new HttpCallParser(120, 1000);
+
+ this.windowBasedThresholdNotifier =
+ new WindowBasedThresholdNotifier(
+ new RedisBackedCounterCache(redisClient, "wbt"),
+ new WindowBasedThresholdNotifier.Config(100, 10 * 60));
+
+ this.internalKafka = new KafkaProtoProducer(internalConfig);
+ }
+
+ public void run() {
+ this.kafkaConsumer.subscribe(Collections.singletonList("akto.api.logs"));
+ ExecutorService pollingExecutor = Executors.newSingleThreadExecutor();
+ pollingExecutor.execute(
+ () -> {
+ // Poll data from Kafka topic
+ while (true) {
+ ConsumerRecords records =
+ kafkaConsumer.poll(
+ Duration.ofMillis(kafkaConfig.getConsumerConfig().getPollDurationMilli()));
+
+ try {
+ for (ConsumerRecord record : records) {
+ HttpResponseParam httpResponseParam = HttpResponseParam.parseFrom(record.value());
+ processRecord(httpResponseParam);
+ }
+
+ if (!records.isEmpty()) {
+ // Should we commit even if there are no records ?
+ kafkaConsumer.commitSync();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ }
+
+ private Map getFilters() {
+ int now = (int) (System.currentTimeMillis() / 1000);
+ if (now - filterLastUpdatedAt < filterUpdateIntervalSec) {
+ return apiFilters;
+ }
+
+ List templates = dataActor.fetchFilterYamlTemplates();
+ apiFilters = FilterYamlTemplateDao.fetchFilterConfig(false, templates, false);
+ this.filterLastUpdatedAt = now;
+ return apiFilters;
+ }
+
+ private boolean validateFilterForRequest(
+ FilterConfig apiFilter, RawApi rawApi, ApiInfo.ApiInfoKey apiInfoKey, String message) {
+ try {
+ Map varMap = apiFilter.resolveVarMap();
+ VariableResolver.resolveWordList(
+ varMap,
+ new HashMap>() {
+ {
+ put(apiInfoKey, Collections.singletonList(message));
+ }
+ },
+ apiInfoKey);
+
+ String filterExecutionLogId = UUID.randomUUID().toString();
+ ValidationResult res =
+ TestPlugin.validateFilter(
+ apiFilter.getFilter().getNode(), rawApi, apiInfoKey, varMap, filterExecutionLogId);
+
+ return res.getIsValid();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ return false;
+ }
+
+ private void processRecord(HttpResponseParam record) throws Exception {
+ HttpResponseParams responseParam = buildHttpResponseParam(record);
+
+ Context.accountId.set(Integer.parseInt(responseParam.getAccountId()));
+ Map filters = this.getFilters();
+ if (filters.isEmpty()) {
+ return;
+ }
+
+ List maliciousMessages = new ArrayList<>();
+
+ String message = responseParam.getOrig();
+ RawApi rawApi = RawApi.buildFromMessageNew(responseParam);
+ int apiCollectionId = httpCallParser.createApiCollectionId(responseParam);
+ responseParam.requestParams.setApiCollectionId(apiCollectionId);
+ String url = responseParam.getRequestParams().getURL();
+ URLMethods.Method method =
+ URLMethods.Method.fromString(responseParam.getRequestParams().getMethod());
+ ApiInfo.ApiInfoKey apiInfoKey = new ApiInfo.ApiInfoKey(apiCollectionId, url, method);
+
+ for (FilterConfig apiFilter : apiFilters.values()) {
+ boolean hasPassedFilter = validateFilterForRequest(apiFilter, rawApi, apiInfoKey, message);
+
+ // If a request passes any of the filter, then it's a malicious request,
+ // and so we push it to kafka
+ if (hasPassedFilter) {
+ // Later we will also add aggregation support
+ // Eg: 100 4xx requests in last 10 minutes.
+ // But regardless of whether request falls in aggregation or not,
+ // we still push malicious requests to kafka
+
+ // todo: modify fetch yaml and read aggregate rules from it
+ List rules = new ArrayList<>();
+ rules.add(new Rule("Lfi Rule 1", new Condition(10, 10)));
+ AggregationRules aggRules = new AggregationRules();
+ aggRules.setRule(rules);
+
+ boolean isAggFilter = aggRules != null && !aggRules.getRule().isEmpty();
+
+ SourceIPActorGenerator.instance
+ .generate(responseParam)
+ .ifPresent(
+ actor -> {
+ String groupKey = apiFilter.getId();
+ String aggKey = actor + "|" + groupKey;
+
+ SampleMaliciousRequest maliciousReq =
+ SampleMaliciousRequest.newBuilder()
+ .setUrl(responseParam.getRequestParams().getURL())
+ .setMethod(responseParam.getRequestParams().getMethod())
+ .setPayload(responseParam.getOrig())
+ .setIp(actor) // For now using actor as IP
+ .setApiCollectionId(responseParam.getRequestParams().getApiCollectionId())
+ .setTimestamp(responseParam.getTime())
+ .setFilterId(apiFilter.getId())
+ .build();
+
+ maliciousMessages.add(
+ SampleRequestKafkaEnvelope.newBuilder()
+ .setActor(actor)
+ .setAccountId(responseParam.getAccountId())
+ .setMaliciousRequest(maliciousReq)
+ .build());
+
+ if (!isAggFilter) {
+ generateAndPushMaliciousEventRequest(
+ apiFilter, actor, responseParam, maliciousReq, EventType.EVENT_TYPE_SINGLE);
+ return;
+ }
+
+ // Aggregation rules
+ for (Rule rule : aggRules.getRule()) {
+ WindowBasedThresholdNotifier.Result result =
+ this.windowBasedThresholdNotifier.shouldNotify(aggKey, maliciousReq, rule);
+
+ if (result.shouldNotify()) {
+ generateAndPushMaliciousEventRequest(
+ apiFilter,
+ actor,
+ responseParam,
+ maliciousReq,
+ EventType.EVENT_TYPE_AGGREGATED);
+ }
+ }
+ });
+ }
+ }
+
+ // Should we push all the messages in one go
+ // or call kafka.send for each HttpRequestParams
+ try {
+ maliciousMessages.forEach(
+ sample -> {
+ internalKafka.send(KafkaTopic.ThreatDetection.MALICIOUS_EVENTS, sample);
+ });
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void generateAndPushMaliciousEventRequest(
+ FilterConfig apiFilter,
+ String actor,
+ HttpResponseParams responseParam,
+ SampleMaliciousRequest maliciousReq,
+ EventType eventType) {
+ MaliciousEventMessage maliciousEvent =
+ MaliciousEventMessage.newBuilder()
+ .setFilterId(apiFilter.getId())
+ .setActor(actor)
+ .setDetectedAt(responseParam.getTime())
+ .setEventType(eventType)
+ .setLatestApiCollectionId(maliciousReq.getApiCollectionId())
+ .setLatestApiIp(maliciousReq.getIp())
+ .setLatestApiPayload(maliciousReq.getPayload())
+ .setLatestApiMethod(maliciousReq.getMethod())
+ .setDetectedAt(responseParam.getTime())
+ .build();
+ MaliciousEventKafkaEnvelope envelope =
+ MaliciousEventKafkaEnvelope.newBuilder()
+ .setActor(actor)
+ .setAccountId(responseParam.getAccountId())
+ .setMaliciousEvent(maliciousEvent)
+ .build();
+ internalKafka.send(KafkaTopic.ThreatDetection.ALERTS, envelope);
+ }
+
+ public static HttpResponseParams buildHttpResponseParam(
+ HttpResponseParam httpResponseParamProto) {
+
+ String apiCollectionIdStr = httpResponseParamProto.getAktoVxlanId();
+ int apiCollectionId = 0;
+ if (NumberUtils.isDigits(apiCollectionIdStr)) {
+ apiCollectionId = NumberUtils.toInt(apiCollectionIdStr, 0);
+ }
+
+ String requestPayload =
+ HttpRequestResponseUtils.rawToJsonString(httpResponseParamProto.getRequestPayload(), null);
+
+ Map> reqHeaders = (Map) httpResponseParamProto.getRequestHeadersMap();
+
+ // for (Map.Entry entry :
+ // httpResponseParamProto.getRequestHeadersMap().entrySet()) {
+ // ArrayList list = new ArrayList<>(entry.getValue().getValuesList());
+ // reqHeaders.put(entry.getKey(), list);
+ // }
+
+ HttpRequestParams requestParams =
+ new HttpRequestParams(
+ httpResponseParamProto.getMethod(),
+ httpResponseParamProto.getPath(),
+ httpResponseParamProto.getType(),
+ reqHeaders,
+ requestPayload,
+ apiCollectionId);
+
+ String responsePayload =
+ HttpRequestResponseUtils.rawToJsonString(httpResponseParamProto.getResponsePayload(), null);
+
+ String sourceStr = httpResponseParamProto.getSource();
+ if (sourceStr == null || sourceStr == "") {
+ sourceStr = HttpResponseParams.Source.OTHER.name();
+ }
+
+ HttpResponseParams.Source source = HttpResponseParams.Source.valueOf(sourceStr);
+ Map> respHeaders = (Map) httpResponseParamProto.getResponseHeadersMap();
+
+ // for (Map.Entry entry :
+ // httpResponseParamProto.getResponseHeadersMap().entrySet()) {
+ // ArrayList list = new ArrayList<>(entry.getValue().getValuesList());
+ // respHeaders.put(entry.getKey(), list);
+ // }
+
+ return new HttpResponseParams(
+ httpResponseParamProto.getType(),
+ httpResponseParamProto.getStatusCode(),
+ httpResponseParamProto.getStatus(),
+ respHeaders,
+ responsePayload,
+ requestParams,
+ httpResponseParamProto.getTime(),
+ httpResponseParamProto.getAktoAccountId(),
+ httpResponseParamProto.getIsPending(),
+ source,
+ "",
+ httpResponseParamProto.getIp(),
+ httpResponseParamProto.getDestIp(),
+ httpResponseParamProto.getDirection());
+ }
+}
diff --git a/apps/threat-detection/src/main/java/com/akto/threat/detection/tasks/SendMaliciousEventsToBackend.java b/apps/threat-detection/src/main/java/com/akto/threat/detection/tasks/SendMaliciousEventsToBackend.java
new file mode 100644
index 0000000000..08e634069e
--- /dev/null
+++ b/apps/threat-detection/src/main/java/com/akto/threat/detection/tasks/SendMaliciousEventsToBackend.java
@@ -0,0 +1,156 @@
+package com.akto.threat.detection.tasks;
+
+import com.akto.kafka.KafkaConfig;
+import com.akto.proto.generated.threat_detection.message.malicious_event.event_type.v1.EventType;
+import com.akto.proto.generated.threat_detection.message.malicious_event.v1.MaliciousEventKafkaEnvelope;
+import com.akto.proto.generated.threat_detection.message.malicious_event.v1.MaliciousEventMessage;
+import com.akto.proto.generated.threat_detection.message.sample_request.v1.SampleMaliciousRequest;
+import com.akto.proto.generated.threat_detection.service.malicious_alert_service.v1.RecordMaliciousEventRequest;
+import com.akto.proto.utils.ProtoMessageUtils;
+import com.akto.threat.detection.db.entity.MaliciousEventEntity;
+import com.google.protobuf.InvalidProtocolBufferException;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+import java.util.stream.Collectors;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.kafka.clients.consumer.ConsumerRecords;
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.hibernate.Transaction;
+
+/*
+This will send alerts to threat detection backend
+ */
+public class SendMaliciousEventsToBackend extends AbstractKafkaConsumerTask {
+
+ private final SessionFactory sessionFactory;
+ private final CloseableHttpClient httpClient;
+
+ public SendMaliciousEventsToBackend(
+ SessionFactory sessionFactory, KafkaConfig trafficConfig, String topic) {
+ super(trafficConfig, topic);
+ this.sessionFactory = sessionFactory;
+ this.httpClient = HttpClients.createDefault();
+ }
+
+ private void markSampleDataAsSent(List ids) {
+ Session session = this.sessionFactory.openSession();
+ Transaction txn = session.beginTransaction();
+ try {
+ session
+ .createQuery(
+ "update MaliciousEventEntity m set m.alertedToBackend = true where m.id in :ids")
+ .setParameterList("ids", ids)
+ .executeUpdate();
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ txn.rollback();
+ } finally {
+ txn.commit();
+ session.close();
+ }
+ }
+
+ private List getSampleMaliciousRequests(String actor, String filterId) {
+ Session session = this.sessionFactory.openSession();
+ Transaction txn = session.beginTransaction();
+ try {
+ return session
+ .createQuery(
+ "from MaliciousEventEntity m where m.actor = :actor and m.filterId = :filterId and"
+ + " m.alertedToBackend = false order by m.createdAt desc",
+ MaliciousEventEntity.class)
+ .setParameter("actor", actor)
+ .setParameter("filterId", filterId)
+ .setMaxResults(50)
+ .getResultList();
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ txn.rollback();
+ } finally {
+ txn.commit();
+ session.close();
+ }
+
+ return Collections.emptyList();
+ }
+
+ protected void processRecords(ConsumerRecords records) {
+ records.forEach(
+ r -> {
+ MaliciousEventKafkaEnvelope envelope;
+ try {
+ envelope = MaliciousEventKafkaEnvelope.parseFrom(r.value());
+ } catch (InvalidProtocolBufferException e) {
+ e.printStackTrace();
+ return;
+ }
+
+ if (envelope == null) {
+ return;
+ }
+
+ try {
+ MaliciousEventMessage evt = envelope.getMaliciousEvent();
+
+ // Get sample data from postgres for this alert
+ List sampleData =
+ this.getSampleMaliciousRequests(evt.getActor(), evt.getFilterId());
+ RecordMaliciousEventRequest.Builder reqBuilder =
+ RecordMaliciousEventRequest.newBuilder().setMaliciousEvent(evt);
+ if (EventType.EVENT_TYPE_AGGREGATED.equals(evt.getEventType())) {
+ sampleData = this.getSampleMaliciousRequests(evt.getActor(), evt.getFilterId());
+
+ reqBuilder.addAllSampleRequests(
+ sampleData.stream()
+ .map(
+ d ->
+ SampleMaliciousRequest.newBuilder()
+ .setUrl(d.getUrl())
+ .setMethod(d.getMethod().name())
+ .setTimestamp(d.getTimestamp())
+ .setPayload(d.getOrig())
+ .setIp(d.getIp())
+ .setApiCollectionId(d.getApiCollectionId())
+ .build())
+ .collect(Collectors.toList()));
+ }
+
+ List sampleIds =
+ sampleData.stream().map(MaliciousEventEntity::getId).collect(Collectors.toList());
+
+ RecordMaliciousEventRequest maliciousEventRequest = reqBuilder.build();
+ String url = System.getenv("AKTO_THREAT_PROTECTION_BACKEND_URL");
+ String token = System.getenv("AKTO_THREAT_PROTECTION_BACKEND_TOKEN");
+ ProtoMessageUtils.toString(maliciousEventRequest)
+ .ifPresent(
+ msg -> {
+ StringEntity requestEntity =
+ new StringEntity(msg, ContentType.APPLICATION_JSON);
+ HttpPost req =
+ new HttpPost(
+ String.format("%s/api/threat_detection/record_malicious_event", url));
+ req.addHeader("Authorization", "Bearer " + token);
+ req.setEntity(requestEntity);
+ try {
+ this.httpClient.execute(req);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ if (!sampleIds.isEmpty()) {
+ markSampleDataAsSent(sampleIds);
+ }
+ });
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ });
+ }
+}
diff --git a/apps/threat-detection/src/main/java/com/akto/threat/detection/tasks/Task.java b/apps/threat-detection/src/main/java/com/akto/threat/detection/tasks/Task.java
new file mode 100644
index 0000000000..1156ccbd8b
--- /dev/null
+++ b/apps/threat-detection/src/main/java/com/akto/threat/detection/tasks/Task.java
@@ -0,0 +1,6 @@
+package com.akto.threat.detection.tasks;
+
+public interface Task {
+
+ void run();
+}
diff --git a/apps/threat-detection/src/main/resources/db/migration/V1__enable_uuid.sql b/apps/threat-detection/src/main/resources/db/migration/V1__enable_uuid.sql
new file mode 100644
index 0000000000..e5c340505a
--- /dev/null
+++ b/apps/threat-detection/src/main/resources/db/migration/V1__enable_uuid.sql
@@ -0,0 +1,2 @@
+-- enabling uuid extension
+create extension if not exists "uuid-ossp";
\ No newline at end of file
diff --git a/apps/threat-detection/src/main/resources/db/migration/V2__create_malicious_event_table.sql b/apps/threat-detection/src/main/resources/db/migration/V2__create_malicious_event_table.sql
new file mode 100644
index 0000000000..17e1b9d80c
--- /dev/null
+++ b/apps/threat-detection/src/main/resources/db/migration/V2__create_malicious_event_table.sql
@@ -0,0 +1,17 @@
+-- create schema and table for malicious events
+create schema if not exists threat_detection;
+create table if not exists threat_detection.malicious_event (
+ id uuid primary key default uuid_generate_v4(),
+ actor varchar(255) not null,
+ filter_id varchar(255) not null,
+ url varchar(1024),
+ ip varchar(255),
+ method varchar(255),
+ timestamp bigint not null,
+ orig text not null,
+ api_collection_id int not null,
+ created_at timestamp default (timezone('utc', now()))
+);
+
+-- add index on actor and filter_id and sort data by timestamp
+create index malicious_events_actor_filter_id_timestamp_idx on threat_detection.malicious_event(actor, filter_id, timestamp desc);
diff --git a/apps/threat-detection/src/main/resources/db/migration/V3__add_alert_to_backend_column.sql b/apps/threat-detection/src/main/resources/db/migration/V3__add_alert_to_backend_column.sql
new file mode 100644
index 0000000000..756815e5b0
--- /dev/null
+++ b/apps/threat-detection/src/main/resources/db/migration/V3__add_alert_to_backend_column.sql
@@ -0,0 +1,4 @@
+alter table threat_detection.malicious_event add column _alerted_to_backend boolean default false;
+
+-- set all existing rows to false
+update threat_detection.malicious_event set _alerted_to_backend = false;
diff --git a/apps/threat-detection/src/main/resources/log4j2.xml b/apps/threat-detection/src/main/resources/log4j2.xml
new file mode 100644
index 0000000000..133e6bdd7e
--- /dev/null
+++ b/apps/threat-detection/src/main/resources/log4j2.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+ [%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/api-threat-detection/src/main/resources/version.txt b/apps/threat-detection/src/main/resources/version.txt
similarity index 100%
rename from apps/api-threat-detection/src/main/resources/version.txt
rename to apps/threat-detection/src/main/resources/version.txt
diff --git a/libs/dao/src/main/java/com/akto/DaoInit.java b/libs/dao/src/main/java/com/akto/DaoInit.java
index 7e4ea5a2fe..04ced65abf 100644
--- a/libs/dao/src/main/java/com/akto/DaoInit.java
+++ b/libs/dao/src/main/java/com/akto/DaoInit.java
@@ -72,6 +72,8 @@
import com.mongodb.ConnectionString;
import com.mongodb.MongoClientSettings;
import com.mongodb.ReadPreference;
+import com.mongodb.client.MongoClient;
+import com.mongodb.WriteConcern;
import com.mongodb.WriteConcern;
import com.mongodb.client.MongoClients;
@@ -88,327 +90,471 @@
public class DaoInit {
- private static final Logger logger = LoggerFactory.getLogger(DaoInit.class);
+ private static final Logger logger = LoggerFactory.getLogger(DaoInit.class);
- public static CodecRegistry createCodecRegistry(){
- ClassModel configClassModel = ClassModel.builder(Config.class).enableDiscriminator(true).build();
- ClassModel signupInfoClassModel = ClassModel.builder(SignupInfo.class).enableDiscriminator(true)
- .build();
- ClassModel apiAuthClassModel = ClassModel.builder(APIAuth.class).enableDiscriminator(true).build();
- ClassModel attempResultModel = ClassModel.builder(AttemptResult.class).enableDiscriminator(true)
- .build();
- ClassModel urlTemplateModel = ClassModel.builder(URLTemplate.class).enableDiscriminator(true)
- .build();
- ClassModel pendingInviteCodeClassModel = ClassModel.builder(PendingInviteCode.class)
- .enableDiscriminator(true).build();
- ClassModel rbacClassModel = ClassModel.builder(RBAC.class).enableDiscriminator(true).build();
- ClassModel singleTypeInfoClassModel = ClassModel.builder(SingleTypeInfo.class)
- .enableDiscriminator(true).build();
- ClassModel kafkaHealthMetricClassModel = ClassModel.builder(KafkaHealthMetric.class)
- .enableDiscriminator(true).build();
- ClassModel thirdPartyAccessClassModel = ClassModel.builder(ThirdPartyAccess.class)
- .enableDiscriminator(true).build();
- ClassModel credentialClassModel = ClassModel.builder(Credential.class).enableDiscriminator(true)
- .build();
- ClassModel apiTokenClassModel = ClassModel.builder(ApiToken.class).enableDiscriminator(true).build();
- ClassModel apiInfoClassModel = ClassModel.builder(ApiInfo.class).enableDiscriminator(true).build();
- ClassModel apiInfoKeyClassModel = ClassModel.builder(ApiInfo.ApiInfoKey.class)
- .enableDiscriminator(true).build();
- ClassModel customFilterClassModel = ClassModel.builder(CustomFilter.class)
- .enableDiscriminator(true).build();
- ClassModel fieldExistsFilterClassModel = ClassModel.builder(FieldExistsFilter.class)
- .enableDiscriminator(true).build();
- ClassModel responseCodeRuntimeFilterClassModel = ClassModel
- .builder(ResponseCodeRuntimeFilter.class).enableDiscriminator(true).build();
- ;
- ClassModel runtimeFilterClassModel = ClassModel.builder(RuntimeFilter.class)
- .enableDiscriminator(true).build();
- ClassModel filterSampleDataClassModel = ClassModel.builder(FilterSampleData.class)
- .enableDiscriminator(true).build();
- ClassModel accountSettingsClassModel = ClassModel.builder(AccountSettings.class)
- .enableDiscriminator(true).build();
- ClassModel predicateClassModel = ClassModel.builder(Predicate.class).enableDiscriminator(true)
- .build();
- ClassModel regexPredicateClassModel = ClassModel.builder(RegexPredicate.class)
- .enableDiscriminator(true).build();
- ClassModel startsWithPredicateClassModel = ClassModel.builder(StartsWithPredicate.class)
- .enableDiscriminator(true).build();
- ClassModel endsWithPredicateClassModel = ClassModel.builder(EndsWithPredicate.class)
- .enableDiscriminator(true).build();
- ClassModel equalsToPredicateClassModel = ClassModel.builder(EqualsToPredicate.class)
- .enableDiscriminator(true).build();
- ClassModel isNumberPredicateClassModel = ClassModel.builder(IsNumberPredicate.class)
- .enableDiscriminator(true).build();
- ClassModel conditionsClassModel = ClassModel.builder(Conditions.class).enableDiscriminator(true)
- .build();
- ClassModel cappedListClassModel = ClassModel.builder(CappedList.class).enableDiscriminator(true)
- .build();
- ClassModel testingRunClassModel = ClassModel.builder(TestingRun.class).enableDiscriminator(true)
- .build();
- ClassModel testingRunResultClassModel = ClassModel.builder(TestingRunResult.class)
- .enableDiscriminator(true).build();
- ClassModel testResultClassModel = ClassModel.builder(TestResult.class).enableDiscriminator(true)
- .build();
- ClassModel multiExecTestResultClassModel = ClassModel.builder(MultiExecTestResult.class).enableDiscriminator(true)
- .build();
- ClassModel genericTestResultClassModel = ClassModel.builder(GenericTestResult.class).enableDiscriminator(true)
- .build();
- ClassModel authMechanismClassModel = ClassModel.builder(AuthMechanism.class)
- .enableDiscriminator(true).build();
- ClassModel setupClassModel = ClassModel.builder(Setup.class)
- .enableDiscriminator(true).build();
- ClassModel authParamClassModel = ClassModel.builder(AuthParam.class).enableDiscriminator(true)
- .build();
- ClassModel hardcodedAuthParamClassModel = ClassModel.builder(HardcodedAuthParam.class)
- .enableDiscriminator(true).build();
- ClassModel loginReqAuthParamClassModel = ClassModel.builder(LoginRequestAuthParam.class)
- .enableDiscriminator(true).build();
- ClassModel testingEndpointsClassModel = ClassModel.builder(TestingEndpoints.class)
- .enableDiscriminator(true).build();
- ClassModel customTestingEndpointsClassModel = ClassModel
- .builder(CustomTestingEndpoints.class).enableDiscriminator(true).build();
- ClassModel allTestingEndpointsClassModel = ClassModel
- .builder(AllTestingEndpoints.class).enableDiscriminator(true).build();
- ClassModel collectionWiseTestingEndpointsClassModel = ClassModel
- .builder(CollectionWiseTestingEndpoints.class).enableDiscriminator(true).build();
- ClassModel workflowTestingEndpointsClassModel = ClassModel
- .builder(WorkflowTestingEndpoints.class).enableDiscriminator(true).build();
- ClassModel workflowTestResultClassModel = ClassModel.builder(WorkflowTestResult.class)
- .enableDiscriminator(true).build();
- ClassModel workflowTestClassModel = ClassModel.builder(WorkflowTest.class)
- .enableDiscriminator(true).build();
- ClassModel cappedSetClassModel = ClassModel.builder(CappedSet.class).enableDiscriminator(true)
- .build();
- ClassModel CustomWebhookClassModel = ClassModel.builder(CustomWebhook.class)
- .enableDiscriminator(true).build();
- ClassModel WorkflowNodeDetailsClassModel = ClassModel.builder(WorkflowNodeDetails.class)
- .enableDiscriminator(true).build();
- ClassModel CustomWebhookResultClassModel = ClassModel.builder(CustomWebhookResult.class)
- .enableDiscriminator(true).build();
- ClassModel nodeResultClassModel = ClassModel
- .builder(WorkflowTestResult.NodeResult.class).enableDiscriminator(true).build();
- ClassModel testingRunIssuesClassModel = ClassModel
- .builder(TestingRunIssues.class).enableDiscriminator(true).build();
- ClassModel testingIssuesIdClassModel = ClassModel
- .builder(TestingIssuesId.class).enableDiscriminator(true).build();
- ClassModel testSourceConfigClassModel = ClassModel
- .builder(TestSourceConfig.class).enableDiscriminator(true).build();
- ClassModel endpointLogicalGroupClassModel = ClassModel
- .builder(EndpointLogicalGroup.class).enableDiscriminator(true).build();
- ClassModel testRolesClassModel = ClassModel
- .builder(TestRoles.class).enableDiscriminator(true).build();
- ClassModel logicalGroupTestingEndpointClassModel = ClassModel
- .builder(LogicalGroupTestingEndpoint.class).enableDiscriminator(true).build();
- ClassModel customAuthTypeModel = ClassModel
- .builder(CustomAuthType.class).enableDiscriminator(true).build();
- ClassModel containsPredicateClassModel = ClassModel
- .builder(ContainsPredicate.class).enableDiscriminator(true).build();
- ClassModel notBelongsToPredicateClassModel = ClassModel
- .builder(NotBelongsToPredicate.class).enableDiscriminator(true).build();
- ClassModel belongsToPredicateClassModel = ClassModel
- .builder(BelongsToPredicate.class).enableDiscriminator(true).build();
- ClassModel yamlNodeDetails = ClassModel
- .builder(YamlNodeDetails.class).enableDiscriminator(true).build();
- ClassModel unauthenticatedEndpointsClassModel = ClassModel
- .builder(UnauthenticatedEndpoint.class).enableDiscriminator(true).build();
- ClassModel eventsExampleClassModel = ClassModel
- .builder(EventsExample.class).enableDiscriminator(true).build();
- // ClassModel awsResourceModel =
- // ClassModel.builder(AwsResource.class).enableDiscriminator(true)
- // .build();
- ClassModel awsResourcesModel = ClassModel.builder(AwsResources.class).enableDiscriminator(true)
- .build();
- ClassModel AktoDataTypeClassModel = ClassModel.builder(AktoDataType.class).enableDiscriminator(true).build();
- ClassModel testInfoClassModel = ClassModel.builder(TestInfo.class).enableDiscriminator(true).build();
- ClassModel bflaTestInfoClassModel = ClassModel.builder(BFLATestInfo.class).enableDiscriminator(true).build();
- ClassModel accessMatrixUrlToRoleClassModel = ClassModel.builder(AccessMatrixUrlToRole.class).enableDiscriminator(true).build();
- ClassModel accessMatrixTaskInfoClassModel = ClassModel.builder(AccessMatrixTaskInfo.class).enableDiscriminator(true).build();
- ClassModel loaderClassModel = ClassModel.builder(Loader.class).enableDiscriminator(true).build();
- ClassModel normalLoaderClassModel = ClassModel.builder(NormalLoader.class).enableDiscriminator(true).build();
- ClassModel postmanUploadLoaderClassModel = ClassModel.builder(PostmanUploadLoader.class).enableDiscriminator(true).build();
- ClassModel aktoGptConfigClassModel = ClassModel.builder(AktoGptConfig.class).enableDiscriminator(true).build();
+ public static CodecRegistry createCodecRegistry() {
+ ClassModel configClassModel =
+ ClassModel.builder(Config.class).enableDiscriminator(true).build();
+ ClassModel signupInfoClassModel =
+ ClassModel.builder(SignupInfo.class).enableDiscriminator(true).build();
+ ClassModel apiAuthClassModel =
+ ClassModel.builder(APIAuth.class).enableDiscriminator(true).build();
+ ClassModel attempResultModel =
+ ClassModel.builder(AttemptResult.class).enableDiscriminator(true).build();
+ ClassModel urlTemplateModel =
+ ClassModel.builder(URLTemplate.class).enableDiscriminator(true).build();
+ ClassModel pendingInviteCodeClassModel =
+ ClassModel.builder(PendingInviteCode.class).enableDiscriminator(true).build();
+ ClassModel rbacClassModel =
+ ClassModel.builder(RBAC.class).enableDiscriminator(true).build();
+ ClassModel singleTypeInfoClassModel =
+ ClassModel.builder(SingleTypeInfo.class).enableDiscriminator(true).build();
+ ClassModel kafkaHealthMetricClassModel =
+ ClassModel.builder(KafkaHealthMetric.class).enableDiscriminator(true).build();
+ ClassModel thirdPartyAccessClassModel =
+ ClassModel.builder(ThirdPartyAccess.class).enableDiscriminator(true).build();
+ ClassModel credentialClassModel =
+ ClassModel.builder(Credential.class).enableDiscriminator(true).build();
+ ClassModel apiTokenClassModel =
+ ClassModel.builder(ApiToken.class).enableDiscriminator(true).build();
+ ClassModel apiInfoClassModel =
+ ClassModel.builder(ApiInfo.class).enableDiscriminator(true).build();
+ ClassModel apiInfoKeyClassModel =
+ ClassModel.builder(ApiInfo.ApiInfoKey.class).enableDiscriminator(true).build();
+ ClassModel customFilterClassModel =
+ ClassModel.builder(CustomFilter.class).enableDiscriminator(true).build();
+ ClassModel fieldExistsFilterClassModel =
+ ClassModel.builder(FieldExistsFilter.class).enableDiscriminator(true).build();
+ ClassModel responseCodeRuntimeFilterClassModel =
+ ClassModel.builder(ResponseCodeRuntimeFilter.class).enableDiscriminator(true).build();
+ ;
+ ClassModel runtimeFilterClassModel =
+ ClassModel.builder(RuntimeFilter.class).enableDiscriminator(true).build();
+ ClassModel filterSampleDataClassModel =
+ ClassModel.builder(FilterSampleData.class).enableDiscriminator(true).build();
+ ClassModel accountSettingsClassModel =
+ ClassModel.builder(AccountSettings.class).enableDiscriminator(true).build();
+ ClassModel predicateClassModel =
+ ClassModel.builder(Predicate.class).enableDiscriminator(true).build();
+ ClassModel regexPredicateClassModel =
+ ClassModel.builder(RegexPredicate.class).enableDiscriminator(true).build();
+ ClassModel startsWithPredicateClassModel =
+ ClassModel.builder(StartsWithPredicate.class).enableDiscriminator(true).build();
+ ClassModel endsWithPredicateClassModel =
+ ClassModel.builder(EndsWithPredicate.class).enableDiscriminator(true).build();
+ ClassModel equalsToPredicateClassModel =
+ ClassModel.builder(EqualsToPredicate.class).enableDiscriminator(true).build();
+ ClassModel isNumberPredicateClassModel =
+ ClassModel.builder(IsNumberPredicate.class).enableDiscriminator(true).build();
+ ClassModel conditionsClassModel =
+ ClassModel.builder(Conditions.class).enableDiscriminator(true).build();
+ ClassModel cappedListClassModel =
+ ClassModel.builder(CappedList.class).enableDiscriminator(true).build();
+ ClassModel testingRunClassModel =
+ ClassModel.builder(TestingRun.class).enableDiscriminator(true).build();
+ ClassModel testingRunResultClassModel =
+ ClassModel.builder(TestingRunResult.class).enableDiscriminator(true).build();
+ ClassModel testResultClassModel =
+ ClassModel.builder(TestResult.class).enableDiscriminator(true).build();
+ ClassModel multiExecTestResultClassModel =
+ ClassModel.builder(MultiExecTestResult.class).enableDiscriminator(true).build();
+ ClassModel genericTestResultClassModel =
+ ClassModel.builder(GenericTestResult.class).enableDiscriminator(true).build();
+ ClassModel authMechanismClassModel =
+ ClassModel.builder(AuthMechanism.class).enableDiscriminator(true).build();
+ ClassModel setupClassModel =
+ ClassModel.builder(Setup.class).enableDiscriminator(true).build();
+ ClassModel authParamClassModel =
+ ClassModel.builder(AuthParam.class).enableDiscriminator(true).build();
+ ClassModel hardcodedAuthParamClassModel =
+ ClassModel.builder(HardcodedAuthParam.class).enableDiscriminator(true).build();
+ ClassModel loginReqAuthParamClassModel =
+ ClassModel.builder(LoginRequestAuthParam.class).enableDiscriminator(true).build();
+ ClassModel testingEndpointsClassModel =
+ ClassModel.builder(TestingEndpoints.class).enableDiscriminator(true).build();
+ ClassModel customTestingEndpointsClassModel =
+ ClassModel.builder(CustomTestingEndpoints.class).enableDiscriminator(true).build();
+ ClassModel allTestingEndpointsClassModel =
+ ClassModel.builder(AllTestingEndpoints.class).enableDiscriminator(true).build();
+ ClassModel collectionWiseTestingEndpointsClassModel =
+ ClassModel.builder(CollectionWiseTestingEndpoints.class).enableDiscriminator(true).build();
+ ClassModel workflowTestingEndpointsClassModel =
+ ClassModel.builder(WorkflowTestingEndpoints.class).enableDiscriminator(true).build();
+ ClassModel workflowTestResultClassModel =
+ ClassModel.builder(WorkflowTestResult.class).enableDiscriminator(true).build();
+ ClassModel workflowTestClassModel =
+ ClassModel.builder(WorkflowTest.class).enableDiscriminator(true).build();
+ ClassModel cappedSetClassModel =
+ ClassModel.builder(CappedSet.class).enableDiscriminator(true).build();
+ ClassModel CustomWebhookClassModel =
+ ClassModel.builder(CustomWebhook.class).enableDiscriminator(true).build();
+ ClassModel WorkflowNodeDetailsClassModel =
+ ClassModel.builder(WorkflowNodeDetails.class).enableDiscriminator(true).build();
+ ClassModel CustomWebhookResultClassModel =
+ ClassModel.builder(CustomWebhookResult.class).enableDiscriminator(true).build();
+ ClassModel nodeResultClassModel =
+ ClassModel.builder(WorkflowTestResult.NodeResult.class).enableDiscriminator(true).build();
+ ClassModel testingRunIssuesClassModel =
+ ClassModel.builder(TestingRunIssues.class).enableDiscriminator(true).build();
+ ClassModel testingIssuesIdClassModel =
+ ClassModel.builder(TestingIssuesId.class).enableDiscriminator(true).build();
+ ClassModel testSourceConfigClassModel =
+ ClassModel.builder(TestSourceConfig.class).enableDiscriminator(true).build();
+ ClassModel endpointLogicalGroupClassModel =
+ ClassModel.builder(EndpointLogicalGroup.class).enableDiscriminator(true).build();
+ ClassModel testRolesClassModel =
+ ClassModel.builder(TestRoles.class).enableDiscriminator(true).build();
+ ClassModel logicalGroupTestingEndpointClassModel =
+ ClassModel.builder(LogicalGroupTestingEndpoint.class).enableDiscriminator(true).build();
+ ClassModel customAuthTypeModel =
+ ClassModel.builder(CustomAuthType.class).enableDiscriminator(true).build();
+ ClassModel containsPredicateClassModel =
+ ClassModel.builder(ContainsPredicate.class).enableDiscriminator(true).build();
+ ClassModel notBelongsToPredicateClassModel =
+ ClassModel.builder(NotBelongsToPredicate.class).enableDiscriminator(true).build();
+ ClassModel belongsToPredicateClassModel =
+ ClassModel.builder(BelongsToPredicate.class).enableDiscriminator(true).build();
+ ClassModel yamlNodeDetails =
+ ClassModel.builder(YamlNodeDetails.class).enableDiscriminator(true).build();
+ ClassModel unauthenticatedEndpointsClassModel =
+ ClassModel.builder(UnauthenticatedEndpoint.class).enableDiscriminator(true).build();
+ ClassModel eventsExampleClassModel =
+ ClassModel.builder(EventsExample.class).enableDiscriminator(true).build();
+ // ClassModel awsResourceModel =
+ // ClassModel.builder(AwsResource.class).enableDiscriminator(true)
+ // .build();
+ ClassModel awsResourcesModel =
+ ClassModel.builder(AwsResources.class).enableDiscriminator(true).build();
+ ClassModel AktoDataTypeClassModel =
+ ClassModel.builder(AktoDataType.class).enableDiscriminator(true).build();
+ ClassModel testInfoClassModel =
+ ClassModel.builder(TestInfo.class).enableDiscriminator(true).build();
+ ClassModel bflaTestInfoClassModel =
+ ClassModel.builder(BFLATestInfo.class).enableDiscriminator(true).build();
+ ClassModel accessMatrixUrlToRoleClassModel =
+ ClassModel.builder(AccessMatrixUrlToRole.class).enableDiscriminator(true).build();
+ ClassModel accessMatrixTaskInfoClassModel =
+ ClassModel.builder(AccessMatrixTaskInfo.class).enableDiscriminator(true).build();
+ ClassModel loaderClassModel =
+ ClassModel.builder(Loader.class).enableDiscriminator(true).build();
+ ClassModel normalLoaderClassModel =
+ ClassModel.builder(NormalLoader.class).enableDiscriminator(true).build();
+ ClassModel postmanUploadLoaderClassModel =
+ ClassModel.builder(PostmanUploadLoader.class).enableDiscriminator(true).build();
+ ClassModel aktoGptConfigClassModel =
+ ClassModel.builder(AktoGptConfig.class).enableDiscriminator(true).build();
- ClassModel loginFlowStepsData = ClassModel.builder(LoginFlowStepsData.class)
- .enableDiscriminator(true).build();
- ClassModel vulnerableRequestForTemplateClassModel = ClassModel.builder(VulnerableRequestForTemplate.class).enableDiscriminator(true).build();
- ClassModel trafficMetricsAlertClassModel = ClassModel.builder(TrafficMetricsAlert.class).enableDiscriminator(true).build();
- ClassModel jiraintegrationClassModel = ClassModel.builder(JiraIntegration.class).enableDiscriminator(true).build();
- ClassModel methodConditionClassModel = ClassModel.builder(MethodCondition.class).enableDiscriminator(true).build();
- ClassModel regexTestingEndpointsClassModel = ClassModel.builder(RegexTestingEndpoints.class).enableDiscriminator(true).build();
- ClassModel hostRegexTestingEndpointsClassModel = ClassModel.builder(HostRegexTestingEndpoints.class).enableDiscriminator(true).build();
- ClassModel dependencyNodeClassModel = ClassModel.builder(DependencyNode.class).enableDiscriminator(true).build();
- ClassModel paramInfoClassModel = ClassModel.builder(ParamInfo.class).enableDiscriminator(true).build();
- ClassModel nodeClassModel = ClassModel.builder(Node.class).enableDiscriminator(true).build();
- ClassModel connectionClassModel = ClassModel.builder(Connection.class).enableDiscriminator(true).build();
- ClassModel edgeClassModel = ClassModel.builder(Edge.class).enableDiscriminator(true).build();
- ClassModel cronTimersClassModel = ClassModel.builder(LastCronRunInfo.class)
- .enableDiscriminator(true).build();
- ClassModel connectionInfoClassModel = ClassModel.builder(ConnectionInfo.class)
- .enableDiscriminator(true).build();
- ClassModel testLibraryClassModel = ClassModel.builder(TestLibrary.class).enableDiscriminator(true).build();
+ ClassModel loginFlowStepsData =
+ ClassModel.builder(LoginFlowStepsData.class).enableDiscriminator(true).build();
+ ClassModel vulnerableRequestForTemplateClassModel =
+ ClassModel.builder(VulnerableRequestForTemplate.class).enableDiscriminator(true).build();
+ ClassModel trafficMetricsAlertClassModel =
+ ClassModel.builder(TrafficMetricsAlert.class).enableDiscriminator(true).build();
+ ClassModel