Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-shodan for openSUSE:Factory checked in at 2022-03-05 14:44:37 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-shodan (Old) and /work/SRC/openSUSE:Factory/.python-shodan.new.1958 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-shodan" Sat Mar 5 14:44:37 2022 rev:29 rq:959539 version:1.27.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-shodan/python-shodan.changes 2022-01-27 23:18:23.662386441 +0100 +++ /work/SRC/openSUSE:Factory/.python-shodan.new.1958/python-shodan.changes 2022-03-05 14:45:19.451729795 +0100 @@ -1,0 +2,14 @@ +Fri Feb 25 20:15:17 UTC 2022 - Sebastian Wagner <sebix+novell....@sebix.at> + +- - update to version 1.27.0: + - New command: ``shodan alert export`` to save the current network monitoring configuration + - New command: ``shodan alert import`` to restore a previous network monitoring configuration + - Automatically rate limit API requests to 1 request per second (credit to @malvidin) +- update to version 1.26.1: + - Fix a unicode issue that caused the streams to get truncated and error out due to invalid JSON +- update to version 1.26.0: + - Add the ability to create custom data streams in the Shodan() class as well as the CLI (``shodan stream --custom-filters <query>``) +- update to version 1.25.0: + - Add new CLI command: shodan alert download + +------------------------------------------------------------------- Old: ---- shodan-1.26.1.tar.gz New: ---- shodan-1.27.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-shodan.spec ++++++ --- /var/tmp/diff_new_pack.qKj6mX/_old 2022-03-05 14:45:19.907729910 +0100 +++ /var/tmp/diff_new_pack.qKj6mX/_new 2022-03-05 14:45:19.907729910 +0100 @@ -19,7 +19,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} %{!?license: %global license %doc} Name: python-shodan -Version: 1.26.1 +Version: 1.27.0 Release: 0 Summary: Python library and command-line utility for Shodan License: MIT ++++++ shodan-1.26.1.tar.gz -> shodan-1.27.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/shodan-1.26.1/CHANGELOG.md new/shodan-1.27.0/CHANGELOG.md --- old/shodan-1.26.1/CHANGELOG.md 2021-01-25 23:31:44.000000000 +0100 +++ new/shodan-1.27.0/CHANGELOG.md 2022-02-23 00:00:28.000000000 +0100 @@ -1,6 +1,20 @@ CHANGELOG ========= +1.27.0 +------ +* New command: ``shodan alert export`` to save the current network monitoring configuration +* New command: ``shodan alert import`` to restore a previous network monitoring configuration +* Automatically rate limit API requests to 1 request per second (credit to @malvidin) + +1.26.1 +------ +* Fix a unicode issue that caused the streams to get truncated and error out due to invalid JSON + +1.26.0 +------ +* Add the ability to create custom data streams in the Shodan() class as well as the CLI (``shodan stream --custom-filters <query>``) + 1.25.0 ------ * Add new CLI command: shodan alert download diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/shodan-1.26.1/PKG-INFO new/shodan-1.27.0/PKG-INFO --- old/shodan-1.26.1/PKG-INFO 2022-01-19 16:47:33.000000000 +0100 +++ new/shodan-1.27.0/PKG-INFO 2022-02-23 00:00:57.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: shodan -Version: 1.26.1 +Version: 1.27.0 Summary: Python library and command-line utility for Shodan (https://developer.shodan.io) Home-page: https://github.com/achillean/shodan-python Author: John Matherly diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/shodan-1.26.1/setup.py new/shodan-1.27.0/setup.py --- old/shodan-1.26.1/setup.py 2022-01-19 16:47:04.000000000 +0100 +++ new/shodan-1.27.0/setup.py 2022-02-22 23:56:38.000000000 +0100 @@ -9,7 +9,7 @@ setup( name='shodan', - version='1.26.1', + version='1.27.0', description='Python library and command-line utility for Shodan (https://developer.shodan.io)', long_description=README, long_description_content_type='text/x-rst', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/shodan-1.26.1/shodan/__main__.py new/shodan-1.27.0/shodan/__main__.py --- old/shodan-1.26.1/shodan/__main__.py 2022-01-06 23:04:43.000000000 +0100 +++ new/shodan-1.27.0/shodan/__main__.py 2022-02-22 00:36:09.000000000 +0100 @@ -287,10 +287,10 @@ raise click.ClickException('The Shodan API is unresponsive at the moment, please try again later.') # Print some summary information about the download request - click.echo('Search query:\t\t\t%s' % query) - click.echo('Total number of results:\t%s' % total) - click.echo('Query credits left:\t\t%s' % info['unlocked_left']) - click.echo('Output file:\t\t\t%s' % filename) + click.echo('Search query:\t\t\t{}'.format(query)) + click.echo('Total number of results:\t{}'.format(total)) + click.echo('Query credits left:\t\t{}'.format(info['unlocked_left'])) + click.echo('Output file:\t\t\t{}'.format(filename)) if limit > total: limit = total diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/shodan-1.26.1/shodan/cli/alert.py new/shodan-1.27.0/shodan/cli/alert.py --- old/shodan-1.26.1/shodan/cli/alert.py 2021-01-25 23:30:07.000000000 +0100 +++ new/shodan-1.27.0/shodan/cli/alert.py 2022-02-22 18:42:03.000000000 +0100 @@ -1,5 +1,7 @@ import click import csv +import gzip +import json import shodan from collections import defaultdict @@ -221,6 +223,87 @@ click.secho('Successfully downloaded results into: {}'.format(filename), fg='green') +@alert.command(name='export') +@click.option('--filename', help='Name of the output file', default='shodan-alerts.json.gz', type=str) +def alert_export(filename): + """Export the configuration of monitored networks/ IPs to be used by ``shodan alert import``.""" + # Setup the API wrapper + key = get_api_key() + api = shodan.Shodan(key) + + try: + # Get the list of alerts for the user + click.echo('Looking up alert information...') + alerts = api.alerts() + + # Create the output file + click.echo('Writing alerts to file: {}'.format(filename)) + with gzip.open(filename, 'wt', encoding='utf-8') as fout: + json.dump(alerts, fout) + except Exception as e: + raise click.ClickException(e.value) + + click.secho('Successfully exported monitored networks', fg='green') + + +@alert.command(name='import') +@click.argument('filename', metavar='<export file>') +def alert_import(filename): + """Export the configuration of monitored networks/ IPs to be used by ``shodan alert import``.""" + # Setup the API wrapper + key = get_api_key() + api = shodan.Shodan(key) + + # A mapping of the old notifier IDs to the new ones + notifier_map = {} + + try: + # Loading the alerts + click.echo('Loading alerts from: {}'.format(filename)) + with gzip.open(filename, 'rt', encoding='utf-8') as fin: + alerts = json.load(fin) + + for item in alerts: + # Create the alert + click.echo('Creating: {}'.format(item['name'])) + alert = api.create_alert(item['name'], item['filters']['ip']) + + # Enable any triggers + if item.get('triggers', {}): + triggers = ','.join(item['triggers'].keys()) + + api.enable_alert_trigger(alert['id'], triggers) + + # Add any whitelisted services for this trigger + for trigger, info in item['triggers'].items(): + if info.get('ignore', []): + for whitelist in info['ignore']: + api.ignore_alert_trigger_notification(alert['id'], trigger, whitelist['ip'], whitelist['port']) + + # Enable the notifiers + for prev_notifier in item.get('notifiers', []): + # We don't need to do anything for the default notifier as that + # uses the account's email address automatically. + if prev_notifier['id'] == 'default': + continue + + # Get the new notifier based on the ID of the old one + notifier = notifier_map.get(prev_notifier['id']) + + # Create the notifier if it doesn't yet exist + if notifier is None: + notifier = api.notifier.create(prev_notifier['provider'], prev_notifier['args'], description=prev_notifier['description']) + + # Add it to our map of old notifier IDs to new notifiers + notifier_map[prev_notifier['id']] = notifier + + api.add_alert_notifier(alert['id'], notifier['id']) + except Exception as e: + raise click.ClickException(e.value) + + click.secho('Successfully imported monitored networks', fg='green') + + @alert.command(name='info') @click.argument('alert', metavar='<alert id>') def alert_info(alert): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/shodan-1.26.1/shodan/cli/helpers.py new/shodan-1.27.0/shodan/cli/helpers.py --- old/shodan-1.26.1/shodan/cli/helpers.py 2019-11-13 18:17:29.000000000 +0100 +++ new/shodan-1.27.0/shodan/cli/helpers.py 2022-02-22 00:36:09.000000000 +0100 @@ -47,7 +47,7 @@ def open_streaming_file(directory, timestr, compresslevel=9): - return gzip.open('%s/%s.json.gz' % (directory, timestr), 'a', compresslevel) + return gzip.open('{}/{}.json.gz'.format(directory, timestr), 'a', compresslevel) def get_banner_field(banner, flat_field): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/shodan-1.26.1/shodan/cli/worldmap.py new/shodan-1.27.0/shodan/cli/worldmap.py --- old/shodan-1.26.1/shodan/cli/worldmap.py 2020-06-23 23:30:50.000000000 +0200 +++ new/shodan-1.27.0/shodan/cli/worldmap.py 2022-02-22 00:36:09.000000000 +0100 @@ -166,7 +166,7 @@ attrs |= curses.color_pair(self.colors[color]) self.window.addstr(char_y, char_x, char, attrs) if desc: - det_show = "%s %s" % (char, desc) + det_show = "{} {}".format(char, desc) else: det_show = None @@ -179,7 +179,7 @@ # FIXME: check window size before addstr() break self.window.overwrite(target) - self.window.leaveok(1) + self.window.leaveok(True) class MapApp(object): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/shodan-1.26.1/shodan/client.py new/shodan-1.27.0/shodan/client.py --- old/shodan-1.26.1/shodan/client.py 2022-01-06 23:04:43.000000000 +0100 +++ new/shodan-1.27.0/shodan/client.py 2022-02-22 00:36:09.000000000 +0100 @@ -273,6 +273,8 @@ self.tools = self.Tools(self) self.stream = Stream(key, proxies=proxies) self._session = requests.Session() + self.api_rate_limit = 1 # Requests per second + self._api_query_time = None if proxies: self._session.proxies.update(proxies) self._session.trust_env = False @@ -297,6 +299,11 @@ 'exploits': self.base_exploits_url, }.get(service, 'shodan') + # Wait for API rate limit + if self._api_query_time is not None and self.api_rate_limit > 0: + while (1.0 / self.api_rate_limit) + self._api_query_time >= time.time(): + time.sleep(0.1 / self.api_rate_limit) + # Send the request try: method = method.lower() @@ -308,6 +315,7 @@ data = self._session.delete(base_url + function, params=params) else: data = self._session.get(base_url + function, params=params) + self._api_query_time = time.time() except Exception: raise APIError('Unable to connect to Shodan') @@ -377,7 +385,7 @@ params['history'] = history if minify: params['minify'] = minify - return self._request('/shodan/host/%s' % ','.join(ips), params) + return self._request('/shodan/host/{}'.format(','.join(ips)), params) def info(self): """Returns information about the current API key, such as a list of add-ons @@ -468,7 +476,7 @@ :returns: A dictionary with general information about the scan, including its status in getting processed. """ - return self._request('/shodan/scan/%s' % scan_id, {}) + return self._request('/shodan/scan/{}'.format(scan_id), {}) def search(self, query, page=1, limit=None, offset=None, facets=None, minify=True): """Search the SHODAN database. @@ -677,7 +685,7 @@ def alerts(self, aid=None, include_expired=True): """List all of the active alerts that the user created.""" if aid: - func = '/shodan/alert/%s/info' % aid + func = '/shodan/alert/{}/info'.format(aid) else: func = '/shodan/alert/info' @@ -689,7 +697,7 @@ def delete_alert(self, aid): """Delete the alert with the given ID.""" - func = '/shodan/alert/%s' % aid + func = '/shodan/alert/{}'.format(aid) response = api_request(self.api_key, func, params={}, method='delete', proxies=self._session.proxies) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/shodan-1.26.1/shodan/helpers.py new/shodan-1.27.0/shodan/helpers.py --- old/shodan-1.26.1/shodan/helpers.py 2019-09-29 20:13:20.000000000 +0200 +++ new/shodan-1.27.0/shodan/helpers.py 2022-02-22 00:36:09.000000000 +0100 @@ -142,7 +142,7 @@ fout.write(line.encode('utf-8')) -def humanize_bytes(bytes, precision=1): +def humanize_bytes(byte_count, precision=1): """Return a humanized string representation of a number of bytes. >>> humanize_bytes(1) '1 byte' @@ -161,15 +161,15 @@ >>> humanize_bytes(1024*1234*1111,1) '1.3 GB' """ - if bytes == 1: + if byte_count == 1: return '1 byte' - if bytes < 1024: - return '%.*f %s' % (precision, bytes, "bytes") + if byte_count < 1024: + '{0:0.{1}f} {2}'.format(byte_count, 0, 'bytes') suffixes = ['KB', 'MB', 'GB', 'TB', 'PB'] multiple = 1024.0 # .0 to force float on python 2 for suffix in suffixes: - bytes /= multiple - if bytes < multiple: - return '%.*f %s' % (precision, bytes, suffix) - return '%.*f %s' % (precision, bytes, suffix) + byte_count /= multiple + if byte_count < multiple: + return '{0:0.{1}f} {2}'.format(byte_count, precision, suffix) + return '{0:0.{1}f} {2}'.format(byte_count, precision, suffix) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/shodan-1.26.1/shodan/stream.py new/shodan-1.27.0/shodan/stream.py --- old/shodan-1.26.1/shodan/stream.py 2022-01-19 16:45:34.000000000 +0100 +++ new/shodan-1.27.0/shodan/stream.py 2022-02-22 00:36:09.000000000 +0100 @@ -74,7 +74,7 @@ def alert(self, aid=None, timeout=None, raw=False): if aid: - stream = self._create_stream('/shodan/alert/%s' % aid, timeout=timeout) + stream = self._create_stream('/shodan/alert/{}'.format(aid), timeout=timeout) else: stream = self._create_stream('/shodan/alert', timeout=timeout) @@ -93,7 +93,7 @@ :param asn: A list of ASN to return banner data on. :type asn: string[] """ - stream = self._create_stream('/shodan/asn/%s' % ','.join(asn), timeout=timeout) + stream = self._create_stream('/shodan/asn/{}'.format(','.join(asn)), timeout=timeout) for line in self._iter_stream(stream, raw): yield line @@ -112,7 +112,7 @@ :param countries: A list of countries to return banner data on. :type countries: string[] """ - stream = self._create_stream('/shodan/countries/%s' % ','.join(countries), timeout=timeout) + stream = self._create_stream('/shodan/countries/{}'.format(','.join(countries)), timeout=timeout) for line in self._iter_stream(stream, raw): yield line @@ -135,7 +135,7 @@ :param ports: A list of ports to return banner data on. :type ports: int[] """ - stream = self._create_stream('/shodan/ports/%s' % ','.join([str(port) for port in ports]), timeout=timeout) + stream = self._create_stream('/shodan/ports/{}'.format(','.join([str(port) for port in ports])), timeout=timeout) for line in self._iter_stream(stream, raw): yield line @@ -146,7 +146,7 @@ :param tags: A list of tags to return banner data on. :type tags: string[] """ - stream = self._create_stream('/shodan/tags/%s' % ','.join(tags), timeout=timeout) + stream = self._create_stream('/shodan/tags/{}'.format(','.join(tags)), timeout=timeout) for line in self._iter_stream(stream, raw): yield line @@ -157,6 +157,6 @@ :param vulns: A list of vulns to return banner data on. :type vulns: string[] """ - stream = self._create_stream('/shodan/vulns/%s' % ','.join(vulns), timeout=timeout) + stream = self._create_stream('/shodan/vulns/{}'.format(','.join(vulns)), timeout=timeout) for line in self._iter_stream(stream, raw): yield line diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/shodan-1.26.1/shodan.egg-info/PKG-INFO new/shodan-1.27.0/shodan.egg-info/PKG-INFO --- old/shodan-1.26.1/shodan.egg-info/PKG-INFO 2022-01-19 16:47:33.000000000 +0100 +++ new/shodan-1.27.0/shodan.egg-info/PKG-INFO 2022-02-23 00:00:57.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: shodan -Version: 1.26.1 +Version: 1.27.0 Summary: Python library and command-line utility for Shodan (https://developer.shodan.io) Home-page: https://github.com/achillean/shodan-python Author: John Matherly