Skip to content

Commit 67d5b9b

Browse files
author
Birger Schacht
committed
ENH: add http status code expert
This adds the module intelmq.bots.experts.http.expert_status.HttpStatusExpertBot and some unit tests for the module. It also adds a description in the documentation and a changelog entry introducing the new bot.
1 parent 9c15409 commit 67d5b9b

File tree

6 files changed

+141
-0
lines changed

6 files changed

+141
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ Update allowed classification fields to 2020-01-28 version (#1409, #1476). Old n
4444

4545
#### Experts
4646
- `intelmq.bots.experts.domain_suffix.expert`: Added `--update-database` option to update domain suffix database.
47+
- Added `intelmq.bots.experts.http.expert_status`: A bot that fetches the HTTP Status for a given URI and adds it to the message.
4748

4849
#### Outputs
4950
- Remove `intelmq.bots.outputs.xmpp`: one of the dependencies of the bot was deprecated and according to a short survey on the IntelMQ

docs/user/bots.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2041,6 +2041,21 @@ Other errors result in an exception if not ignored by the parameter `gaierrors_t
20412041
All gaierrors can be found here: http://www.castaglia.org/proftpd/doc/devel-guide/src/lib/glibc-gai_strerror.c.html
20422042

20432043

2044+
HTTP Status
2045+
^^^^^^^^^^^
2046+
2047+
Fetches the HTTP Status for a given URI
2048+
2049+
**Information**
2050+
2051+
* `name:` intelmq.bots.experts.http.expert_status
2052+
* `description:` The bot fetches the HTTP status for a given URL and saves it in the event.
2053+
2054+
**Configuration Parameters**
2055+
2056+
* `field:` The name of the field containing the URL to be checked (required).
2057+
* `success_status_codes:` A list of success status codes. If this parameter is omitted or the list is empty, successful status codes are the ones between 200 and 400.
2058+
* `overwrite:` Specifies if an existing 'status' value should be overwritten.
20442059

20452060
IDEA Converter
20462061
^^^^^^^^^^^^^^

intelmq/bots/BOTS

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -844,6 +844,15 @@
844844
"overwrite": false
845845
}
846846
},
847+
"HTTP Status": {
848+
"description": "Get the HTTP Status for an URL.",
849+
"module": "intelmq.bots.experts.http.expert_status",
850+
"parameters": {
851+
"field": "source.url",
852+
"success_status_codes": [],
853+
"overwrite": false
854+
}
855+
},
847856
"IDEA Converter": {
848857
"description": "Convert events into the IDEA format.",
849858
"module": "intelmq.bots.experts.idea.expert",

intelmq/bots/experts/http/__init__.py

Whitespace-only changes.
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
"""
2+
HTTP Status Expert Bot
3+
4+
SPDX-FileCopyrightText: 2021 Birger Schacht <[email protected]>
5+
SPDX-License-Identifier: AGPL-3.0-or-later
6+
"""
7+
from typing import List
8+
9+
from intelmq.lib.bot import Bot
10+
from intelmq.lib.utils import create_request_session
11+
12+
13+
class HttpStatusExpertBot(Bot):
14+
"""
15+
Fetch the HTTP Status for a given URL
16+
17+
Parameters
18+
----------
19+
field: str
20+
The name of the field containing the URL to be checked (defaults to 'source.url').
21+
success_status_codes: List
22+
A list of success status codes. If this parameter is omitted or the list is empty,
23+
successful status codes are the ones between 200 and 400.
24+
overwrite:
25+
Specifies if an existing 'status' value should be overwritten.
26+
"""
27+
field: str = "source.url" # The field containing the URL
28+
success_status_codes: List[int] = [] # A list of status codes for success
29+
overwrite: bool = True
30+
31+
def process(self):
32+
event = self.receive_message()
33+
34+
if self.field in event:
35+
self.set_request_parameters()
36+
session = create_request_session(self)
37+
38+
try:
39+
response = session.get(event[self.field])
40+
# If success_status_codes are configured, we use those
41+
# to check the success of the request, otherwise we
42+
# rely on the boolean value of the response
43+
if (self.success_status_codes and response.status_code in self.success_status_codes) or (response):
44+
event.add('status', "online", overwrite=self.overwrite)
45+
else:
46+
event.add('status', 'offline', overwrite=self.overwrite)
47+
event.add('extra.reason', response.reason)
48+
except Exception as exc:
49+
event.add('status', 'offline', overwrite=self.overwrite)
50+
event.add('extra.reason', str(exc))
51+
else:
52+
self.logger.debug('Field %s was not part of the message.', self.field)
53+
54+
self.send_message(event)
55+
self.acknowledge_message()
56+
57+
58+
BOT = HttpStatusExpertBot
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
Testing HTTP Status expert
4+
"""
5+
import unittest
6+
import requests_mock
7+
8+
import intelmq.lib.test as test
9+
import intelmq.lib.utils as utils
10+
from intelmq.bots.experts.http.expert_status import HttpStatusExpertBot
11+
12+
EXAMPLE_INPUT1 = {"__type": "Event",
13+
"source.url": "http://localhost/foo",
14+
"destination.url": "http://localhost/bar",
15+
}
16+
EXAMPLE_OUTPUT1 = {"__type": "Event",
17+
"source.url": "http://localhost/foo",
18+
"destination.url": "http://localhost/bar",
19+
"status": "online",
20+
}
21+
EXAMPLE_OUTPUT2 = {"__type": "Event",
22+
"source.url": "http://localhost/foo",
23+
"destination.url": "http://localhost/bar",
24+
"status": "offline",
25+
}
26+
27+
def prepare_mocker(mocker):
28+
mocker.get('http://localhost/foo', status_code=200)
29+
mocker.get('http://localhost/bar', status_code=404)
30+
31+
32+
@requests_mock.Mocker()
33+
class TestHTTPStatusExpertBot(test.BotTestCase, unittest.TestCase):
34+
"""
35+
A TestCase for HTTPStatusExpertBot.
36+
"""
37+
38+
@classmethod
39+
def set_bot(cls):
40+
cls.bot_reference = HttpStatusExpertBot
41+
42+
def test_status_200(self, mocker):
43+
""" Test with HTTP Status code 200. """
44+
prepare_mocker(mocker)
45+
self.input_message = EXAMPLE_INPUT1
46+
self.run_bot()
47+
self.assertMessageEqual(0, EXAMPLE_OUTPUT1)
48+
49+
def test_status_404(self, mocker):
50+
""" Test with HTTP Status code 404 and a custom field. """
51+
prepare_mocker(mocker)
52+
self.input_message = EXAMPLE_INPUT1
53+
self.prepare_bot(parameters={'field': 'destination.url'})
54+
self.run_bot(prepare=False)
55+
self.assertMessageEqual(0, EXAMPLE_OUTPUT2)
56+
57+
if __name__ == '__main__': # pragma: no cover
58+
unittest.main()

0 commit comments

Comments
 (0)