1
1
"""
2
- © 2021 Sebastian Wagner <wagner@cert.at>
3
-
2
+ SPDX-FileCopyrightText: 2021 Sebastian Wagner <wagner@cert.at>
3
+ SPDX-FileCopyrightText: 2025 CERT.at GmbH <https://cert.at/>
4
4
SPDX-License-Identifier: AGPL-3.0-or-later
5
5
6
6
https://gitlab.com/intevation/tuency/tuency/-/blob/master/backend/docs/IntelMQ-API.md
@@ -26,6 +26,17 @@ class TuencyExpertBot(ExpertBot):
26
26
authentication_token : str
27
27
overwrite : bool = True
28
28
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
+
29
40
def init (self ):
30
41
self .set_request_parameters ()
31
42
self .session = create_request_session (self )
@@ -44,11 +55,17 @@ def process(self):
44
55
"classification_taxonomy" : event ["classification.taxonomy" ],
45
56
"classification_type" : event ["classification.type" ],
46
57
"feed_provider" : event ["feed.provider" ],
47
- "feed_name" : event ["feed.name" ],
48
58
"feed_status" : "production" ,
49
59
}
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" ]
50
67
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 )
52
69
self .send_message (event )
53
70
self .acknowledge_message ()
54
71
return
@@ -62,24 +79,42 @@ def process(self):
62
79
pass
63
80
64
81
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 )
66
83
67
84
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 )
69
88
else :
70
- if ' interval' not in response :
89
+ if " interval" not in response :
71
90
# empty response
72
91
self .send_message (event )
73
92
self .acknowledge_message ()
74
93
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 )
77
96
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
+ )
79
106
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 ))
83
118
84
119
self .send_message (event )
85
120
self .acknowledge_message ()
0 commit comments