From 5f9391d5caf6b14dc0a1f91077bf353da8534e0c Mon Sep 17 00:00:00 2001
From: matejsoroka <matej.soroka@wandera.com>
Date: Fri, 5 May 2023 13:16:30 +0200
Subject: [PATCH] Suricata queries

---
 GranefAPI/main.py                     |   3 +-
 GranefAPI/models/query_models.py      |   6 +-
 GranefAPI/routers/suricata_queries.py | 104 ++++++++++++++++++++++++++
 requirements.txt                      |   1 +
 4 files changed, 112 insertions(+), 2 deletions(-)
 create mode 100644 GranefAPI/routers/suricata_queries.py

diff --git a/GranefAPI/main.py b/GranefAPI/main.py
index 1042afb..2d38605 100644
--- a/GranefAPI/main.py
+++ b/GranefAPI/main.py
@@ -46,7 +46,7 @@ from fastapi.middleware.cors import CORSMiddleware
 
 # Custom modules of Granef API
 from utilities.dgraph_client import DgraphClient
-from routers import general_queries, overview_queries, graph_queries, analysis_queries
+from routers import general_queries, overview_queries, graph_queries, analysis_queries, suricata_queries
 
 
 # Application definition ("description" key may be added too).
@@ -60,6 +60,7 @@ app.include_router(general_queries.router, tags=["General"])
 app.include_router(graph_queries.router, prefix="/graph", tags=["Graph queries"])
 app.include_router(overview_queries.router, prefix="/overview", tags=["Overview queries"])
 app.include_router(analysis_queries.router, prefix="/analysis", tags=["Analysis queries"])
+app.include_router(suricata_queries.router, prefix="/suricata", tags=["Suricata queries"])
 
 
 @app.get("/", summary="Get API information", tags=["General"])
diff --git a/GranefAPI/models/query_models.py b/GranefAPI/models/query_models.py
index 099b0ee..fde17ee 100644
--- a/GranefAPI/models/query_models.py
+++ b/GranefAPI/models/query_models.py
@@ -88,4 +88,8 @@ class AdressProtocolTimestampsQuery(BaseModel):
     address: str = Field(None, example='192.168.1.16')
     protocol: str = Field(None, example='HTTP')
     timestamp_min: str = Field(None, example='2008-07-22T01:51:07.095278Z')
-    timestamp_max: str = Field(None, example='2008-07-22T01:55:00')
\ No newline at end of file
+    timestamp_max: str = Field(None, example='2008-07-22T01:55:00')
+
+class AlertFilterQuery(BaseModel):
+    severity: int = Field(0, example='5')
+    regexp: str = Field(None, example='/JA3/')
diff --git a/GranefAPI/routers/suricata_queries.py b/GranefAPI/routers/suricata_queries.py
new file mode 100644
index 0000000..7482b5e
--- /dev/null
+++ b/GranefAPI/routers/suricata_queries.py
@@ -0,0 +1,104 @@
+#! /usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+#
+# Granef -- graph-based network forensics toolkit
+# Copyright (C) 2020-2021  Milan Cermak, Institute of Computer Science of Masaryk University
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+#
+
+
+"""
+Definition of queries providing an overview of the network data.
+"""
+
+# Common Python modules
+import json
+
+# FastAPI modules
+from fastapi import APIRouter
+
+# GranefAPI
+from models import query_models
+from utilities import validation, preprocessing
+from utilities.dgraph_client import DgraphClient
+
+
+# Initialize FastAPI router
+router = APIRouter()
+
+@router.post("/alert",
+    response_model=query_models.GeneralResponseList,
+    summary="Get alerts produced by suricata.")
+def alert(request: query_models.AlertFilterQuery) -> dict:
+    """
+    Get alerts produced by suricata.
+    """
+
+    dgraph_client = DgraphClient()
+
+    regexp = "" if not request.regexp else f"and regexp(alert.alert.signature, {request.regexp})"
+
+    query = f"""{{
+        getAlert(func:type(Alert)) @filter (le(alert.alert.severity, {request.severity}) {regexp} ) {{
+            dgraph.type
+            alert.alert.signature
+            alert.alert.severity
+            alert.alert.action
+        }}
+    }}
+    """
+
+    result = json.loads(dgraph_client.query(preprocessing.add_default_attributes(query)))
+    return {"response": result["getAlert"]}
+
+
+@router.post("/alert-connection",
+    response_model=query_models.GeneralResponseList,
+    summary="Get alerts produced by suricata with related connections.")
+def alert_connections(request: query_models.AlertFilterQuery) -> dict:
+    """
+    Get alerts produced by suricata with related connections.
+    """
+
+    dgraph_client = DgraphClient()
+
+    regexp = "" if not request.regexp else f"and regexp(alert.alert.signature, {request.regexp})"
+
+    query = f"""{{
+        getConnectionsWithAlerts(func:type(Connection)) @filter (has(connection.alert)) @cascade {{
+            dgraph.type
+            ~host.responded {{
+                uid
+                dgraph.type
+                host.ip
+            }}
+            ~host.originated {{
+                uid
+                dgraph.type
+                host.ip
+            }}
+            connection.alert @filter(le(alert.alert.severity, {request.severity}) {regexp} ) {{
+                dgraph.type
+                alert.alert.signature
+                alert.alert.severity
+                alert.alert.action
+            }}
+        }}
+    }}
+    """
+
+    result = json.loads(dgraph_client.query(preprocessing.add_default_attributes(query)))
+    return {"response": result["getConnectionsWithAlerts"]}
diff --git a/requirements.txt b/requirements.txt
index e5603b1..1f853a5 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -6,3 +6,4 @@ gunicorn
 ipaddress
 typing-extensions
 coloredlogs
+networkx
\ No newline at end of file
-- 
GitLab