Skip to content

Commit

Permalink
Prepare Tuency bot for new version
Browse files Browse the repository at this point in the history
In currently developed Tuency version, the quering
capabilities are being extended to support identifier
and feed.code, and the information about consituency
is returned.

In addition, the bot gots more customisation capabilities.
  • Loading branch information
kamil-certat committed Jan 29, 2025
1 parent d3160ce commit 4b26ded
Show file tree
Hide file tree
Showing 4 changed files with 345 additions and 60 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@
- `intelmq.bots.experts.securitytxt`:
- Added new bot (PR#2538 by Frank Westers and Sebastian Wagner)
- `intelmq.bots.experts.misp`: Use `PyMISP` class instead of deprecated `ExpandedPyMISP` (PR#2532 by Radek Vyhnal)
- `intelmq.bots.experts.tuency`: (PR# by Kamil Mańkowski)
- Support for querying using `feed.code` and `classification.identifier` (requires Tuency 2.6+),
- Support for customizing fields and the TTL value for suspended sending.

#### Outputs
- `intelmq.bots.outputs.cif3.output`:
Expand Down
45 changes: 39 additions & 6 deletions docs/user/bots.md
Original file line number Diff line number Diff line change
Expand Up @@ -4073,8 +4073,9 @@ addresses and delivery settings for IP objects (addresses, netblocks), Autonomou

- `classification.taxonomy`
- `classification.type`
- `classification.identifier`
- `feed.provider`
- `feed.name`
- `feed.name` or `feed.code`

These fields therefore need to exist, otherwise the message is skipped.

Expand All @@ -4083,17 +4084,20 @@ The API parameter "feed_status" is currently set to "production" constantly, unt
The API answer is processed as following. For the notification interval:

- If *suppress* is true, then `extra.notify` is set to false.
If explicitly configured, a special TTL value can be set.
- Otherwise:
- If the interval is *immediate*, then `extra.ttl` is set to 0.
- Otherwise the interval is converted into seconds and saved in
`extra.ttl`.
- If the interval is *immediate*, then `extra.ttl` is set to 0.
- Otherwise the interval is converted into seconds and saved in
`extra.ttl`.

For the contact lookup: For both fields *ip* and *domain*, the
*destinations* objects are iterated and its *email* fields concatenated to a comma-separated list
in `source.abuse_contact`.

The IntelMQ fields used by this bot may change in the next IntelMQ release, as soon as better suited fields are
available.
For constituency: if provided from Tuency, the list of relvant consitituencies will
be saved comma-separated in the `extra.constituency` field.

The IntelMQ fields used by this bot may be customized by the parameters.

**Module:** `intelmq.bots.experts.tuency.expert`

Expand All @@ -4111,6 +4115,35 @@ available.

(optional, boolean) Whether the existing data in `source.abuse_contact` should be overwritten. Defaults to true.

**`notify_field`**

(optional, string) Name of the field to save information if the message should not be send
(suspention in Tuency). By default `extra.notify`

**`ttl_field`**

(optional, string) Name of the field to save the TTL value (in seconds). By default `extra.ttl`.

**`constituency_field`**

(optional, string) Name of the gield to save information about the consitutuency. By default
`extra.constituency`. If set to empty value, this information won't be saved.

**`ttl_on_suspended`**

(optional, integer) Custom value to set as TTL when the sending is suspended. By default
not set - no value will be set at all.

**`query_classification_identifier`**

(optional, boolean) Whether to add `classification.identifier` to the query. Requires
at least Tuency 2.6. By default `False`.

**`query_feed_code`**

(optional, boolean) Whether to query using `feed.code` instead of `feed.name`. Requires
at least Tuency 2.6. By default `False`.

---

### Truncate By Delimiter <div id="intelmq.bots.experts.truncate_by_delimiter.expert" />
Expand Down
61 changes: 48 additions & 13 deletions intelmq/bots/experts/tuency/expert.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""
© 2021 Sebastian Wagner <[email protected]>
SPDX-FileCopyrightText: 2021 Sebastian Wagner <[email protected]>
SPDX-FileCopyrightText: 2025 CERT.at GmbH <https://cert.at/>
SPDX-License-Identifier: AGPL-3.0-or-later
https://gitlab.com/intevation/tuency/tuency/-/blob/master/backend/docs/IntelMQ-API.md
Expand All @@ -26,6 +26,17 @@ class TuencyExpertBot(ExpertBot):
authentication_token: str
overwrite: bool = True

notify_field = "extra.notify"
ttl_field = "extra.ttl"
constituency_field = "extra.constituency"

# Allows setting custom TTL for suspended sending
ttl_on_suspended = None

# Non-default values require Tuency v2.6+
query_classification_identifier = False
query_feed_code = False

def init(self):
self.set_request_parameters()
self.session = create_request_session(self)
Expand All @@ -44,11 +55,17 @@ def process(self):
"classification_taxonomy": event["classification.taxonomy"],
"classification_type": event["classification.type"],
"feed_provider": event["feed.provider"],
"feed_name": event["feed.name"],
"feed_status": "production",
}
if self.query_feed_code:
params["feed_code"] = event["feed.code"]
else:
params["feed_name"] = event["feed.name"]

if self.query_classification_identifier:
params["classification_identifier"] = event["classification.identifier"]
except KeyError as exc:
self.logger.debug('Skipping event because of missing field: %s.', exc)
self.logger.debug("Skipping event because of missing field: %s.", exc)
self.send_message(event)
self.acknowledge_message()
return
Expand All @@ -62,24 +79,42 @@ def process(self):
pass

response = self.session.get(self.url, params=params).json()
self.logger.debug('Received response %r.', response)
self.logger.debug("Received response %r.", response)

if response.get("suppress", False):
event["extra.notify"] = False
event.add(self.notify_field, False)
if self.ttl_on_suspended:
event.add(self.ttl_field, self.ttl_on_suspended)
else:
if 'interval' not in response:
if "interval" not in response:
# empty response
self.send_message(event)
self.acknowledge_message()
return
elif response['interval']['unit'] == 'immediate':
event["extra.ttl"] = 0
elif response["interval"]["unit"] == "immediate":
event.add(self.ttl_field, 0)
else:
event["extra.ttl"] = parse_relative(f"{response['interval']['length']} {response['interval']['unit']}") * 60
event.add(
self.ttl_field,
(
parse_relative(
f"{response['interval']['length']} {response['interval']['unit']}"
)
* 60
),
)
contacts = []
for destination in response.get('ip', {'destinations': []})['destinations'] + response.get('domain', {'destinations': []})['destinations']:
contacts.extend(contact['email'] for contact in destination["contacts"])
event.add('source.abuse_contact', ','.join(contacts), overwrite=self.overwrite)
for destination in (
response.get("ip", {"destinations": []})["destinations"]
+ response.get("domain", {"destinations": []})["destinations"]
):
contacts.extend(contact["email"] for contact in destination["contacts"])
event.add("source.abuse_contact", ",".join(contacts), overwrite=self.overwrite)

if self.constituency_field and (
constituencies := response.get("constituencies", [])
):
event.add(self.constituency_field, ",".join(constituencies))

self.send_message(event)
self.acknowledge_message()
Expand Down
Loading

0 comments on commit 4b26ded

Please sign in to comment.