@@ -20,8 +20,6 @@ import (
2020 "tulip/pkg/db"
2121
2222 "github.com/labstack/echo/v4"
23- "go.mongodb.org/mongo-driver/bson"
24- "go.mongodb.org/mongo-driver/bson/primitive"
2523)
2624
2725// Router holds dependencies for handlers
@@ -71,8 +69,6 @@ func (api *Router) getTickInfo(c echo.Context) error {
7169
7270func (api * Router ) query (c echo.Context ) error {
7371
74- // TODO: this is horrible, the API layer should not be aware of the database structure
75-
7672 type flowQueryRequest struct {
7773 IncludeTags []string `json:"includeTags"`
7874 ExcludeTags []string `json:"excludeTags"`
@@ -94,157 +90,41 @@ func (api *Router) query(c echo.Context) error {
9490 return c .JSON (http .StatusBadRequest , apiError {Error : "Invalid request format" })
9591 }
9692
97- filter := bson.D {}
98-
99- // Handle "flow.data" regex filter
100- if req .FlowData != "" {
101- filter = append (filter , bson.E {
102- Key : "flow.data" ,
103- Value : bson.M {
104- "$regex" : req .FlowData ,
105- "$options" : "i" , // Case-insensitive regex
106- },
107- })
108- }
109-
110- // Handle "dst_ip"
111- if req .DstIp != "" {
112- filter = append (filter , bson.E {Key : "dst_ip" , Value : req .DstIp })
113- }
114-
115- // Handle "dst_port"
116- if req .DstPort != 0 {
117- if req .DstPort == - 1 {
118- // Remove dst_ip
119- for i , e := range filter {
120- if e .Key == "dst_ip" {
121- filter = append (filter [:i ], filter [i + 1 :]... )
122- break
123- }
124- }
125-
126- // Exclude all service ports
127- ninPorts := []int {}
128- for _ , svc := range api .Config .Services {
129- if svc .Port != 0 {
130- ninPorts = append (ninPorts , svc .Port )
131- }
132- }
133- filter = append (filter , bson.E {Key : "dst_port" , Value : bson.M {"$nin" : ninPorts }})
134- } else {
135- filter = append (filter , bson.E {Key : "dst_port" , Value : req .DstPort })
136- }
137- }
138-
139- // Handle time range
140- if req .FromTime != 0 && req .ToTime != 0 {
141- filter = append (filter , bson.E {Key : "time" , Value : bson.D {
142- {Key : "$gte" , Value : req .FromTime },
143- {Key : "$lt" , Value : req .ToTime },
144- }})
145- }
146-
147- // Handle tags
148- tagQueries := bson.M {}
149-
150- if len (req .IncludeTags ) > 0 {
151- tagQueries ["$all" ] = req .IncludeTags
152- }
153-
154- if len (req .ExcludeTags ) > 0 {
155- tagQueries ["$nin" ] = req .ExcludeTags
156- }
157-
158- if len (tagQueries ) > 0 {
159- filter = append (filter , bson.E {Key : "tags" , Value : tagQueries })
160- }
161-
162- type apiFlowEntry struct {
163- Id primitive.ObjectID `json:"_id"` // MongoDB unique identifier
164- SrcPort int `json:"src_port"` // Source port
165- DstPort int `json:"dst_port"` // Destination port
166- SrcIp string `json:"src_ip"` // Source IP address
167- DstIp string `json:"dst_ip"` // Destination IP address
168- Time int `json:"time"` // Timestamp (epoch)
169- Duration int `json:"duration"` // Duration in milliseconds
170- Num_packets int `json:"num_packets"` // Number of packets
171- Blocked bool `json:"blocked"`
172- Filename string `json:"filename"` // Name of the pcap file this flow was captured in
173- Fingerprints []uint32 `json:"fingerprints"`
174- Signatures []db.SuricataSig `json:"signatures"` // Signatures matched by this flow
175- Flow []db.FlowItem `json:"flow"`
176- Tags []string `json:"tags"` // Tags associated with this flow, e.g. "starred", "tcp", "udp", "blocked"
177- Size int `json:"size"` // Size of the flow in bytes
178- Flags []string `json:"flags"` // Flags contained in the flow
179- Flagids []string `json:"flagids"` // Flag IDs associated with this flow
180- }
181-
182- // Convert bson.D filter to GetFlowsOptions
18393 opts := & db.FindFlowsOptions {
94+ FromTime : req .FromTime ,
95+ ToTime : req .ToTime ,
96+ IncludeTags : req .IncludeTags ,
97+ ExcludeTags : req .ExcludeTags ,
98+ DstIp : req .DstIp ,
99+ FlowData : req .FlowData ,
184100 Limit : req .Limit ,
185101 Offset : req .Offset ,
186102 Fingerprints : req .Fingerprints ,
187103 }
188104
189- // Set default limit if not specified
190105 if opts .Limit <= 0 {
191106 opts .Limit = 50
192107 }
193108
194- // Parse filter to populate options
195- for _ , elem := range filter {
196- switch elem .Key {
197- case "dst_ip" :
198- if str , ok := elem .Value .(string ); ok {
199- opts .DstIp = str
200- }
201- case "dst_port" :
202- if port , ok := elem .Value .(int ); ok {
203- opts .DstPort = port
204- }
205- case "time" :
206- if timeRange , ok := elem .Value .(bson.D ); ok {
207- for _ , timeElem := range timeRange {
208- switch timeElem .Key {
209- case "$gte" :
210- if val , ok := timeElem .Value .(int64 ); ok {
211- opts .FromTime = val
212- }
213- case "$lt" :
214- if val , ok := timeElem .Value .(int64 ); ok {
215- opts .ToTime = val
216- }
217- }
218- }
219- }
220- case "tags" :
221- if tagQuery , ok := elem .Value .(bson.M ); ok {
222- if all , exists := tagQuery ["$all" ]; exists {
223- if tags , ok := all .([]string ); ok {
224- opts .IncludeTags = tags
225- }
226- }
227- if nin , exists := tagQuery ["$nin" ]; exists {
228- if tags , ok := nin .([]string ); ok {
229- opts .ExcludeTags = tags
230- }
231- }
232- }
233- case "flow.data" :
234- if regexQuery , ok := elem .Value .(bson.M ); ok {
235- if regex , exists := regexQuery ["$regex" ]; exists {
236- if str , ok := regex .(string ); ok {
237- opts .FlowData = str
238- }
239- }
109+ // DstPort == -1 means "exclude all known service ports" (show non-service traffic)
110+ if req .DstPort == - 1 {
111+ excludePorts := make ([]int , 0 , len (api .Config .Services ))
112+ for _ , svc := range api .Config .Services {
113+ if svc .Port != 0 {
114+ excludePorts = append (excludePorts , svc .Port )
240115 }
241116 }
117+ opts .ExcludePorts = excludePorts
118+ } else if req .DstPort != 0 {
119+ opts .DstPort = req .DstPort
242120 }
243121
244- slog .Info ("Querying flows" ,
245- slog .Any ("filter" , filter ),
246- slog .Any ("options" , opts ),
247- )
122+ type apiFlowEntry struct {
123+ db.FlowEntry
124+ Signatures []db.SuricataSig `json:"signatures"` // Signatures matched by this flow
125+ }
126+
127+ slog .Info ("Querying flows" , slog .Any ("options" , opts ))
248128
249129 results , err := api .DB .GetFlows (c .Request ().Context (), opts )
250130 if err != nil {
@@ -287,24 +167,7 @@ func (api *Router) query(c echo.Context) error {
287167
288168 apiResults := make ([]apiFlowEntry , len (results ))
289169 for i , flow := range results {
290- res := apiFlowEntry {
291- Id : flow .Id ,
292- SrcPort : flow .SrcPort ,
293- DstPort : flow .DstPort ,
294- SrcIp : flow .SrcIp ,
295- DstIp : flow .DstIp ,
296- Time : flow .Time ,
297- Duration : flow .Duration ,
298- Num_packets : flow .NumPackets ,
299- Blocked : flow .Blocked ,
300- Filename : flow .Filename ,
301- Fingerprints : flow .Fingerprints ,
302- Flow : flow .Flow ,
303- Tags : flow .Tags ,
304- Size : flow .Size ,
305- Flags : flow .Flags ,
306- Flagids : flow .Flagids ,
307- }
170+ res := apiFlowEntry {FlowEntry : flow }
308171
309172 res .Signatures = make ([]db.SuricataSig , 0 , len (flow .Suricata ))
310173 for _ , sigID := range flow .Suricata {
0 commit comments