diff --git a/CHANGELOG.md b/CHANGELOG.md index d4eb59e99..d57483513 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,23 @@ CHANGELOG ========== +1.0.1 Bugfix release +-------------------- +### Documentation +- Feeds: use more https:// URLs +- minor fixes + +### Bots +- bots/experts/ripencc_abuse_contact/expert.py: Use HTTPS URLs for rest.db.ripe.net +- bots/outputs/file/output.py: properly close the file handle on shutdown + +### Core +- lib/bot: Bots will now log the used intelmq version at startup + +### Tools +- intelmqctl: To check the status of a bot, the comandline of the running process is compared to the actual executable of the bot. Otherwise unrelated programs with the same PID are detected as running bot. +- intelmqctl: enable, disable, check, clear now support the JSON output + 1.0.0 Stable release -------------------- ### Core diff --git a/NEWS.md b/NEWS.md index c49e7c581..e338b69b3 100644 --- a/NEWS.md +++ b/NEWS.md @@ -3,6 +3,10 @@ NEWS See the changelog for a full list of changes. +1.0.1 Bugfix release +-------------------- +No changes needed. + 1.0.0 Stable release -------------------- ### Configuration diff --git a/debian/changelog b/debian/changelog index 663e79eb5..b8623da31 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,15 @@ +intelmq (1.0.1-1) experimental; urgency=low + + * update to version 1.0.1 + + -- Wagner Sebastian Wed, 30 Aug 2017 15:07:45 +0200 + +intelmq (1.0.1~rc1-1) experimental; urgency=low + + * release release candidate 1.0.1 RC + + -- Wagner Sebastian Wed, 23 Aug 2017 16:02:58 +0200 + intelmq (1.0.0.rel-1) experimental; urgency=medium * release version 1.0.0 diff --git a/docs/Developers-Guide.md b/docs/Developers-Guide.md index aefc8703b..4733f3860 100644 --- a/docs/Developers-Guide.md +++ b/docs/Developers-Guide.md @@ -115,6 +115,13 @@ For example, to run all tests you can use: INTELMQ_TEST_DATABASES=1 INTELMQ_TEST_LOCAL_WEB=1 INTELMQ_TEST_EXOTIC=1 nosetests ``` +### Configuration files + +The tests use the configuration files in your working directory, not those +installed in `/opt/intelmq/etc/` or `/etc/`. You can run the +tests for a locally changed intelmq without affecting an installation or +requiring root to run them. + # Development Guidelines ## Coding-Rules diff --git a/docs/Feeds.md b/docs/Feeds.md index a671620cf..6d85944d1 100644 --- a/docs/Feeds.md +++ b/docs/Feeds.md @@ -357,7 +357,7 @@ id: bambenek-c2-domains-collector provider: Bambenek feed: Bambenek C2 Domains rate_limit: FIXME -http_url: http://osint.bambenekconsulting.com/feeds/c2-dommasterlist.txt +http_url: https://osint.bambenekconsulting.com/feeds/c2-dommasterlist.txt ``` ### Parser Bot @@ -372,7 +372,7 @@ id: bambenek-c2-domains-parser ``` **Notes:** Master Feed of known, active and non-sinkholed C&Cs domain names. -License: http://osint.bambenekconsulting.com/license.txt +License: https://osint.bambenekconsulting.com/license.txt ## C2 IPs @@ -390,7 +390,7 @@ id: bambenek-c2-ips-collector provider: Bambenek feed: Bambenek C2 IPs rate_limit: FIXME -http_url: http://osint.bambenekconsulting.com/feeds/c2-ipmasterlist.txt +http_url: https://osint.bambenekconsulting.com/feeds/c2-ipmasterlist.txt ``` ### Parser Bot @@ -405,7 +405,7 @@ id: bambenek-c2-ips-parser ``` **Notes:** Master Feed of known, active and non-sinkholed C&Cs IP addresses -License: http://osint.bambenekconsulting.com/license.txt +License: https://osint.bambenekconsulting.com/license.txt ## DGA Domains @@ -423,7 +423,7 @@ id: bambenek-dga-domains-collector provider: Bambenek feed: Bambenek DGA Domains rate_limit: FIXME -http_url: http://osint.bambenekconsulting.com/feeds/dga-feed.txt +http_url: https://osint.bambenekconsulting.com/feeds/dga-feed.txt ``` ### Parser Bot @@ -438,7 +438,7 @@ id: bambenek-dga-domains-parser ``` **Notes:** Domain feed of known DGA domains from -2 to +3 days -License: http://osint.bambenekconsulting.com/license.txt +License: https://osint.bambenekconsulting.com/license.txt # Bitcash @@ -457,7 +457,7 @@ id: bitcash-collector provider: BitCash feed: BitCash rate_limit: FIXME -http_url: http://bitcash.cz/misc/log/blacklist +http_url: https://bitcash.cz/misc/log/blacklist ``` ## Parser Bot diff --git a/docs/User-Guide.md b/docs/User-Guide.md index fe55b55e2..29b6f1cf8 100644 --- a/docs/User-Guide.md +++ b/docs/User-Guide.md @@ -428,7 +428,7 @@ In many cases, it is useful to schedule a bot at a specific time (i.e. via cron( "module": "intelmq.bots.collectors.http.collector_http", "description": "All IP addresses which have been reported within the last 48 hours as having run attacks on the service Apache, Apache-DDOS, RFI-Attacks.", "enabled": false, - "run_mode": "scheduled" + "run_mode": "scheduled", "parameters": { "feed": "Blocklist.de Apache", "provider": "Blocklist.de", @@ -457,7 +457,7 @@ Most of the cases, bots will need to be configured as `continuous` run mode (the "module": "intelmq.bots.parsers.blocklistde.parser", "description": "Blocklist.DE Parser is the bot responsible to parse the report and sanitize the information.", "enabled": false, - "run_mode": "continuous" + "run_mode": "continuous", "parameters": { }, }, diff --git a/intelmq/bin/intelmqctl.py b/intelmq/bin/intelmqctl.py index 38aa4a32c..e1835fd79 100644 --- a/intelmq/bin/intelmqctl.py +++ b/intelmq/bin/intelmqctl.py @@ -4,6 +4,8 @@ import importlib import json import os +import re +import shutil import signal import subprocess import time @@ -12,7 +14,7 @@ import psutil from intelmq import (DEFAULTS_CONF_FILE, PIPELINE_CONF_FILE, RUNTIME_CONF_FILE, - VAR_RUN_PATH, BOTS_FILE) + VAR_RUN_PATH, BOTS_FILE, HARMONIZATION_CONF_FILE) from intelmq.lib import utils from intelmq.lib.bot_debugger import BotDebugger from intelmq.lib.pipeline import PipelineFactory @@ -121,7 +123,8 @@ def __init__(self, runtime_configuration, logger, controller): def bot_run(self, bot_id, run_subcommand=None, console_type=None, message_action_kind=None, dryrun=None, msg=None): pid = self.__read_pidfile(bot_id) - if pid and self.__status_process(pid): + module = self.__runtime_configuration[bot_id]['module'] + if pid and self.__status_process(pid, module): self.logger.warning("Main instance of the bot is running in the background and will be stopped; " "when finished, we try to relaunch it again. " "You may want to launch: 'intelmqctl stop {}' to prevent this message." @@ -154,8 +157,9 @@ def bot_run(self, bot_id, run_subcommand=None, console_type=None, message_action def bot_start(self, bot_id, getstatus=True): pid = self.__read_pidfile(bot_id) + module = self.__runtime_configuration[bot_id]['module'] if pid: - if self.__status_process(pid): + if self.__status_process(pid, module): log_bot_message('running', bot_id) return 'running' else: @@ -180,6 +184,7 @@ def bot_start(self, bot_id, getstatus=True): def bot_stop(self, bot_id, getstatus=True): pid = self.__read_pidfile(bot_id) + module = self.__runtime_configuration[bot_id]['module'] if not pid: if self.controller._is_enabled(bot_id): log_bot_error('stopped', bot_id) @@ -187,7 +192,7 @@ def bot_stop(self, bot_id, getstatus=True): else: log_bot_message('disabled', bot_id) return 'disabled' - if not self.__status_process(pid): + if not self.__status_process(pid, module): self.__remove_pidfile(bot_id) log_bot_error('stopped', bot_id) return 'stopped' @@ -201,7 +206,7 @@ def bot_stop(self, bot_id, getstatus=True): else: if getstatus: time.sleep(0.5) - if self.__status_process(pid): + if self.__status_process(pid, module): log_bot_error('running', bot_id) return 'running' try: @@ -213,6 +218,7 @@ def bot_stop(self, bot_id, getstatus=True): def bot_reload(self, bot_id, getstatus=True): pid = self.__read_pidfile(bot_id) + module = self.__runtime_configuration[bot_id]['module'] if not pid: if self.controller._is_enabled(bot_id): log_bot_error('stopped', bot_id) @@ -220,7 +226,7 @@ def bot_reload(self, bot_id, getstatus=True): else: log_bot_message('disabled', bot_id) return 'disabled' - if not self.__status_process(pid): + if not self.__status_process(pid, module): self.__remove_pidfile(bot_id) log_bot_error('stopped', bot_id) return 'stopped' @@ -234,7 +240,7 @@ def bot_reload(self, bot_id, getstatus=True): else: if getstatus: time.sleep(0.5) - if self.__status_process(pid): + if self.__status_process(pid, module): log_bot_message('running', bot_id) return 'running' log_bot_error('stopped', bot_id) @@ -242,11 +248,14 @@ def bot_reload(self, bot_id, getstatus=True): def bot_status(self, bot_id): pid = self.__read_pidfile(bot_id) - if pid and self.__status_process(pid): + module = self.__runtime_configuration[bot_id]['module'] + if pid and self.__status_process(pid, module): log_bot_message('running', bot_id) return 'running' if self.controller._is_enabled(bot_id): + if self.__check_pidfile(bot_id): + self.__remove_pidfile(bot_id) log_bot_message('stopped', bot_id) return 'stopped' else: @@ -276,10 +285,11 @@ def __remove_pidfile(self, bot_id): filename = self.PIDFILE.format(bot_id) os.remove(filename) - def __status_process(self, pid): + def __status_process(self, pid, module): try: - psutil.Process(int(pid)) - return True + proc = psutil.Process(int(pid)) + if len(proc.cmdline()) > 1 and proc.cmdline()[1] == shutil.which(module): + return True except psutil.NoSuchProcess: return False except psutil.AccessDenied: @@ -592,10 +602,12 @@ def bot_status(self, bot_id): def bot_enable(self, bot_id): self.runtime_configuration[bot_id]['enabled'] = True self.write_updated_runtime_config() + return self.bot_process_manager.bot_status(bot_id) def bot_disable(self, bot_id): self.runtime_configuration[bot_id]['enabled'] = False self.write_updated_runtime_config() + return self.bot_process_manager.bot_status(bot_id) def _is_enabled(self, bot_id): return self.runtime_configuration[bot_id].get('enabled', True) @@ -739,7 +751,8 @@ def clear_queue(self, queue): First checks if the queue does exist in the pipeline configuration. """ - logger.info("Clearing queue %s", queue) + if RETURN_TYPE == 'text': + logger.info("Clearing queue %s", queue) queues = set() for key, value in self.pipeline_configuration.items(): if 'source-queue' in value: @@ -753,12 +766,14 @@ def clear_queue(self, queue): pipeline.connect() if queue not in queues: - logger.error("Queue %s does not exist!", queue) + if RETURN_TYPE == 'text': + logger.error("Queue %s does not exist!", queue) return 'not-found' try: pipeline.clear_queue(queue) - logger.info("Successfully cleared queue %s.", queue) + if RETURN_TYPE == 'text': + logger.info("Successfully cleared queue %s.", queue) return 'success' except Exception: # pragma: no cover logger.exception("Error while clearing queue %s.", @@ -817,38 +832,66 @@ def read_bot_log(self, bot_id, log_level, number_of_lines): def check(self): retval = 0 + if RETURN_TYPE == 'json': + output = [] + # loading files and syntax check files = {DEFAULTS_CONF_FILE: None, PIPELINE_CONF_FILE: None, - RUNTIME_CONF_FILE: None, BOTS_FILE: None} - self.logger.info('Reading configuration files.') + RUNTIME_CONF_FILE: None, BOTS_FILE: None, + HARMONIZATION_CONF_FILE: None} + if RETURN_TYPE == 'json': + output.append(['info', 'Reading configuration files.']) + else: + self.logger.info('Reading configuration files.') for filename in files: try: with open(filename) as file_handle: files[filename] = json.load(file_handle) except (IOError, ValueError) as exc: # pragma: no cover - self.logger.error('Coud not load %r: %s.', filename, exc) + if RETURN_TYPE == 'json': + output.append(['error', 'Coud not load %r: %s.' % (filename, exc)]) + else: + self.logger.error('Coud not load %r: %s.', filename, exc) retval = 1 if retval: - self.logger.error('Fatal errors occurred.') + if RETURN_TYPE == 'json': + return {'status': 'error', 'lines': output} + else: + self.logger.error('Fatal errors occurred.') return retval - self.logger.info('Checking runtime configuration.') + if RETURN_TYPE == 'json': + output.append(['info', 'Checking runtime configuration.']) + else: + self.logger.info('Checking runtime configuration.') http_proxy = files[DEFAULTS_CONF_FILE].get('http_proxy') https_proxy = files[DEFAULTS_CONF_FILE].get('https_proxy') # Either both are given or both are not given if (not http_proxy or not https_proxy) and not (http_proxy == https_proxy): - self.logger.warning('Incomplete configuration: Both http and https proxies must be set.') + if RETURN_TYPE == 'json': + output.append(['warning', 'Incomplete configuration: Both http and https proxies must be set.']) + else: + self.logger.warning('Incomplete configuration: Both http and https proxies must be set.') retval = 1 - self.logger.info('Checking pipeline configuration.') + if RETURN_TYPE == 'json': + output.append(['info', 'Checking pipeline configuration.']) + else: + self.logger.info('Checking pipeline configuration.') for bot_id, bot_config in files[RUNTIME_CONF_FILE].items(): # pipeline keys for field in ['description', 'group', 'module', 'name']: if field not in bot_config: - self.logger.warning('Bot %r has no %r.', bot_id, field) + if RETURN_TYPE == 'json': + output.append(['warning', 'Bot %r has no %r.' % (bot_id, field)]) + else: + self.logger.warning('Bot %r has no %r.', bot_id, field) retval = 1 if bot_id not in files[PIPELINE_CONF_FILE]: - self.logger.error('Misconfiguration: No pipeline configuration found for %r.', bot_id) + if RETURN_TYPE == 'json': + output.append(['error', 'Misconfiguration: No pipeline configuration found for %r.' % bot_id]) + else: + self.logger.error('Misconfiguration: No pipeline configuration found for %r.', bot_id) retval = 1 else: if ('group' in bot_config and @@ -856,36 +899,87 @@ def check(self): ('destination-queues' not in files[PIPELINE_CONF_FILE][bot_id] or (not isinstance(files[PIPELINE_CONF_FILE][bot_id]['destination-queues'], list) or len(files[PIPELINE_CONF_FILE][bot_id]['destination-queues']) < 1))): - self.logger.error('Misconfiguration: No destination queues for %r.', bot_id) + if RETURN_TYPE == 'json': + output.append(['error', 'Misconfiguration: No destination queues for %r.' % bot_id]) + else: + self.logger.error('Misconfiguration: No destination queues for %r.', bot_id) retval = 1 if ('group' in bot_config and bot_config['group'] in ['Parser', 'Expert', 'Output'] and ('source-queue' not in files[PIPELINE_CONF_FILE][bot_id] or not isinstance(files[PIPELINE_CONF_FILE][bot_id]['source-queue'], str))): - self.logger.error('Misconfiguration: No source queue for %r.', bot_id) + if RETURN_TYPE == 'json': + output.append(['error', 'Misconfiguration: No source queue for %r.' % bot_id]) + else: + self.logger.error('Misconfiguration: No source queue for %r.', bot_id) + retval = 1 + + if RETURN_TYPE == 'json': + output.append(['info', 'Checking harmoization configuration.']) + else: + self.logger.info('Checking harmoization configuration.') + for event_type, event_type_conf in files[HARMONIZATION_CONF_FILE].items(): + for harm_type_name, harm_type in event_type_conf.items(): + if "description" not in harm_type: + if RETURN_TYPE == 'json': + output.append(['warn', 'Missing description for type %r.' % harm_type_name]) + else: + self.logger.warn('Missing description for type %r.', harm_type_name) + if "type" not in harm_type: + if RETURN_TYPE == 'json': + output.append(['error', 'Missing type for type %r.' % harm_type_name]) + else: + self.logger.error('Missing type for type %r.', harm_type_name) retval = 1 + continue + if "regex" in harm_type: + try: + re.compile(harm_type['regex']) + except Exception as e: + if RETURN_TYPE == 'json': + output.append(['error', 'Invalid regex for type %r: %r.' % (harm_type_name, str(e))]) + else: + self.logger.error('Invalid regex for type %r: %r.', harm_type_name, str(e)) + retval = 1 + continue - self.logger.info('Checking for bots.') + if RETURN_TYPE == 'json': + output.append(['info', 'Checking for bots.']) + else: + self.logger.info('Checking for bots.') for bot_id, bot_config in files[RUNTIME_CONF_FILE].items(): # importable module try: importlib.import_module(bot_config['module']) except ImportError: - self.logger.error('Incomplete installation: Module %r not importable.', bot_id) + if RETURN_TYPE == 'json': + output.append(['error', 'Incomplete installation: Module %r not importable.' % bot_id]) + else: + self.logger.error('Incomplete installation: Module %r not importable.', bot_id) retval = 1 for group in files[BOTS_FILE].values(): for bot_id, bot in group.items(): - if subprocess.call(['which', bot['module']], stdout=subprocess.DEVNULL): - self.logger.error('Incomplete installation: Executable %r for %r not found.', - bot['module'], bot_id) + if subprocess.call(['which', bot['module']], stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL): + if RETURN_TYPE == 'json': + output.append(['error', 'Incomplete installation: Executable %r for %r not found.' % + (bot['module'], bot_id)]) + else: + self.logger.error('Incomplete installation: Executable %r for %r not found.', + bot['module'], bot_id) retval = 1 - if retval: - self.logger.error('Some issues have been found, please check the above output.') + if RETURN_TYPE == 'json': + if retval: + return {'status': 'error', 'lines': output} + else: + return {'status': 'success', 'lines': output} else: - self.logger.info('No issues found.') - - return retval + if retval: + self.logger.error('Some issues have been found, please check the above output.') + else: + self.logger.info('No issues found.') + return retval def main(): # pragma: no cover diff --git a/intelmq/bots/collectors/n6/REQUIREMENTS.txt b/intelmq/bots/collectors/n6/REQUIREMENTS.txt index 61862fda8..0cfc2c497 100644 --- a/intelmq/bots/collectors/n6/REQUIREMENTS.txt +++ b/intelmq/bots/collectors/n6/REQUIREMENTS.txt @@ -1 +1 @@ -stomp.py==4.1.8 +stomp.py>=4.1.8 diff --git a/intelmq/bots/experts/generic_db_lookup/README.md b/intelmq/bots/experts/generic_db_lookup/README.md index e67417d53..55834a217 100644 --- a/intelmq/bots/experts/generic_db_lookup/README.md +++ b/intelmq/bots/experts/generic_db_lookup/README.md @@ -11,9 +11,9 @@ If more than one result is returned, a ValueError is raised. * `database`: string, defaults to "intelmq" * `host`: string, defaults to "localhost" -* `password: string -* `port: integer, defaults to 5432 -* `sslmode: string, defaults to "require" +* `password`: string +* `port`: integer, defaults to 5432 +* `sslmode`: string, defaults to "require" * `table`: defaults to "contacts" * `user`: defaults to "intelmq" diff --git a/intelmq/bots/experts/ripencc_abuse_contact/expert.py b/intelmq/bots/experts/ripencc_abuse_contact/expert.py index 9c0a9de19..886011d48 100644 --- a/intelmq/bots/experts/ripencc_abuse_contact/expert.py +++ b/intelmq/bots/experts/ripencc_abuse_contact/expert.py @@ -17,8 +17,8 @@ class RIPENCCExpertBot(Bot): - URL_DB_IP = 'http://rest.db.ripe.net/abuse-contact/{}.json' - URL_DB_AS = 'http://rest.db.ripe.net/abuse-contact/as{}.json' + URL_DB_IP = 'https://rest.db.ripe.net/abuse-contact/{}.json' + URL_DB_AS = 'https://rest.db.ripe.net/abuse-contact/as{}.json' URL_STAT = ('https://stat.ripe.net/data/abuse-contact-finder/' 'data.json?resource={}') diff --git a/intelmq/bots/outputs/file/output.py b/intelmq/bots/outputs/file/output.py index 8e88403a6..32d7fd5f1 100644 --- a/intelmq/bots/outputs/file/output.py +++ b/intelmq/bots/outputs/file/output.py @@ -24,5 +24,8 @@ def process(self): else: self.acknowledge_message() + def shutdown(self): + self.file.close() + BOT = FileOutputBot diff --git a/intelmq/bots/parsers/bambenek/parser.py b/intelmq/bots/parsers/bambenek/parser.py index 745c751c8..505e6e56f 100644 --- a/intelmq/bots/parsers/bambenek/parser.py +++ b/intelmq/bots/parsers/bambenek/parser.py @@ -8,10 +8,17 @@ class BambenekParserBot(ParserBot): """ Single parser for Bambenek feeds """ IPMASTERLIST = { - 'http://osint.bambenekconsulting.com/feeds/c2-ipmasterlist.txt'} + 'http://osint.bambenekconsulting.com/feeds/c2-ipmasterlist.txt', + 'https://osint.bambenekconsulting.com/feeds/c2-ipmasterlist.txt', + } DOMMASTERLIST = { - 'http://osint.bambenekconsulting.com/feeds/c2-dommasterlist.txt'} - DGA_FEED = {'http://osint.bambenekconsulting.com/feeds/dga-feed.txt'} + 'http://osint.bambenekconsulting.com/feeds/c2-dommasterlist.txt', + 'https://osint.bambenekconsulting.com/feeds/c2-dommasterlist.txt', + } + DGA_FEED = { + 'http://osint.bambenekconsulting.com/feeds/dga-feed.txt', + 'https://osint.bambenekconsulting.com/feeds/dga-feed.txt', + } MALWARE_NAME_MAP = { "cl": "cryptolocker", diff --git a/intelmq/bots/parsers/dshield/parser_block.py b/intelmq/bots/parsers/dshield/parser_block.py index c755a885d..d3069d963 100644 --- a/intelmq/bots/parsers/dshield/parser_block.py +++ b/intelmq/bots/parsers/dshield/parser_block.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ -# primary URL: http://feeds.dshield.org/block.txt -# PGP Sign.: http://feeds.dshield.org/block.txt.asc +# primary URL: https://feeds.dshield.org/block.txt +# PGP Sign.: https://feeds.dshield.org/block.txt.asc # # updated: Tue Dec 15 15:33:38 2015 UTC # diff --git a/intelmq/lib/bot.py b/intelmq/lib/bot.py index e6fc81da6..cc4068fa7 100644 --- a/intelmq/lib/bot.py +++ b/intelmq/lib/bot.py @@ -16,7 +16,7 @@ from intelmq import (DEFAULT_LOGGING_PATH, DEFAULTS_CONF_FILE, HARMONIZATION_CONF_FILE, PIPELINE_CONF_FILE, - RUNTIME_CONF_FILE) + RUNTIME_CONF_FILE, __version__) from intelmq.lib import exceptions, utils import intelmq.lib.message as libmessage from intelmq.lib.pipeline import PipelineFactory @@ -46,11 +46,11 @@ def __init__(self, bot_id: str): try: version_info = sys.version.splitlines()[0].strip() self.__log_buffer.append(('info', - '{} initialized with id {} and version ' - '{} as process {}.' - ''.format(self.__class__.__name__, - bot_id, version_info, - os.getpid()))) + '{bot} initialized with id {id} and intelmq {intelmq}' + ' and python {python} as process {pid}.' + ''.format(bot=self.__class__.__name__, + id=bot_id, python=version_info, + pid=os.getpid(), intelmq=__version__))) self.__log_buffer.append(('debug', 'Library path: %r.' % __file__)) self.__load_defaults_configuration() diff --git a/intelmq/lib/test.py b/intelmq/lib/test.py index 92c4f13aa..28d9cc997 100644 --- a/intelmq/lib/test.py +++ b/intelmq/lib/test.py @@ -255,7 +255,7 @@ def run_bot(self, iterations: int=1, error_on_pipeline: bool=False, prepare=True self.assertIn('raw', event) """ Test if bot log messages are correctly formatted. """ - self.assertLoglineMatches(0, "{} initialized with id {} and version" + self.assertLoglineMatches(0, "{} initialized with id {} and intelmq [0-9a-z.]* and python" " [0-9.]{{5}}\\+? \([a-zA-Z0-9,:. ]+\)( \[GCC\])?" " as process [0-9]+\." "".format(self.bot_name, diff --git a/intelmq/tests/bots/experts/cymru_whois/test_expert.py b/intelmq/tests/bots/experts/cymru_whois/test_expert.py index 11e1c1761..bb2504d6a 100644 --- a/intelmq/tests/bots/experts/cymru_whois/test_expert.py +++ b/intelmq/tests/bots/experts/cymru_whois/test_expert.py @@ -71,8 +71,8 @@ EXAMPLE_6TO4_OUTPUT = {"__type": "Event", "source.ip": "2002:3ee0:3972:0001::1", "source.network": "2002::/16", - "source.asn": 1103, - "source.as_name": "SURFNET-NL SURFnet, The Netherlands, NL", + "source.asn": 6939, + "source.as_name": "HURRICANE - Hurricane Electric, Inc., US", "time.observation": "2015-01-01T00:00:00+00:00", } diff --git a/intelmq/version.py b/intelmq/version.py index 0df0cc846..a770be9ca 100644 --- a/intelmq/version.py +++ b/intelmq/version.py @@ -1,2 +1,2 @@ -__version_info__ = ('1', '0', '0') +__version_info__ = ('1', '0', '1') __version__ = '.'.join(__version_info__) diff --git a/setup.py b/setup.py index 7e82f7f67..285f04638 100644 --- a/setup.py +++ b/setup.py @@ -73,18 +73,21 @@ 'processing security feeds using a message queuing protocol.', long_description=README, classifiers=[ - 'Development Status :: 3 - Alpha', + 'Development Status :: 5 - Production/Stable', 'Environment :: Console', 'Intended Audience :: Information Technology', 'Intended Audience :: System Administrators', 'Intended Audience :: Telecommunications Industry', 'License :: OSI Approved :: GNU Affero General Public License v3', - 'Operating System :: Unix', + 'Operating System :: POSIX :: Linux', + 'Programming Language :: Python', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3 :: Only', + 'Programming Language :: Python :: Implementation :: CPython', 'Topic :: Security', ], keywords='incident handling cert csirt',