11"""
2- © 2021 Sebastian Wagner <[email protected] >3-
2+ SPDX-FileCopyrightText: 2021 Sebastian Wagner <[email protected] >3+ SPDX-FileCopyrightText: 2025 CERT.at GmbH <https://cert.at/>
44SPDX-License-Identifier: AGPL-3.0-or-later
55
66https://gitlab.com/intevation/tuency/tuency/-/blob/master/backend/docs/IntelMQ-API.md
@@ -26,6 +26,17 @@ class TuencyExpertBot(ExpertBot):
2626 authentication_token : str
2727 overwrite : bool = True
2828
29+ notify_field = "extra.notify"
30+ ttl_field = "extra.ttl"
31+ constituency_field = "extra.constituency"
32+
33+ # Allows setting custom TTL for suspended sending
34+ ttl_on_suspended = None
35+
36+ # Non-default values require Tuency v2.6+
37+ query_classification_identifier = False
38+ query_feed_code = False
39+
2940 def init (self ):
3041 self .set_request_parameters ()
3142 self .session = create_request_session (self )
@@ -44,11 +55,17 @@ def process(self):
4455 "classification_taxonomy" : event ["classification.taxonomy" ],
4556 "classification_type" : event ["classification.type" ],
4657 "feed_provider" : event ["feed.provider" ],
47- "feed_name" : event ["feed.name" ],
4858 "feed_status" : "production" ,
4959 }
60+ if self .query_feed_code :
61+ params ["feed_code" ] = event ["feed.code" ]
62+ else :
63+ params ["feed_name" ] = event ["feed.name" ]
64+
65+ if self .query_classification_identifier :
66+ params ["classification_identifier" ] = event ["classification.identifier" ]
5067 except KeyError as exc :
51- self .logger .debug (' Skipping event because of missing field: %s.' , exc )
68+ self .logger .debug (" Skipping event because of missing field: %s." , exc )
5269 self .send_message (event )
5370 self .acknowledge_message ()
5471 return
@@ -62,24 +79,42 @@ def process(self):
6279 pass
6380
6481 response = self .session .get (self .url , params = params ).json ()
65- self .logger .debug (' Received response %r.' , response )
82+ self .logger .debug (" Received response %r." , response )
6683
6784 if response .get ("suppress" , False ):
68- event ["extra.notify" ] = False
85+ event .add (self .notify_field , False )
86+ if self .ttl_on_suspended :
87+ event .add (self .ttl_field , self .ttl_on_suspended )
6988 else :
70- if ' interval' not in response :
89+ if " interval" not in response :
7190 # empty response
7291 self .send_message (event )
7392 self .acknowledge_message ()
7493 return
75- elif response [' interval' ][ ' unit' ] == ' immediate' :
76- event [ "extra.ttl" ] = 0
94+ elif response [" interval" ][ " unit" ] == " immediate" :
95+ event . add ( self . ttl_field , 0 )
7796 else :
78- event ["extra.ttl" ] = parse_relative (f"{ response ['interval' ]['length' ]} { response ['interval' ]['unit' ]} " ) * 60
97+ event .add (
98+ self .ttl_field ,
99+ (
100+ parse_relative (
101+ f"{ response ['interval' ]['length' ]} { response ['interval' ]['unit' ]} "
102+ )
103+ * 60
104+ ),
105+ )
79106 contacts = []
80- for destination in response .get ('ip' , {'destinations' : []})['destinations' ] + response .get ('domain' , {'destinations' : []})['destinations' ]:
81- contacts .extend (contact ['email' ] for contact in destination ["contacts" ])
82- event .add ('source.abuse_contact' , ',' .join (contacts ), overwrite = self .overwrite )
107+ for destination in (
108+ response .get ("ip" , {"destinations" : []})["destinations" ]
109+ + response .get ("domain" , {"destinations" : []})["destinations" ]
110+ ):
111+ contacts .extend (contact ["email" ] for contact in destination ["contacts" ])
112+ event .add ("source.abuse_contact" , "," .join (contacts ), overwrite = self .overwrite )
113+
114+ if self .constituency_field and (
115+ constituencies := response .get ("constituencies" , [])
116+ ):
117+ event .add (self .constituency_field , "," .join (constituencies ))
83118
84119 self .send_message (event )
85120 self .acknowledge_message ()
0 commit comments