From 728b8789e6482ec9dc153b8daa5646bf70873d68 Mon Sep 17 00:00:00 2001 From: Ajinkya Date: Fri, 3 Jan 2025 15:53:03 +0530 Subject: [PATCH] added dashboard for threat detection --- .github/workflows/prod.yml | 5 + .github/workflows/staging.yml | 5 + .gitignore | 1 + Makefile | 8 + apps/dashboard/pom.xml | 14 +- .../AbstractThreatDetectionAction.java | 32 ++ .../DashboardMaliciousEvent.java | 111 ++++++ .../DashboardThreatActor.java | 77 ++++ .../threat_detection/DashboardThreatApi.java | 61 +++ .../SuspectSampleDataAction.java | 377 ++++++++++-------- .../threat_detection/ThreatActorAction.java | 154 +++++++ .../ThreatActorPerCountry.java | 27 ++ .../threat_detection/ThreatApiAction.java | 164 ++++++++ .../threat_detection/ThreatCategoryCount.java | 37 ++ apps/dashboard/src/main/resources/struts.xml | 102 ++++- .../threat_detection/ThreatActorPage.jsx | 147 +++++++ .../pages/threat_detection/ThreatApiPage.jsx | 111 ++++++ .../dashboard/pages/threat_detection/api.js | 32 ++ .../components/SusDataTable.jsx | 355 ++++++++++------- .../components/ThreatActorsTable.jsx | 102 +++++ .../components/ThreatApiSubcategoryCount.jsx | 25 ++ .../components/ThreatApisTable.jsx | 104 +++++ .../components/ThreatWorldMap.jsx | 86 ++++ .../components/TopThreatTypeChart.jsx | 43 ++ .../web/polaris_web/web/src/apps/main/App.js | 10 + apps/pom.xml | 20 +- buf.gen.yaml | 18 + libs/pom.xml | 1 + libs/protobuf/.gitignore | 2 + libs/protobuf/pom.xml | 76 ++++ .../java/com/akto/grpc/auth/AuthToken.java | 51 +++ .../akto/proto/utils/ProtoMessageUtils.java | 27 ++ protobuf/buf.yaml | 7 + .../event_type/v1/event_type.proto | 12 + .../message/malicious_event/v1/message.proto | 28 ++ .../message/sample_request/v1/message.proto | 22 + .../dashboard_service/v1/service.proto | 106 +++++ .../malicious_alert_service/v1/service.proto | 19 + scripts/proto-gen.sh | 12 + 39 files changed, 2242 insertions(+), 349 deletions(-) create mode 100644 Makefile create mode 100644 apps/dashboard/src/main/java/com/akto/action/threat_detection/AbstractThreatDetectionAction.java create mode 100644 apps/dashboard/src/main/java/com/akto/action/threat_detection/DashboardMaliciousEvent.java create mode 100644 apps/dashboard/src/main/java/com/akto/action/threat_detection/DashboardThreatActor.java create mode 100644 apps/dashboard/src/main/java/com/akto/action/threat_detection/DashboardThreatApi.java create mode 100644 apps/dashboard/src/main/java/com/akto/action/threat_detection/ThreatActorAction.java create mode 100644 apps/dashboard/src/main/java/com/akto/action/threat_detection/ThreatActorPerCountry.java create mode 100644 apps/dashboard/src/main/java/com/akto/action/threat_detection/ThreatApiAction.java create mode 100644 apps/dashboard/src/main/java/com/akto/action/threat_detection/ThreatCategoryCount.java create mode 100644 apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatActorPage.jsx create mode 100644 apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatApiPage.jsx create mode 100644 apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/ThreatActorsTable.jsx create mode 100644 apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/ThreatApiSubcategoryCount.jsx create mode 100644 apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/ThreatApisTable.jsx create mode 100644 apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/ThreatWorldMap.jsx create mode 100644 apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/TopThreatTypeChart.jsx create mode 100644 buf.gen.yaml create mode 100644 libs/protobuf/.gitignore create mode 100644 libs/protobuf/pom.xml create mode 100644 libs/protobuf/src/main/java/com/akto/grpc/auth/AuthToken.java create mode 100644 libs/protobuf/src/main/java/com/akto/proto/utils/ProtoMessageUtils.java create mode 100644 protobuf/buf.yaml create mode 100644 protobuf/threat_detection/message/malicious_event/event_type/v1/event_type.proto create mode 100644 protobuf/threat_detection/message/malicious_event/v1/message.proto create mode 100644 protobuf/threat_detection/message/sample_request/v1/message.proto create mode 100644 protobuf/threat_detection/service/dashboard_service/v1/service.proto create mode 100644 protobuf/threat_detection/service/malicious_alert_service/v1/service.proto create mode 100644 scripts/proto-gen.sh diff --git a/.github/workflows/prod.yml b/.github/workflows/prod.yml index c0aa1ecfe2..e339e91bd9 100644 --- a/.github/workflows/prod.yml +++ b/.github/workflows/prod.yml @@ -47,6 +47,11 @@ jobs: - uses: actions/setup-node@v2 with: node-version: '17' + - uses: bufbuild/buf-action@v1 + with: + setup_only: true + - name: Generate Proto files + run: make proto-gen - name: Download Akto templates zip and PII files working-directory: ./apps/dashboard/src/main/resources run: | diff --git a/.github/workflows/staging.yml b/.github/workflows/staging.yml index 02920d5ba9..4f3fbe6e2c 100644 --- a/.github/workflows/staging.yml +++ b/.github/workflows/staging.yml @@ -26,6 +26,11 @@ jobs: - uses: actions/setup-node@v2 with: node-version: "17" + - uses: bufbuild/buf-action@v1 + with: + setup_only: true + - name: Generate Proto files + run: make proto-gen - name: Convert github branch name to be compatible with docker tag name convention and generate tag name id: docker_tag run: echo "IMAGE_TAG=a-$(echo ${{ github.ref_name }} | sed 's/[^a-zA-Z0-9]/-/g')" >> $GITHUB_OUTPUT diff --git a/.gitignore b/.gitignore index 685da2accc..d90e415a62 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ https: **/data-zoo-data **/data-zoo-logs **/bin +.factorypath diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000..b79b0909a1 --- /dev/null +++ b/Makefile @@ -0,0 +1,8 @@ +proto-gen: + sh ./scripts/proto-gen.sh + +build: proto-gen + mvn install -DskipTests + +build-clean: proto-gen + mvn clean install -DskipTests \ No newline at end of file diff --git a/apps/dashboard/pom.xml b/apps/dashboard/pom.xml index c7966302b5..6cbf9bd3ee 100644 --- a/apps/dashboard/pom.xml +++ b/apps/dashboard/pom.xml @@ -1,5 +1,6 @@ - + 4.0.0 @@ -25,7 +26,7 @@ - + com.amazonaws aws-java-sdk-lambda 1.12.405 @@ -109,6 +110,11 @@ dao ${project.version} + + com.akto.libs.protobuf + protobuf + ${project.version} + com.akto.libs.utils utils @@ -294,4 +300,4 @@ ${project.artifactId} - + \ No newline at end of file diff --git a/apps/dashboard/src/main/java/com/akto/action/threat_detection/AbstractThreatDetectionAction.java b/apps/dashboard/src/main/java/com/akto/action/threat_detection/AbstractThreatDetectionAction.java new file mode 100644 index 0000000000..3d2c78cc0b --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/threat_detection/AbstractThreatDetectionAction.java @@ -0,0 +1,32 @@ +package com.akto.action.threat_detection; + +import com.akto.action.UserAction; +import com.akto.dao.context.Context; +import com.akto.database_abstractor_authenticator.JwtAuthenticator; +import java.util.Calendar; +import java.util.HashMap; +import java.util.Map; + +public class AbstractThreatDetectionAction extends UserAction { + + private Map tokens = new HashMap<>(); + + public String getApiToken() { + try { + int accountId = Context.accountId.get(); + if (tokens.containsKey(accountId)) { + return tokens.get(accountId); + } + + Map claims = new HashMap<>(); + claims.put("accountId", accountId); + String token = JwtAuthenticator.createJWT(claims, "Akto", "access_tbs", Calendar.MINUTE, 1); + tokens.put(accountId, token); + + return token; + } catch (Exception e) { + System.out.println(e); + return ""; + } + } +} diff --git a/apps/dashboard/src/main/java/com/akto/action/threat_detection/DashboardMaliciousEvent.java b/apps/dashboard/src/main/java/com/akto/action/threat_detection/DashboardMaliciousEvent.java new file mode 100644 index 0000000000..d63268b448 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/threat_detection/DashboardMaliciousEvent.java @@ -0,0 +1,111 @@ +package com.akto.action.threat_detection; + +import com.akto.dto.type.URLMethods; +import com.akto.dto.type.URLMethods.Method; + +public class DashboardMaliciousEvent { + private String id; + private String actor; + private String filter_id; + private String url; + private URLMethods.Method method; + private int apiCollectionId; + private String ip; + private String country; + private long timestamp; + + public DashboardMaliciousEvent() {} + + public DashboardMaliciousEvent( + String id, + String actor, + String filter, + String url, + Method method, + int apiCollectionId, + String ip, + String country, + long timestamp) { + this.id = id; + this.actor = actor; + this.filter_id = filter; + this.url = url; + this.method = method; + this.apiCollectionId = apiCollectionId; + this.ip = ip; + this.country = country; + this.timestamp = timestamp; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getActor() { + return actor; + } + + public void setActor(String actor) { + this.actor = actor; + } + + public String getFilterId() { + return filter_id; + } + + public void setFilterId(String filter) { + this.filter_id = filter; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public URLMethods.Method getMethod() { + return method; + } + + public void setMethod(URLMethods.Method method) { + this.method = method; + } + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public String getCountry() { + return country; + } + + public void setCountry(String country) { + this.country = country; + } + + public long getTimestamp() { + return timestamp; + } + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } + + public int getApiCollectionId() { + return apiCollectionId; + } + + public void setApiCollectionId(int apiCollectionId) { + this.apiCollectionId = apiCollectionId; + } +} diff --git a/apps/dashboard/src/main/java/com/akto/action/threat_detection/DashboardThreatActor.java b/apps/dashboard/src/main/java/com/akto/action/threat_detection/DashboardThreatActor.java new file mode 100644 index 0000000000..5b7c3a7d04 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/threat_detection/DashboardThreatActor.java @@ -0,0 +1,77 @@ +package com.akto.action.threat_detection; + +import com.akto.dto.type.URLMethods.Method; + +public class DashboardThreatActor { + + private String id; + private String latestApiEndpoint; + private String latestApiIp; + private Method latestApiMethod; + private long discoveredAt; + private String country; + + public DashboardThreatActor( + String id, + String latestApiEndpoint, + String latestApiIp, + Method latestApiMethod, + long discoveredAt, + String country) { + + this.id = id; + this.latestApiEndpoint = latestApiEndpoint; + this.latestApiIp = latestApiIp; + this.latestApiMethod = latestApiMethod; + this.discoveredAt = discoveredAt; + this.country = country; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getLatestApiEndpoint() { + return latestApiEndpoint; + } + + public void setLatestApiEndpoint(String latestApiEndpoint) { + this.latestApiEndpoint = latestApiEndpoint; + } + + public String getLatestApiIp() { + return latestApiIp; + } + + public void setLatestApiIp(String latestApiIp) { + this.latestApiIp = latestApiIp; + } + + public Method getLatestApiMethod() { + return latestApiMethod; + } + + public void setLatestApiMethod(Method latestApiMethod) { + this.latestApiMethod = latestApiMethod; + } + + public long getDiscoveredAt() { + return discoveredAt; + } + + public void setDiscoveredAt(long discoveredAt) { + this.discoveredAt = discoveredAt; + } + + public String getCountry() { + return country; + } + + public void setCountry(String country) { + this.country = country; + } +} diff --git a/apps/dashboard/src/main/java/com/akto/action/threat_detection/DashboardThreatApi.java b/apps/dashboard/src/main/java/com/akto/action/threat_detection/DashboardThreatApi.java new file mode 100644 index 0000000000..9ebe40965b --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/threat_detection/DashboardThreatApi.java @@ -0,0 +1,61 @@ +package com.akto.action.threat_detection; + +import com.akto.dto.type.URLMethods; + +public class DashboardThreatApi { + + private String api; + private URLMethods.Method method; + private int actorsCount; + private int requestsCount; + private long discoveredAt; + + public DashboardThreatApi( + String api, URLMethods.Method method, int actorsCount, int requestsCount, long discoveredAt) { + this.api = api; + this.method = method; + this.actorsCount = actorsCount; + this.requestsCount = requestsCount; + this.discoveredAt = discoveredAt; + } + + public String getApi() { + return api; + } + + public void setApi(String api) { + this.api = api; + } + + public URLMethods.Method getMethod() { + return method; + } + + public void setMethod(URLMethods.Method method) { + this.method = method; + } + + public int getActorsCount() { + return actorsCount; + } + + public void setActorsCount(int actorsCount) { + this.actorsCount = actorsCount; + } + + public int getRequestsCount() { + return requestsCount; + } + + public void setRequestsCount(int requestsCount) { + this.requestsCount = requestsCount; + } + + public long getDiscoveredAt() { + return discoveredAt; + } + + public void setDiscoveredAt(long discoveredAt) { + this.discoveredAt = discoveredAt; + } +} diff --git a/apps/dashboard/src/main/java/com/akto/action/threat_detection/SuspectSampleDataAction.java b/apps/dashboard/src/main/java/com/akto/action/threat_detection/SuspectSampleDataAction.java index d271463da8..473768bdb8 100644 --- a/apps/dashboard/src/main/java/com/akto/action/threat_detection/SuspectSampleDataAction.java +++ b/apps/dashboard/src/main/java/com/akto/action/threat_detection/SuspectSampleDataAction.java @@ -1,184 +1,209 @@ package com.akto.action.threat_detection; -import java.util.ArrayList; -import java.util.HashSet; +import com.akto.dto.traffic.SuspectSampleData; +import com.akto.dto.type.URLMethods; +import com.akto.proto.generated.threat_detection.service.dashboard_service.v1.FetchAlertFiltersResponse; +import com.akto.proto.generated.threat_detection.service.dashboard_service.v1.ListMaliciousRequestsResponse; +import com.akto.proto.utils.ProtoMessageUtils; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import org.bson.conversions.Bson; - -import com.akto.action.UserAction; -import com.akto.dao.SuspectSampleDataDao; -import com.akto.dao.context.Context; -import com.akto.dto.traffic.SuspectSampleData; -import com.akto.util.Constants; -import com.mongodb.client.model.Filters; -import com.mongodb.client.model.Sorts; - -public class SuspectSampleDataAction extends UserAction { - - List sampleData; - int skip; - static final int LIMIT = 50; - List ips; - List urls; - List apiCollectionIds; - long total; - Map sort; - int startTimestamp, endTimestamp; - - public String fetchSuspectSampleData() { - - List filterList = new ArrayList<>(); - - /* - * In case time filters are empty, - * using default filter as 2 months. - */ - - if (startTimestamp <= 0) { - startTimestamp = Context.now() - 2 * 30 * 24 * 60 * 60; - } - if (endTimestamp <= 0) { - endTimestamp = Context.now() + 10 * 60; - } - - filterList.add(Filters.gte(SuspectSampleData._DISCOVERED, startTimestamp)); - filterList.add(Filters.lte(SuspectSampleData._DISCOVERED, endTimestamp)); - - if (ips != null && !ips.isEmpty()) { - filterList.add(Filters.in(SuspectSampleData.SOURCE_IPS, ips)); - } - if (urls != null && !urls.isEmpty()) { - filterList.add(Filters.in(SuspectSampleData.MATCHING_URL, urls)); - } - if (apiCollectionIds != null && !apiCollectionIds.isEmpty()) { - filterList.add(Filters.in(SuspectSampleData.API_COLLECTION_ID, apiCollectionIds)); - } - - Bson finalFilter = Filters.empty(); - - if (!filterList.isEmpty()) { - finalFilter = Filters.and(filterList); - } - - String sortKey = SuspectSampleData._DISCOVERED; - int sortDirection = -1; - /* - * add any new sort key here, - * for validation and sanity. - */ - Set sortKeys = new HashSet<>(); - sortKeys.add(SuspectSampleData._DISCOVERED); - - if (sort != null && !sort.isEmpty()) { - Entry sortEntry = sort.entrySet().iterator().next(); - sortKey = sortEntry.getKey(); - if (!sortKeys.contains(sortKey)) { - sortKey = SuspectSampleData._DISCOVERED; - } - sortDirection = sortEntry.getValue(); - if (!(sortDirection == -1 || sortDirection == 1)) { - sortDirection = -1; - } - } - - /* - * In case timestamp is same, then id acts as tie-breaker, - * to avoid repeating the same documents again. - */ - Bson sort = sortDirection == -1 ? Sorts.descending(sortKey, Constants.ID) - : Sorts.ascending(sortKey, Constants.ID); - sampleData = SuspectSampleDataDao.instance.findAll(finalFilter, skip, LIMIT, sort); - total = SuspectSampleDataDao.instance.count(finalFilter); - - return SUCCESS.toUpperCase(); - } - - public String fetchFilters() { - ips = new ArrayList<>( - SuspectSampleDataDao.instance.findDistinctFields(SuspectSampleData.SOURCE_IPS, String.class, Filters.empty())); - urls = new ArrayList<>(SuspectSampleDataDao.instance.findDistinctFields(SuspectSampleData.MATCHING_URL, String.class, - Filters.empty())); - return SUCCESS.toUpperCase(); - } - - public List getSampleData() { - return sampleData; - } - - public void setSampleData(List sampleData) { - this.sampleData = sampleData; - } +import java.util.stream.Collectors; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +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.http.util.EntityUtils; + +public class SuspectSampleDataAction extends AbstractThreatDetectionAction { + + List sampleData; + List maliciousEvents; + int skip; + static final int LIMIT = 50; + List ips; + List urls; + List apiCollectionIds; + long total; + Map sort; + int startTimestamp, endTimestamp; + + private final CloseableHttpClient httpClient; + private final String backendUrl; + + private final ObjectMapper objectMapper = new ObjectMapper(); + + public SuspectSampleDataAction() { + super(); + this.httpClient = HttpClients.createDefault(); + this.backendUrl = System.getenv("THREAT_DETECTION_BACKEND_URL"); + } + + public String fetchSampleData() { + HttpPost post = + new HttpPost(String.format("%s/api/dashboard/list_malicious_requests", backendUrl)); + post.addHeader("Authorization", "Bearer " + this.getApiToken()); + post.addHeader("Content-Type", "application/json"); + + System.out.print("API Token: " + this.getApiToken()); + + Map body = + new HashMap() { + { + put("skip", skip); + put("limit", LIMIT); + } + }; + String msg = objectMapper.valueToTree(body).toString(); + + System.out.println("Request body for list malicious requests" + msg); + + StringEntity requestEntity = new StringEntity(msg, ContentType.APPLICATION_JSON); + post.setEntity(requestEntity); + + try (CloseableHttpResponse resp = this.httpClient.execute(post)) { + String responseBody = EntityUtils.toString(resp.getEntity()); + + System.out.println(responseBody); + + ProtoMessageUtils.toProtoMessage( + ListMaliciousRequestsResponse.class, responseBody) + .ifPresent( + m -> { + this.maliciousEvents = + m.getMaliciousEventsList().stream() + .map( + smr -> + new DashboardMaliciousEvent( + smr.getId(), + smr.getActor(), + smr.getFilterId(), + smr.getEndpoint(), + URLMethods.Method.fromString(smr.getMethod()), + smr.getApiCollectionId(), + smr.getIp(), + smr.getCountry(), + smr.getDetectedAt())) + .collect(Collectors.toList()); + }); + } catch (Exception e) { + e.printStackTrace(); + return ERROR.toUpperCase(); + } + + return SUCCESS.toUpperCase(); + } + + public String fetchFilters() { + HttpGet get = new HttpGet(String.format("%s/api/dashboard/fetch_filters", backendUrl)); + get.addHeader("Authorization", "Bearer " + this.getApiToken()); + get.addHeader("Content-Type", "application/json"); + + try (CloseableHttpResponse resp = this.httpClient.execute(get)) { + String responseBody = EntityUtils.toString(resp.getEntity()); + + System.out.println(responseBody); + + ProtoMessageUtils.toProtoMessage( + FetchAlertFiltersResponse.class, responseBody) + .ifPresent( + msg -> { + this.ips = msg.getActorsList(); + this.urls = msg.getUrlsList(); + }); + } catch (Exception e) { + e.printStackTrace(); + return ERROR.toUpperCase(); + } + + return SUCCESS.toUpperCase(); + } + + public List getSampleData() { + return sampleData; + } + + public void setSampleData(List sampleData) { + this.sampleData = sampleData; + } + + public int getSkip() { + return skip; + } + + public void setSkip(int skip) { + this.skip = skip; + } + + public static int getLimit() { + return LIMIT; + } + + public List getIps() { + return ips; + } + + public void setIps(List ips) { + this.ips = ips; + } + + public List getUrls() { + return urls; + } + + public void setUrls(List urls) { + this.urls = urls; + } + + public List getApiCollectionIds() { + return apiCollectionIds; + } + + public void setApiCollectionIds(List apiCollectionIds) { + this.apiCollectionIds = apiCollectionIds; + } + + public long getTotal() { + return total; + } + + public void setTotal(long total) { + this.total = total; + } + + public Map getSort() { + return sort; + } + + public void setSort(Map sort) { + this.sort = sort; + } + + public int getStartTimestamp() { + return startTimestamp; + } + + public void setStartTimestamp(int startTimestamp) { + this.startTimestamp = startTimestamp; + } - public int getSkip() { - return skip; - } - - public void setSkip(int skip) { - this.skip = skip; - } - - public static int getLimit() { - return LIMIT; - } - - public List getIps() { - return ips; - } - - public void setIps(List ips) { - this.ips = ips; - } - - public List getUrls() { - return urls; - } + public int getEndTimestamp() { + return endTimestamp; + } - public void setUrls(List urls) { - this.urls = urls; - } - - public List getApiCollectionIds() { - return apiCollectionIds; - } - - public void setApiCollectionIds(List apiCollectionIds) { - this.apiCollectionIds = apiCollectionIds; - } - - public long getTotal() { - return total; - } - - public void setTotal(long total) { - this.total = total; - } - - public Map getSort() { - return sort; - } - - public void setSort(Map sort) { - this.sort = sort; - } - - public int getStartTimestamp() { - return startTimestamp; - } - - public void setStartTimestamp(int startTimestamp) { - this.startTimestamp = startTimestamp; - } - - public int getEndTimestamp() { - return endTimestamp; - } - - public void setEndTimestamp(int endTimestamp) { - this.endTimestamp = endTimestamp; - } + public void setEndTimestamp(int endTimestamp) { + this.endTimestamp = endTimestamp; + } + + public List getMaliciousEvents() { + return maliciousEvents; + } + public void setMaliciousEvents(List maliciousRequests) { + this.maliciousEvents = maliciousRequests; + } } diff --git a/apps/dashboard/src/main/java/com/akto/action/threat_detection/ThreatActorAction.java b/apps/dashboard/src/main/java/com/akto/action/threat_detection/ThreatActorAction.java new file mode 100644 index 0000000000..b169e934e3 --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/threat_detection/ThreatActorAction.java @@ -0,0 +1,154 @@ +package com.akto.action.threat_detection; + +import com.akto.dto.type.URLMethods; +import com.akto.proto.generated.threat_detection.service.dashboard_service.v1.ListThreatActorResponse; +import com.akto.proto.generated.threat_detection.service.dashboard_service.v1.ThreatActorByCountryResponse; +import com.akto.proto.utils.ProtoMessageUtils; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +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.http.util.EntityUtils; + +public class ThreatActorAction extends AbstractThreatDetectionAction { + + List actors; + List actorsCountPerCountry; + int skip; + static final int LIMIT = 50; + long total; + Map sort; + int startTimestamp, endTimestamp; + + private final CloseableHttpClient httpClient; + private final String backendUrl; + + private final ObjectMapper objectMapper = new ObjectMapper(); + + public ThreatActorAction() { + super(); + this.httpClient = HttpClients.createDefault(); + this.backendUrl = System.getenv("THREAT_DETECTION_BACKEND_URL"); + } + + public String getActorsCountPerCounty() { + HttpGet get = + new HttpGet(String.format("%s/api/dashboard/get_actors_count_per_country", backendUrl)); + get.addHeader("Authorization", "Bearer " + this.getApiToken()); + get.addHeader("Content-Type", "application/json"); + + try (CloseableHttpResponse resp = this.httpClient.execute(get)) { + String responseBody = EntityUtils.toString(resp.getEntity()); + + System.out.println(responseBody); + + ProtoMessageUtils.toProtoMessage( + ThreatActorByCountryResponse.class, responseBody) + .ifPresent( + m -> { + this.actorsCountPerCountry = + m.getCountriesList().stream() + .map(smr -> new ThreatActorPerCountry(smr.getCode(), smr.getCount())) + .collect(Collectors.toList()); + }); + } catch (Exception e) { + e.printStackTrace(); + return ERROR.toUpperCase(); + } + + return SUCCESS.toUpperCase(); + } + + public String fetchThreatActors() { + HttpPost post = new HttpPost(String.format("%s/api/dashboard/list_threat_actors", backendUrl)); + post.addHeader("Authorization", "Bearer " + this.getApiToken()); + post.addHeader("Content-Type", "application/json"); + + Map body = + new HashMap() { + { + put("skip", skip); + put("limit", LIMIT); + } + }; + String msg = objectMapper.valueToTree(body).toString(); + + System.out.println("Request body for list threat actors" + msg); + + StringEntity requestEntity = new StringEntity(msg, ContentType.APPLICATION_JSON); + post.setEntity(requestEntity); + + try (CloseableHttpResponse resp = this.httpClient.execute(post)) { + String responseBody = EntityUtils.toString(resp.getEntity()); + + System.out.println(responseBody); + + ProtoMessageUtils.toProtoMessage( + ListThreatActorResponse.class, responseBody) + .ifPresent( + m -> { + this.actors = + m.getActorsList().stream() + .map( + smr -> + new DashboardThreatActor( + smr.getId(), + smr.getLatestApiEndpoint(), + smr.getLatestApiIp(), + URLMethods.Method.fromString(smr.getLatestApiMethod()), + smr.getDiscoveredAt(), + smr.getCountry())) + .collect(Collectors.toList()); + }); + } catch (Exception e) { + e.printStackTrace(); + return ERROR.toUpperCase(); + } + + return SUCCESS.toUpperCase(); + } + + public int getSkip() { + return skip; + } + + public void setSkip(int skip) { + this.skip = skip; + } + + public static int getLimit() { + return LIMIT; + } + + public long getTotal() { + return total; + } + + public void setTotal(long total) { + this.total = total; + } + + public List getActors() { + return actors; + } + + public void setActors(List actor) { + this.actors = actor; + } + + public List getActorsCountPerCountry() { + return actorsCountPerCountry; + } + + public void setActorsCountPerCountry(List actorsCountPerCountry) { + this.actorsCountPerCountry = actorsCountPerCountry; + } +} diff --git a/apps/dashboard/src/main/java/com/akto/action/threat_detection/ThreatActorPerCountry.java b/apps/dashboard/src/main/java/com/akto/action/threat_detection/ThreatActorPerCountry.java new file mode 100644 index 0000000000..33765fe5dc --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/threat_detection/ThreatActorPerCountry.java @@ -0,0 +1,27 @@ +package com.akto.action.threat_detection; + +public class ThreatActorPerCountry { + private String country; + private int count; + + public ThreatActorPerCountry(String country, int count) { + this.country = country; + this.count = count; + } + + public String getCountry() { + return country; + } + + public void setCountry(String country) { + this.country = country; + } + + public int getCount() { + return count; + } + + public void setCount(int count) { + this.count = count; + } +} diff --git a/apps/dashboard/src/main/java/com/akto/action/threat_detection/ThreatApiAction.java b/apps/dashboard/src/main/java/com/akto/action/threat_detection/ThreatApiAction.java new file mode 100644 index 0000000000..ebbebc669e --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/threat_detection/ThreatApiAction.java @@ -0,0 +1,164 @@ +package com.akto.action.threat_detection; + +import com.akto.dto.type.URLMethods; +import com.akto.proto.generated.threat_detection.service.dashboard_service.v1.ListThreatApiResponse; +import com.akto.proto.generated.threat_detection.service.dashboard_service.v1.ThreatCategoryWiseCountResponse; +import com.akto.proto.utils.ProtoMessageUtils; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +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.http.util.EntityUtils; + +public class ThreatApiAction extends AbstractThreatDetectionAction { + + List apis; + List categoryCounts; + int skip; + static final int LIMIT = 50; + long total; + Map sort; + int startTimestamp, endTimestamp; + + private final CloseableHttpClient httpClient; + private final String backendUrl; + + private final ObjectMapper objectMapper = new ObjectMapper(); + + public ThreatApiAction() { + super(); + this.httpClient = HttpClients.createDefault(); + this.backendUrl = System.getenv("THREAT_DETECTION_BACKEND_URL"); + } + + public String fetchThreatCategoryCount() { + HttpGet get = + new HttpGet(String.format("%s/api/dashboard/get_subcategory_wise_count", backendUrl)); + get.addHeader("Authorization", "Bearer " + this.getApiToken()); + get.addHeader("Content-Type", "application/json"); + + try (CloseableHttpResponse resp = this.httpClient.execute(get)) { + String responseBody = EntityUtils.toString(resp.getEntity()); + + System.out.println(responseBody); + + ProtoMessageUtils.toProtoMessage( + ThreatCategoryWiseCountResponse.class, responseBody) + .ifPresent( + m -> { + this.categoryCounts = + m.getCategoryWiseCountsList().stream() + .map( + smr -> + new ThreatCategoryCount( + smr.getCategory(), smr.getSubCategory(), smr.getCount())) + .collect(Collectors.toList()); + }); + } catch (Exception e) { + e.printStackTrace(); + return ERROR.toUpperCase(); + } + + return SUCCESS.toUpperCase(); + } + + public String fetchThreatApis() { + HttpPost post = new HttpPost(String.format("%s/api/dashboard/list_threat_apis", backendUrl)); + post.addHeader("Authorization", "Bearer " + this.getApiToken()); + post.addHeader("Content-Type", "application/json"); + + Map body = + new HashMap() { + { + put("skip", skip); + put("limit", LIMIT); + } + }; + String msg = objectMapper.valueToTree(body).toString(); + + System.out.println("Request body for list threat actors" + msg); + + StringEntity requestEntity = new StringEntity(msg, ContentType.APPLICATION_JSON); + post.setEntity(requestEntity); + + try (CloseableHttpResponse resp = this.httpClient.execute(post)) { + String responseBody = EntityUtils.toString(resp.getEntity()); + + System.out.println(responseBody); + + ProtoMessageUtils.toProtoMessage( + ListThreatApiResponse.class, responseBody) + .ifPresent( + m -> { + this.apis = + m.getApisList().stream() + .map( + smr -> + new DashboardThreatApi( + smr.getEndpoint(), + URLMethods.Method.fromString(smr.getMethod()), + smr.getActorsCount(), + smr.getRequestsCount(), + smr.getDiscoveredAt())) + .collect(Collectors.toList()); + }); + } catch (Exception e) { + e.printStackTrace(); + return ERROR.toUpperCase(); + } + + return SUCCESS.toUpperCase(); + } + + public int getSkip() { + return skip; + } + + public void setSkip(int skip) { + this.skip = skip; + } + + public static int getLimit() { + return LIMIT; + } + + public long getTotal() { + return total; + } + + public void setTotal(long total) { + this.total = total; + } + + public List getApis() { + return apis; + } + + public void setApis(List apis) { + this.apis = apis; + } + + public Map getSort() { + return sort; + } + + public void setSort(Map sort) { + this.sort = sort; + } + + public List getCategoryCounts() { + return categoryCounts; + } + + public void setCategoryCounts(List categoryCounts) { + this.categoryCounts = categoryCounts; + } +} diff --git a/apps/dashboard/src/main/java/com/akto/action/threat_detection/ThreatCategoryCount.java b/apps/dashboard/src/main/java/com/akto/action/threat_detection/ThreatCategoryCount.java new file mode 100644 index 0000000000..e2ac0b4f5c --- /dev/null +++ b/apps/dashboard/src/main/java/com/akto/action/threat_detection/ThreatCategoryCount.java @@ -0,0 +1,37 @@ +package com.akto.action.threat_detection; + +public class ThreatCategoryCount { + private String category; + private String subCategory; + private int count; + + public ThreatCategoryCount(String category, String subCategory, int count) { + this.category = category; + this.subCategory = subCategory; + this.count = count; + } + + public String getCategory() { + return category; + } + + public void setCategory(String category) { + this.category = category; + } + + public String getSubCategory() { + return subCategory; + } + + public void setSubCategory(String subCategory) { + this.subCategory = subCategory; + } + + public int getCount() { + return count; + } + + public void setCount(int count) { + this.count = count; + } +} diff --git a/apps/dashboard/src/main/resources/struts.xml b/apps/dashboard/src/main/resources/struts.xml index a5d6881002..f6b07cae05 100644 --- a/apps/dashboard/src/main/resources/struts.xml +++ b/apps/dashboard/src/main/resources/struts.xml @@ -7279,40 +7279,122 @@ - - + + 403 false - ^actionErrors.* + ^actionErrors.* - + + + 422 + false + ^actionErrors.* + + + + + + + + 403 + false + ^actionErrors.* + 422 false - ^actionErrors.* + ^actionErrors.* - - + + + 403 false - ^actionErrors.* + ^actionErrors.* - + + + 422 + false + ^actionErrors.* + + + + + + 403 + false + ^actionErrors.* + + 422 false - ^actionErrors.* + ^actionErrors.* + + + + + 403 + false + ^actionErrors.* + + + + 422 + false + ^actionErrors.* + + + + + + + + 403 + false + ^actionErrors.* + + + + 422 + false + ^actionErrors.* + + diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatActorPage.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatActorPage.jsx new file mode 100644 index 0000000000..a8c3f58e34 --- /dev/null +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatActorPage.jsx @@ -0,0 +1,147 @@ +import { useReducer, useState, useEffect } from "react"; +import DateRangeFilter from "../../components/layouts/DateRangeFilter"; +import PageWithMultipleCards from "../../components/layouts/PageWithMultipleCards"; +import TitleWithInfo from "../../components/shared/TitleWithInfo"; +import values from "@/util/values"; +import { produce } from "immer"; +import func from "@/util/func"; +import ThreatActorTable from "./components/ThreatActorsTable"; +import ThreatWorldMap from "./components/ThreatWorldMap"; +import ThreatApiSubcategoryCount from "./components/ThreatApiSubcategoryCount"; + +import api from "./api"; +import { HorizontalGrid, VerticalStack } from "@shopify/polaris"; +import TopThreatTypeChart from "./components/TopThreatTypeChart"; +function ThreatActorPage() { + const [mapData, setMapData] = useState([]); + const [loading, setLoading] = useState(false); + const [subCategoryCount, setSubCategoryCount] = useState([]); + const [categoryCount, setCategoryCount] = useState([]); + const initialVal = values.ranges[3]; + const [currDateRange, dispatchCurrDateRange] = useReducer( + produce((draft, action) => func.dateRangeReducer(draft, action)), + initialVal + ); + + useEffect(() => { + const fetchActorsPerCountry = async () => { + setLoading(true); + const res = await api.getActorsCountPerCounty(); + if (res?.actorsCountPerCountry) { + setMapData( + res.actorsCountPerCountry.map((x) => { + return { + code: x.country, + z: 100, + count: x.count, + }; + }) + ); + } + setLoading(false); + }; + const fetchThreatCategoryCount = async () => { + setLoading(true); + const res = await api.fetchThreatCategoryCount(); + if (res?.categoryCounts) { + const categoryRes = {}; + const subCategoryRes = {}; + for (const cc of res.categoryCounts) { + if (categoryRes[cc.category]) { + categoryRes[cc.category] += cc.count; + } else { + categoryRes[cc.category] = cc.count; + } + + if (subCategoryRes[cc.subCategory]) { + subCategoryRes[cc.subCategory] += cc.count; + } else { + subCategoryRes[cc.subCategory] = cc.count; + } + } + + setSubCategoryCount( + Object.keys(subCategoryRes).map((x) => { + return { + text: x.replaceAll("_", " "), + value: subCategoryRes[x], + color: "#A5B4FC", + }; + }) + ); + + setCategoryCount( + Object.keys(categoryRes).map((x) => { + return { + text: x.replaceAll("_", " "), + value: categoryRes[x], + color: "#A5B4FC", + }; + }) + ); + } + setLoading(false); + }; + fetchActorsPerCountry(); + fetchThreatCategoryCount(); + }, []); + + const ChartComponent = () => { + return ( + + + + + + + + ); + }; + + const components = [ + , + , + ]; + + return ( + } + isFirstPage={true} + primaryAction={ + + dispatchCurrDateRange({ + type: "update", + period: dateObj.period, + title: dateObj.title, + alias: dateObj.alias, + }) + } + /> + } + components={components} + /> + ); +} + +export default ThreatActorPage; diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatApiPage.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatApiPage.jsx new file mode 100644 index 0000000000..12af7273f6 --- /dev/null +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatApiPage.jsx @@ -0,0 +1,111 @@ +import { useEffect, useReducer, useState } from "react"; +import DateRangeFilter from "../../components/layouts/DateRangeFilter"; +import PageWithMultipleCards from "../../components/layouts/PageWithMultipleCards"; +import TitleWithInfo from "../../components/shared/TitleWithInfo"; +import values from "@/util/values"; +import { produce } from "immer"; +import func from "@/util/func"; +import ThreatApisTable from "./components/ThreatApisTable"; +import TopThreatTypeChart from "./components/TopThreatTypeChart"; +import ThreatApiSubcategoryCount from "./components/ThreatApiSubcategoryCount"; + +import api from "./api"; +import { HorizontalGrid } from "@shopify/polaris"; +function ThreatApiPage() { + const [loading, setLoading] = useState(false); + const [categoryCount, setCategoryCount] = useState([]); + const [subCategoryCount, setSubCategoryCount] = useState([]); + const initialVal = values.ranges[3]; + const [currDateRange, dispatchCurrDateRange] = useReducer( + produce((draft, action) => func.dateRangeReducer(draft, action)), + initialVal + ); + + const ChartComponent = () => { + return ( + + + + + ); + }; + const components = [ + , + , + ]; + + useEffect(() => { + const fetchThreatCategoryCount = async () => { + setLoading(true); + const res = await api.fetchThreatCategoryCount(); + if (res?.categoryCounts) { + const categoryRes = {}; + const subCategoryRes = {}; + for (const cc of res.categoryCounts) { + if (categoryRes[cc.category]) { + categoryRes[cc.category] += cc.count; + } else { + categoryRes[cc.category] = cc.count; + } + + if (subCategoryRes[cc.subCategory]) { + subCategoryRes[cc.subCategory] += cc.count; + } else { + subCategoryRes[cc.subCategory] = cc.count; + } + } + + setSubCategoryCount( + Object.keys(subCategoryRes).map((x) => { + return { + text: x.replaceAll("_", " "), + value: subCategoryRes[x], + }; + }) + ); + + setCategoryCount( + Object.keys(categoryRes).map((x) => { + return { + text: x.replaceAll("_", " "), + value: categoryRes[x], + color: "#A5B4FC", + }; + }) + ); + } + setLoading(false); + }; + + fetchThreatCategoryCount(); + }, []); + + return ( + } + isFirstPage={true} + primaryAction={ + + dispatchCurrDateRange({ + type: "update", + period: dateObj.period, + title: dateObj.title, + alias: dateObj.alias, + }) + } + /> + } + components={components} + /> + ); +} + +export default ThreatApiPage; 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 d85a5597ab..61379cdc98 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,6 +38,38 @@ const threatDetectionRequests = { data: {} }) }, + fetchThreatActors(skip) { + return request({ + url: '/api/fetchThreatActors', + method: 'post', + data: { + skip: skip + } + }) + }, + fetchThreatApis(skip) { + return request({ + url: '/api/fetchThreatApis', + method: 'post', + data: { + skip: skip + } + }) + }, + getActorsCountPerCounty() { + return request({ + url: '/api/getActorsCountPerCounty', + method: 'get', + data: {} + }) + }, + fetchThreatCategoryCount() { + return request({ + url: '/api/fetchThreatCategoryCount', + method: 'get', + data: {} + }) + } } export default threatDetectionRequests \ No newline at end of file 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 9e3ea68191..4af459eab5 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 @@ -1,184 +1,231 @@ import { useEffect, useState } from "react"; import GithubServerTable from "../../../components/tables/GithubServerTable"; -import api from "../api" +import api from "../api"; import { CellType } from "../../../components/tables/rows/GithubRow"; import GetPrettifyEndpoint from "../../observe/GetPrettifyEndpoint"; import PersistStore from "../../../../main/PersistStore"; import func from "../../../../../util/func"; -import { Text } from "@shopify/polaris"; const resourceName = { - singular: 'sample', - plural: 'samples', + singular: "sample", + plural: "samples", }; const headers = [ - { - text: "Endpoint", - value: "endpointComp", - title: "Api endpoints", - }, - { - text: "matchingUrl", - value: "matchingUrl", - title: "Affected url", - }, - { - text: "Filter", - value: "filterId", - title: "Threat filter", - }, - { - text: "Collection", - value: 'apiCollectionName', - title: "Collection", - maxWidth: '95px', - type: CellType.TEXT, - }, - { - text: 'Discovered', - title: 'Discovered', - value: 'discoveredTs', - type: CellType.TEXT, - sortActive: true - }, - { - text: 'Source IPs', - title: 'Source IPs', - value: 'sourceIPComponent', - }, - { - title: '', - type: CellType.ACTION, - } -] + { + text: "Endpoint", + value: "endpointComp", + title: "Endpoint", + }, + { + text: "Actor", + value: "actorComp", + title: "Actor", + }, + { + text: "Filter", + value: "filterId", + title: "Threat filter", + }, + { + text: "Collection", + value: "apiCollectionName", + title: "Collection", + maxWidth: "95px", + type: CellType.TEXT, + }, + { + text: "Discovered", + title: "Discovered", + value: "discoveredTs", + type: CellType.TEXT, + sortActive: true, + }, + { + text: "Source IP", + title: "Source IP", + value: "sourceIPComponent", + }, + { + title: "", + type: CellType.ACTION, + }, +]; const sortOptions = [ - { label: 'Discovered time', value: 'discovered asc', directionLabel: 'Newest', sortKey: 'discovered', columnIndex: 3 }, - { label: 'Discovered time', value: 'discovered desc', directionLabel: 'Oldest', sortKey: 'discovered', columnIndex: 3 }, + { + label: "Discovered time", + value: "discovered asc", + directionLabel: "Newest", + sortKey: "discovered", + columnIndex: 3, + }, + { + label: "Discovered time", + value: "discovered desc", + directionLabel: "Oldest", + sortKey: "discovered", + columnIndex: 3, + }, ]; -let filters = [] +let filters = []; function SusDataTable({ currDateRange, rowClicked }) { - const getTimeEpoch = (key) => { - return Math.floor(Date.parse(currDateRange.period[key]) / 1000) - } - const startTimestamp = getTimeEpoch("since") - const endTimestamp = getTimeEpoch("until") + const getTimeEpoch = (key) => { + return Math.floor(Date.parse(currDateRange.period[key]) / 1000); + }; + const startTimestamp = getTimeEpoch("since"); + const endTimestamp = getTimeEpoch("until"); - const [loading, setLoading] = useState(true); - const collectionsMap = PersistStore(state => state.collectionsMap) - const allCollections = PersistStore(state => state.allCollections) + const [loading, setLoading] = useState(true); + const collectionsMap = PersistStore((state) => state.collectionsMap); + const allCollections = PersistStore((state) => state.allCollections); - async function fetchData(sortKey, sortOrder, skip, limit, filters, filterOperators, queryValue) { - setLoading(true); - let sourceIpsFilter = [], apiCollectionIdsFilter = [], matchingUrlFilter = [] - if (filters?.sourceIps) { - sourceIpsFilter = filters?.sourceIps - } - if (filters?.apiCollectionId) { - apiCollectionIdsFilter = filters?.apiCollectionId - } - if (filters?.url) { - matchingUrlFilter = filters?.url - } - const sort = { [sortKey]: sortOrder } - const res = await api.fetchSuspectSampleData(skip, sourceIpsFilter, apiCollectionIdsFilter, matchingUrlFilter, sort, startTimestamp, endTimestamp) - let total = res.total; - let ret = res?.sampleData.map(x => { - return { - ...x, - endpointComp: , - apiCollectionName: collectionsMap[x.apiCollectionId] || '-', - discoveredTs: func.prettifyEpoch(x.discovered), - sourceIPComponent: {x?.sourceIPs ? x.sourceIPs.reduce((a, b) => a += ((a.length > 0 ? ", " : "") + b), "") : "-"} - } - }) - setLoading(false); - return { value: ret, total: total }; + async function fetchData( + sortKey, + sortOrder, + skip, + limit, + filters, + filterOperators, + queryValue + ) { + setLoading(true); + let sourceIpsFilter = [], + apiCollectionIdsFilter = [], + matchingUrlFilter = []; + if (filters?.sourceIps) { + sourceIpsFilter = filters?.sourceIps; + } + if (filters?.apiCollectionId) { + apiCollectionIdsFilter = filters?.apiCollectionId; + } + if (filters?.url) { + matchingUrlFilter = filters?.url; } + const sort = { [sortKey]: sortOrder }; + const res = await api.fetchSuspectSampleData( + skip, + sourceIpsFilter, + apiCollectionIdsFilter, + matchingUrlFilter, + sort, + startTimestamp, + endTimestamp + ); + let total = res.total; + let ret = res?.maliciousEvents.map((x) => { + return { + ...x, + actorComp: x?.actor, + endpointComp: ( + + ), + apiCollectionName: collectionsMap[x.apiCollectionId] || "-", + discoveredTs: func.prettifyEpoch(x.timestamp), + sourceIPComponent: x?.ip || "-", + }; + }); + setLoading(false); + return { value: ret, total: total }; + } - async function fillFilters() { - const res = await api.fetchFiltersThreatTable(); - let apiCollectionFilterChoices = allCollections.filter(x => { - return x.type !== 'API_GROUP' - }).map(x => { - return { label: x.displayName, value: x.id } - }) - let urlChoices = res?.urls.filter(x => { - return x.length > 0 - }).map(x => { - return { label: x, value: x } - }) - let ipChoices = res?.ips.map(x => { - return { label: x, value: x } - }) + async function fillFilters() { + const res = await api.fetchFiltersThreatTable(); + let apiCollectionFilterChoices = allCollections + .filter((x) => { + return x.type !== "API_GROUP"; + }) + .map((x) => { + return { label: x.displayName, value: x.id }; + }); + let urlChoices = res?.urls + .filter((x) => { + return x.length > 0; + }) + .map((x) => { + return { label: x, value: x }; + }); + let ipChoices = res?.ips.map((x) => { + return { label: x, value: x }; + }); - filters = [ - { - key: 'apiCollectionId', - label: 'Collection', - title: 'Collection', - choices: apiCollectionFilterChoices, - }, { - key: 'sourceIps', - label: 'Source IP', - title: 'Source IP', - choices: ipChoices, - }, { - key: 'url', - label: 'URL', - title: 'URL', - choices: urlChoices, - }, - ] - } + filters = [ + { + key: "apiCollectionId", + label: "Collection", + title: "Collection", + choices: apiCollectionFilterChoices, + }, + { + key: "sourceIps", + label: "Source IP", + title: "Source IP", + choices: ipChoices, + }, + { + key: "url", + label: "URL", + title: "URL", + choices: urlChoices, + }, + ]; + } - useEffect(() => { - fillFilters() - }, []) + useEffect(() => { + fillFilters(); + }, []); - function disambiguateLabel(key, value) { - switch (key) { - case "apiCollectionId": - return func.convertToDisambiguateLabelObj(value, collectionsMap, 2) - default: - return func.convertToDisambiguateLabelObj(value, null, 2); - } + function disambiguateLabel(key, value) { + switch (key) { + case "apiCollectionId": + return func.convertToDisambiguateLabelObj(value, collectionsMap, 2); + default: + return func.convertToDisambiguateLabelObj(value, null, 2); } + } - const getActions = (item) => { - return [{ - items: [{ - content: 'View in collection', - onAction: () => { - window.open(`/dashboard/observe/inventory/${item.apiCollectionId}`, "_blank") - }, - }] - }] - } + 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)} - fetchData={fetchData} - filters={filters} - selectable={false} - hasRowActions={true} - getActions={getActions} - hideQueryField={true} - headings={headers} - useNewRow={true} - condensedHeight={true} - />) + const key = startTimestamp + endTimestamp; + return ( + rowClicked(data)} [For now removing on row click functionality] + fetchData={fetchData} + filters={filters} + selectable={false} + hasRowActions={true} + getActions={getActions} + hideQueryField={true} + headings={headers} + useNewRow={true} + condensedHeight={true} + /> + ); } export default SusDataTable; 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 new file mode 100644 index 0000000000..a4ff602489 --- /dev/null +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/ThreatActorsTable.jsx @@ -0,0 +1,102 @@ +import { useState } from "react"; +import GithubServerTable from "../../../components/tables/GithubServerTable"; +import api from "../api"; +import { CellType } from "../../../components/tables/rows/GithubRow"; +import GetPrettifyEndpoint from "../../observe/GetPrettifyEndpoint"; +import func from "../../../../../util/func"; + +const resourceName = { + singular: "actor", + plural: "actors", +}; + +const headers = [ + { + text: "Actor", + value: "actor", + title: "Actor", + }, + { + text: "Latest IP", + title: "Latest IP", + value: "latestIp", + }, + { + text: "Latest API", + title: "Latest API", + value: "latestApi", + }, + { + text: "Discovered", + title: "Discovered", + value: "discoveredAt", + type: CellType.TEXT, + sortActive: true, + }, +]; + +const sortOptions = []; + +let filters = []; + +function ThreatActorTable({ data, currDateRange, rowClicked }) { + const [loading, setLoading] = useState(false); + + const getTimeEpoch = (key) => { + return Math.floor(Date.parse(currDateRange.period[key]) / 1000); + }; + const startTimestamp = getTimeEpoch("since"); + const endTimestamp = getTimeEpoch("until"); + + function disambiguateLabel(key, value) { + return func.convertToDisambiguateLabelObj(value, null, 2); + } + + async function fetchData(sortKey, sortOrder, skip) { + setLoading(true); + const sort = { [sortKey]: sortOrder }; + const res = await api.fetchThreatActors(skip); + let total = res.total; + let ret = res?.actors?.map((x) => { + return { + ...x, + actor: x.id, + latestIp: x.latestApiIp, + discoveredAt: func.prettifyEpoch(x.discoveredAt), + latestApi: ( + + ), + }; + }); + setLoading(false); + return { value: ret, total: total }; + } + + const key = startTimestamp + endTimestamp; + return ( + { }} + hideQueryField={true} + headings={headers} + useNewRow={true} + condensedHeight={true} + /> + ); +} + +export default ThreatActorTable; diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/ThreatApiSubcategoryCount.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/ThreatApiSubcategoryCount.jsx new file mode 100644 index 0000000000..0fd745bc7e --- /dev/null +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/ThreatApiSubcategoryCount.jsx @@ -0,0 +1,25 @@ +import React, { useState, useEffect } from "react"; +import { Box, Card, DataTable } from "@shopify/polaris"; +import InfoCard from "../../dashboard/new_components/InfoCard"; + +const ThreatApiSubcategoryCount = ({ data }) => { + const Data = () => ( + [x.text, x.value]) + .sort((a, b) => b[0].localeCompare(a[0]))} + /> + ); + + return ( + } + /> + ); +}; + +export default ThreatApiSubcategoryCount; 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 new file mode 100644 index 0000000000..0e7ae16d47 --- /dev/null +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/ThreatApisTable.jsx @@ -0,0 +1,104 @@ +import { useEffect, useState } from "react"; +import GithubServerTable from "../../../components/tables/GithubServerTable"; +import api from "../api"; +import { CellType } from "../../../components/tables/rows/GithubRow"; +import GetPrettifyEndpoint from "../../observe/GetPrettifyEndpoint"; +import PersistStore from "../../../../main/PersistStore"; +import func from "../../../../../util/func"; + +const resourceName = { + singular: "api", + plural: "apis", +}; + +const headers = [ + { + text: "Endpoint", + value: "api", + title: "Endpoint", + }, + { + text: "Malicious Actors", + value: "actorsCount", + title: "Malicious Actors", + }, + { + text: "Malicious Requests", + value: "requestsCount", + title: "Malicious Requests", + }, + { + text: "Discovered", + title: "Discovered", + value: "discoveredAt", + type: CellType.TEXT, + sortActive: true, + }, +]; + +const sortOptions = []; + +let filters = []; + +function ThreatApiTable({ currDateRange, rowClicked }) { + const getTimeEpoch = (key) => { + return Math.floor(Date.parse(currDateRange.period[key]) / 1000); + }; + const startTimestamp = getTimeEpoch("since"); + const endTimestamp = getTimeEpoch("until"); + + const [loading, setLoading] = useState(true); + const collectionsMap = PersistStore((state) => state.collectionsMap); + const allCollections = PersistStore((state) => state.allCollections); + + useEffect(() => {}, []); + + function disambiguateLabel(key, value) { + return func.convertToDisambiguateLabelObj(value, null, 2); + } + + async function fetchData(sortKey, sortOrder, skip) { + setLoading(true); + const sort = { [sortKey]: sortOrder }; + const res = await api.fetchThreatApis(skip); + let total = res.total; + let ret = res?.apis?.map((x) => { + return { + ...x, + id: `${x.method}-${x.api}`, + actorsCount: x.actorsCount, + requestsCount: x.requestsCount, + discoveredAt: func.prettifyEpoch(x.discoveredAt), + api: ( + + ), + }; + }); + setLoading(false); + return { value: ret, total: total }; + } + + const key = startTimestamp + endTimestamp; + return ( + {}} + hideQueryField={true} + headings={headers} + useNewRow={true} + condensedHeight={true} + /> + ); +} + +export default ThreatApiTable; diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/ThreatWorldMap.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/ThreatWorldMap.jsx new file mode 100644 index 0000000000..7b737a5228 --- /dev/null +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/ThreatWorldMap.jsx @@ -0,0 +1,86 @@ +import { useEffect } from "react"; +import Highcharts from "highcharts/highmaps"; + +function ThreatWorldMap({ data, style, loading }) { + useEffect(() => { + const fetchMapData = async () => { + const topology = await fetch( + "https://code.highcharts.com/mapdata/custom/world.topo.json" + ).then((response) => response.json()); + + Highcharts.mapChart("threat-world-map-container", { + chart: { + map: topology, + backgroundColor: "#fff", + }, + + title: { + text: "Threat Actor Map", + }, + + credits: { + enabled: false, + }, + + subtitle: { + text: "", + }, + + legend: { + enabled: false, + }, + + mapNavigation: { + enabled: false, + }, + + mapView: { + fitToGeometry: { + type: "MultiPoint", + coordinates: [ + [-164, 54], // Alaska west + [-35, 84], // Greenland north + [179, -38], // New Zealand east + [-68, -55], // Chile south + ], + }, + }, + + series: [ + { + name: "Countries", + color: "#E0E0E0", + enableMouseTracking: false, + states: { + inactive: { + enabled: true, + opacity: 1, + }, + }, + }, + { + type: "mapbubble", + name: "", + data: data, + minSize: "4%", + maxSize: "4%", + joinBy: ["iso-a2", "code"], + marker: { + fillOpacity: 0.5, + lineWidth: 0, + }, + tooltip: { + pointFormat: "{point.name}
Actors: {point.count}", + }, + }, + ], + }); + }; + + fetchMapData(); + }, [data]); + + return
; +} + +export default ThreatWorldMap; diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/TopThreatTypeChart.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/TopThreatTypeChart.jsx new file mode 100644 index 0000000000..43c1d9858c --- /dev/null +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/TopThreatTypeChart.jsx @@ -0,0 +1,43 @@ +import React, { useEffect, useState } from "react"; +import InfoCard from "../../dashboard/new_components/InfoCard"; +import ChartypeComponent from "../../testing/TestRunsPage/ChartypeComponent"; +import BarGraph from "../../../components/charts/BarGraph"; + +const TopThreatTypeChart = ({ data }) => { + const [chartData, setChartData] = useState([]); + useEffect(() => { + const chartData = data + .sort((a, b) => a.text.localeCompare(b.text)) + .map((x) => ({ + text: x.text.replaceAll("_", " "), + value: x.value, + color: x.color, + })); + setChartData(chartData); + }, [data]); + return ( + + } + /> + ); +}; + +export default TopThreatTypeChart; 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 c6c5be0275..42bad35beb 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 @@ -78,6 +78,8 @@ import SignUpWithSSO from "../signup/components/SignUpWithSSO"; import TeamsWebhooks from "../dashboard/pages/settings/integrations/teamsWebhooks/TeamsWebhooks"; import TeamsWebhook from "../dashboard/pages/settings/integrations/teamsWebhooks/TeamsWebhook"; 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"; // if you add a component in a new path, please verify the search implementation in function -> 'getSearchItemsArr' in func.js @@ -180,6 +182,14 @@ const router = createBrowserRouter([ path:"threat-detection", element: }, + { + path: "threat-api", + element: + }, + { + path: "threat-actor", + element: + } ] }, { diff --git a/apps/pom.xml b/apps/pom.xml index ca5ec582af..20dcc1cb26 100644 --- a/apps/pom.xml +++ b/apps/pom.xml @@ -49,15 +49,15 @@ - api-threat-detection + threat-detection - api-threat-detection/pom.xml + threat-detection/pom.xml - api-threat-detection - + threat-detection + source-code-analyser @@ -136,6 +136,16 @@ testing-cli + + threat-detection-backend + + + threat-detection-backend/pom.xml + + + + threat-detection-backend + + - diff --git a/buf.gen.yaml b/buf.gen.yaml new file mode 100644 index 0000000000..f282e25075 --- /dev/null +++ b/buf.gen.yaml @@ -0,0 +1,18 @@ +version: v2 + +managed: + enabled: true + + override: + - file_option: java_multiple_files + value: true + + - file_option: java_package_prefix + value: com.akto.proto.generated + +plugins: + - remote: buf.build/grpc/java:v1.69.0 + out: libs/protobuf/src/main/java + + - remote: buf.build/protocolbuffers/java:v28.3 + out: libs/protobuf/src/main/java diff --git a/libs/pom.xml b/libs/pom.xml index 8bce86a189..2dfb6103a8 100644 --- a/libs/pom.xml +++ b/libs/pom.xml @@ -18,6 +18,7 @@ dao utils integrations + protobuf diff --git a/libs/protobuf/.gitignore b/libs/protobuf/.gitignore new file mode 100644 index 0000000000..9b21cfdc49 --- /dev/null +++ b/libs/protobuf/.gitignore @@ -0,0 +1,2 @@ +src/main/java/com/akto/proto/generated + diff --git a/libs/protobuf/pom.xml b/libs/protobuf/pom.xml new file mode 100644 index 0000000000..6943beca2a --- /dev/null +++ b/libs/protobuf/pom.xml @@ -0,0 +1,76 @@ + + + 4.0.0 + + com.akto.libs + libs + ${revision} + + + + 1.69.0 + + + + com.akto.libs.protobuf + protobuf + jar + + + + + io.grpc + grpc-netty-shaded + 1.68.1 + + + io.grpc + grpc-core + ${grpc.version} + + + io.grpc + grpc-protobuf + ${grpc.version} + + + io.grpc + grpc-stub + ${grpc.version} + + + io.grpc + grpc-services + ${grpc.version} + + + com.google.protobuf + protobuf-java + 4.28.3 + + + com.google.protobuf + protobuf-java-util + 3.25.5 + compile + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + 8 + 8 + + + + src/main/java + + + \ No newline at end of file diff --git a/libs/protobuf/src/main/java/com/akto/grpc/auth/AuthToken.java b/libs/protobuf/src/main/java/com/akto/grpc/auth/AuthToken.java new file mode 100644 index 0000000000..670e945419 --- /dev/null +++ b/libs/protobuf/src/main/java/com/akto/grpc/auth/AuthToken.java @@ -0,0 +1,51 @@ +package com.akto.grpc.auth; + +import static io.grpc.Metadata.ASCII_STRING_MARSHALLER; + +import io.grpc.CallCredentials; +import io.grpc.Metadata; +import io.grpc.Status; +import java.util.Optional; +import java.util.concurrent.Executor; + +public class AuthToken extends CallCredentials { + + private final String token; + public static final Metadata.Key AUTHORIZATION_METADATA_KEY = + Metadata.Key.of("Authorization", ASCII_STRING_MARSHALLER); + + public AuthToken(String token) { + if (token == null || token.trim().isEmpty()) { + throw new IllegalArgumentException("Token cannot be null or empty"); + } + this.token = String.format("Bearer %s", token.trim()); + } + + @Override + public void applyRequestMetadata( + RequestInfo requestInfo, Executor appExecutor, MetadataApplier applier) { + appExecutor.execute( + () -> { + try { + Metadata headers = new Metadata(); + headers.put(AUTHORIZATION_METADATA_KEY, token); + applier.apply(headers); + } catch (Throwable e) { + applier.fail(Status.UNAUTHENTICATED.withCause(e)); + } + }); + } + + public static Optional getBearerTokenFromMeta(Metadata metadata) { + String val = metadata.get(AUTHORIZATION_METADATA_KEY); + if (val == null || val.trim().isEmpty()) { + return Optional.empty(); + } + + if (val.startsWith("Bearer ")) { + return Optional.of(val.substring("Bearer ".length()).trim()); + } + + return Optional.empty(); + } +} diff --git a/libs/protobuf/src/main/java/com/akto/proto/utils/ProtoMessageUtils.java b/libs/protobuf/src/main/java/com/akto/proto/utils/ProtoMessageUtils.java new file mode 100644 index 0000000000..b6d4973e86 --- /dev/null +++ b/libs/protobuf/src/main/java/com/akto/proto/utils/ProtoMessageUtils.java @@ -0,0 +1,27 @@ +package com.akto.proto.utils; + +import com.google.protobuf.Message; +import com.google.protobuf.util.JsonFormat; +import java.util.Optional; + +public class ProtoMessageUtils { + public static Optional toString(Message msg) { + try { + return Optional.of(JsonFormat.printer().print(msg)); + } catch (Exception e) { + // Ignore + } + return Optional.empty(); + } + + public static Optional toProtoMessage(Class clz, String msg) { + try { + T.Builder builder = (T.Builder) clz.getMethod("newBuilder").invoke(null); + JsonFormat.parser().merge(msg, builder); + return Optional.of((T) builder.build()); + } catch (Exception e) { + // Ignore + } + return Optional.empty(); + } +} diff --git a/protobuf/buf.yaml b/protobuf/buf.yaml new file mode 100644 index 0000000000..534fcc9218 --- /dev/null +++ b/protobuf/buf.yaml @@ -0,0 +1,7 @@ +version: v2 +breaking: + use: + - FILE +lint: + use: + - STANDARD diff --git a/protobuf/threat_detection/message/malicious_event/event_type/v1/event_type.proto b/protobuf/threat_detection/message/malicious_event/event_type/v1/event_type.proto new file mode 100644 index 0000000000..5887f5c6e4 --- /dev/null +++ b/protobuf/threat_detection/message/malicious_event/event_type/v1/event_type.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +package threat_detection.message.malicious_event.event_type.v1; + +option java_outer_classname = "MaliciousEventProto"; +option java_package = "threat_detection.message.malicious_event.event_type.v1"; + +enum EventType { + EVENT_TYPE_UNSPECIFIED = 0; + EVENT_TYPE_SINGLE = 1; + EVENT_TYPE_AGGREGATED = 2; +} diff --git a/protobuf/threat_detection/message/malicious_event/v1/message.proto b/protobuf/threat_detection/message/malicious_event/v1/message.proto new file mode 100644 index 0000000000..238836ebb5 --- /dev/null +++ b/protobuf/threat_detection/message/malicious_event/v1/message.proto @@ -0,0 +1,28 @@ +syntax = "proto3"; + +package threat_detection.message.malicious_event.v1; + +import "threat_detection/message/malicious_event/event_type/v1/event_type.proto"; + +option java_outer_classname = "MaliciousEventProto"; +option java_package = "threat_detection.message.malicious_event.v1"; + +message MaliciousEventMessage { + string actor = 1; + string filter_id = 2; + int64 detected_at = 3; + string latest_api_ip = 4; + string latest_api_endpoint = 5; + string latest_api_method = 6; + int32 latest_api_collection_id = 7; + string latest_api_payload = 8; + threat_detection.message.malicious_event.event_type.v1.EventType event_type = 9; + string category = 10; + string sub_category = 11; +} + +message MaliciousEventKafkaEnvelope { + string account_id = 1; + string actor = 2; + MaliciousEventMessage malicious_event = 3; +} diff --git a/protobuf/threat_detection/message/sample_request/v1/message.proto b/protobuf/threat_detection/message/sample_request/v1/message.proto new file mode 100644 index 0000000000..4192572509 --- /dev/null +++ b/protobuf/threat_detection/message/sample_request/v1/message.proto @@ -0,0 +1,22 @@ +syntax = "proto3"; + +package threat_detection.message.sample_request.v1; + +option java_outer_classname = "SampleRequestProto"; +option java_package = "threat_detection.message.sample_request.v1"; + +message SampleMaliciousRequest { + string ip = 1; + int64 timestamp = 2; + string url = 3; + string method = 4; + int32 api_collection_id = 5; + string payload = 6; + string filter_id = 7; +} + +message SampleRequestKafkaEnvelope { + string account_id = 1; + string actor = 2; + SampleMaliciousRequest malicious_request = 3; +} diff --git a/protobuf/threat_detection/service/dashboard_service/v1/service.proto b/protobuf/threat_detection/service/dashboard_service/v1/service.proto new file mode 100644 index 0000000000..983cf18be7 --- /dev/null +++ b/protobuf/threat_detection/service/dashboard_service/v1/service.proto @@ -0,0 +1,106 @@ +syntax = "proto3"; + +package threat_detection.service.dashboard_service.v1; + +import "threat_detection/message/malicious_event/event_type/v1/event_type.proto"; + +// Dashboard service which the dashboard actions will call instead of directly calling DB +option java_outer_classname = "DashboardServiceProto"; +option java_package = "threat_detection.service.dashboard_service.v1"; + +message ListMaliciousRequestsResponse { + message MaliciousEvent { + string id = 1; + string actor = 2; + string filter_id = 3; + int64 detected_at = 4; + string ip = 5; + string endpoint = 6; + string method = 7; + int32 api_collection_id = 8; + string payload = 9; + string country = 10; + threat_detection.message.malicious_event.event_type.v1.EventType event_type = 11; + string category = 12; + string sub_category = 13; + } + repeated MaliciousEvent malicious_events = 1; + int32 total = 2; +} + +message ListMaliciousRequestsRequest { + // The number of alerts to return + optional uint32 skip = 1; + uint32 limit = 2; + map sort = 3; +} + +message FetchAlertFiltersRequest {} + +message FetchAlertFiltersResponse { + repeated string actors = 1; + repeated string urls = 2; +} + +message ListThreatActorsRequest { + optional uint32 skip = 1; + uint32 limit = 2; + map sort = 3; +} + +message ListThreatActorResponse { + message ThreatActor { + string id = 1; + string latest_api_ip = 2; + string latest_api_endpoint = 3; + string latest_api_method = 4; + uint64 discovered_at = 5; + string country = 6; + } + repeated ThreatActor actors = 1; + int32 total = 2; +} + +message ListThreatApiRequest { + optional uint32 skip = 1; + uint32 limit = 2; + map sort = 3; +} + +message ListThreatApiResponse { + message ThreatApi { + string endpoint = 1; + string method = 2; + uint64 discovered_at = 3; + uint32 actors_count = 4; + uint32 requests_count = 5; + } + + repeated ThreatApi apis = 1; + int32 total = 2; +} + +message ThreatActorByCountryRequest { +} + +message ThreatActorByCountryResponse { + message CountryCount { + string code = 1; + uint32 count = 2; + } + + repeated CountryCount countries = 1; +} + +message ThreatCategoryWiseCountRequest { +} + +message ThreatCategoryWiseCountResponse { + message SubCategoryCount { + string category = 1; + string sub_category = 2; + uint32 count = 3; + } + + repeated SubCategoryCount category_wise_counts = 1; +} diff --git a/protobuf/threat_detection/service/malicious_alert_service/v1/service.proto b/protobuf/threat_detection/service/malicious_alert_service/v1/service.proto new file mode 100644 index 0000000000..bde487cfe1 --- /dev/null +++ b/protobuf/threat_detection/service/malicious_alert_service/v1/service.proto @@ -0,0 +1,19 @@ +syntax = "proto3"; + +package threat_detection.service.malicious_alert_service.v1; + +import "threat_detection/message/malicious_event/v1/message.proto"; +import "threat_detection/message/sample_request/v1/message.proto"; + +// This is a consumer service for recording malicious alerts +// For dashboard purposes we will have a separate service to retrieve these events. +option java_outer_classname = "MaliciousAlertServiceProto"; +option java_package = "threat_detection.service.malicious_alert_service.v1"; + +message RecordMaliciousEventResponse { +} + +message RecordMaliciousEventRequest { + threat_detection.message.malicious_event.v1.MaliciousEventMessage malicious_event = 1; + repeated threat_detection.message.sample_request.v1.SampleMaliciousRequest sample_requests = 2; +} diff --git a/scripts/proto-gen.sh b/scripts/proto-gen.sh new file mode 100644 index 0000000000..ff32a1f164 --- /dev/null +++ b/scripts/proto-gen.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +# Check if buf is installed or not +# Please install buf if not already installed by following the instructions at https://docs.buf.build/installation +if ! command -v buf >/dev/null 2>&1; then + echo "Please install buf if not already installed by following the instructions at https://docs.buf.build/installation" + exit +fi + +buf lint protobuf +rm -rf libs/protobuf/src/main/java/com/akto/proto/generated/ +buf generate protobuf \ No newline at end of file