Mailinglist Archive: opensuse-commit (1903 mails)

< Previous Next >
commit python-shodan for openSUSE:Factory
Hello community,

here is the log from the commit of package python-shodan for openSUSE:Factory
checked in at 2019-04-05 11:56:58
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-shodan (Old)
and /work/SRC/openSUSE:Factory/.python-shodan.new.3908 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-shodan"

Fri Apr 5 11:56:58 2019 rev:9 rq:687605 version:1.11.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-shodan/python-shodan.changes
2018-12-24 11:43:56.605316407 +0100
+++ /work/SRC/openSUSE:Factory/.python-shodan.new.3908/python-shodan.changes
2019-04-05 11:56:58.794350141 +0200
@@ -1,0 +2,15 @@
+Sat Mar 16 14:57:46 UTC 2019 - Sebastian Wagner <sebix+novell.com@xxxxxxxx>
+
+- update to version 1.11.1:
+ * Allow a single network alert to monitor multiple IP ranges (#93)
+- update to version 1.11.0:
+ * New command **shodan scan list** to list recently launched scans
+ * New command **shodan alert triggers** to list the available notification
triggers
+ * New command **shodan alert enable** to enable a notification trigger
+ * New command **shodan alert disable** to disable a notification trigger
+ * New command **shodan alert info** to show details of a specific alert
+ * Include timestamp, vulns and tags in CSV converter (#85)
+ * Fixed bug that caused an exception when parsing uncompressed data files in
Python3
+ * Code quality improvements
+
+-------------------------------------------------------------------

Old:
----
shodan-1.10.4.tar.gz

New:
----
shodan-1.11.1.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-shodan.spec ++++++
--- /var/tmp/diff_new_pack.W2sBZb/_old 2019-04-05 11:57:00.586351416 +0200
+++ /var/tmp/diff_new_pack.W2sBZb/_new 2019-04-05 11:57:00.590351419 +0200
@@ -1,7 +1,7 @@
#
# spec file for package python-shodan
#
-# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany.
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -19,7 +19,7 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
%{!?license: %global license %doc}
Name: python-shodan
-Version: 1.10.4
+Version: 1.11.1
Release: 0
Summary: Python library and command-line utility for Shodan
License: MIT

++++++ shodan-1.10.4.tar.gz -> shodan-1.11.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/shodan-1.10.4/.gitignore new/shodan-1.11.1/.gitignore
--- old/shodan-1.10.4/.gitignore 2017-07-05 23:17:17.000000000 +0200
+++ new/shodan-1.11.1/.gitignore 2019-01-13 06:20:00.000000000 +0100
@@ -8,4 +8,6 @@
tmp/*
MANIFEST
.vscode/
-PKG-INFO
\ No newline at end of file
+PKG-INFO
+venv/*
+.idea/*
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/shodan-1.10.4/CHANGELOG.md
new/shodan-1.11.1/CHANGELOG.md
--- old/shodan-1.10.4/CHANGELOG.md 2018-09-22 04:14:40.000000000 +0200
+++ new/shodan-1.11.1/CHANGELOG.md 2019-02-24 10:29:28.000000000 +0100
@@ -1,6 +1,27 @@
CHANGELOG
=========

+1.11.1
+------
+* Allow a single network alert to monitor multiple IP ranges (#93)
+
+1.11.0
+------
+* New command **shodan scan list** to list recently launched scans
+* New command **shodan alert triggers** to list the available notification
triggers
+* New command **shodan alert enable** to enable a notification trigger
+* New command **shodan alert disable** to disable a notification trigger
+* New command **shodan alert info** to show details of a specific alert
+* Include timestamp, vulns and tags in CSV converter (#85)
+* Fixed bug that caused an exception when parsing uncompressed data files in
Python3
+* Code quality improvements
+* Thank you for contributions from @wagner-certat, @cclauss, @opt9, @voldmar
and Antoine Neuenschwander
+
+1.10.4
+------
+* Fix a bug when showing old banner records that don't have the "transport"
property
+* Code quality improvements (bare excepts)
+
1.10.3
------
* Change bare 'except:' statements to 'except Exception:' or more specific ones
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/shodan-1.10.4/PKG-INFO new/shodan-1.11.1/PKG-INFO
--- old/shodan-1.10.4/PKG-INFO 2018-10-05 03:00:20.000000000 +0200
+++ new/shodan-1.11.1/PKG-INFO 2019-02-24 10:58:50.000000000 +0100
@@ -1,6 +1,6 @@
-Metadata-Version: 1.1
+Metadata-Version: 2.1
Name: shodan
-Version: 1.10.4
+Version: 1.11.1
Summary: Python library and command-line utility for Shodan
(https://developer.shodan.io)
Home-page: http://github.com/achillean/shodan-python/tree/master
Author: John Matherly
@@ -26,6 +26,7 @@
- `Fast/ bulk IP lookups
<https://help.shodan.io/developer-fundamentals/looking-up-ip-info>`_
- Streaming API support for real-time consumption of Shodan firehose
- `Network alerts (aka private firehose)
<https://help.shodan.io/guides/how-to-monitor-network>`_
+ - `Manage Email Notifications
<https://asciinema.org/a/7WvyDtNxn0YeNU70ozsxvXDmL>`_
- Exploit search API fully implemented
- Bulk data downloads
- `Command-line interface <https://cli.shodan.io>`_
@@ -96,3 +97,4 @@
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Description-Content-Type: text/x-rst
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/shodan-1.10.4/README.rst new/shodan-1.11.1/README.rst
--- old/shodan-1.10.4/README.rst 2018-08-17 23:19:34.000000000 +0200
+++ new/shodan-1.11.1/README.rst 2019-02-24 10:33:19.000000000 +0100
@@ -18,6 +18,7 @@
- `Fast/ bulk IP lookups
<https://help.shodan.io/developer-fundamentals/looking-up-ip-info>`_
- Streaming API support for real-time consumption of Shodan firehose
- `Network alerts (aka private firehose)
<https://help.shodan.io/guides/how-to-monitor-network>`_
+- `Manage Email Notifications
<https://asciinema.org/a/7WvyDtNxn0YeNU70ozsxvXDmL>`_
- Exploit search API fully implemented
- Bulk data downloads
- `Command-line interface <https://cli.shodan.io>`_
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/shodan-1.10.4/setup.py new/shodan-1.11.1/setup.py
--- old/shodan-1.10.4/setup.py 2018-10-05 02:49:51.000000000 +0200
+++ new/shodan-1.11.1/setup.py 2019-02-24 10:57:38.000000000 +0100
@@ -6,19 +6,19 @@
README = open('README.rst', 'r').read()

setup(
- name = 'shodan',
- version = '1.10.4',
- description = 'Python library and command-line utility for Shodan
(https://developer.shodan.io)',
- long_description = README,
- long_description_content_type = 'text/x-rst',
- author = 'John Matherly',
- author_email = 'jmath@xxxxxxxxx',
- url = 'http://github.com/achillean/shodan-python/tree/master',
- packages = ['shodan', 'shodan.cli', 'shodan.cli.converter'],
- entry_points = {'console_scripts': ['shodan = shodan.__main__:main']},
- install_requires = DEPENDENCIES,
- keywords = ['security', 'network'],
- classifiers = [
+ name='shodan',
+ version='1.11.1',
+ description='Python library and command-line utility for Shodan
(https://developer.shodan.io)',
+ long_description=README,
+ long_description_content_type='text/x-rst',
+ author='John Matherly',
+ author_email='jmath@xxxxxxxxx',
+ url='http://github.com/achillean/shodan-python/tree/master',
+ packages=['shodan', 'shodan.cli', 'shodan.cli.converter'],
+ entry_points={'console_scripts': ['shodan=shodan.__main__:main']},
+ install_requires=DEPENDENCIES,
+ keywords=['security', 'network'],
+ classifiers=[
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/shodan-1.10.4/shodan/__main__.py
new/shodan-1.11.1/shodan/__main__.py
--- old/shodan-1.10.4/shodan/__main__.py 2018-10-05 02:49:11.000000000
+0200
+++ new/shodan-1.11.1/shodan/__main__.py 2019-02-11 01:22:17.000000000
+0100
@@ -49,6 +49,13 @@
from click_plugins import with_plugins
from pkg_resources import iter_entry_points

+# Large subcommands are stored in separate modules
+from shodan.cli.alert import alert
+from shodan.cli.data import data
+from shodan.cli.organization import org
+from shodan.cli.scan import scan
+
+
# Make "-h" work like "--help"
CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'])

@@ -58,7 +65,6 @@
except NameError:
basestring = str

-
# Define the main entry point for all of our commands
# and expose a way for 3rd-party plugins to tie into the Shodan CLI.
@with_plugins(iter_entry_points('shodan.cli.plugins'))
@@ -67,11 +73,7 @@
pass


-# Large subcommands are stored in separate modules
-from shodan.cli.alert import alert
-from shodan.cli.data import data
-from shodan.cli.organization import org
-from shodan.cli.scan import scan
+# Setup the large subcommands
main.add_command(alert)
main.add_command(data)
main.add_command(org)
@@ -151,6 +153,7 @@

os.chmod(keyfile, 0o600)

+
@main.command()
@click.argument('query', metavar='<search query>', nargs=-1)
def count(query):
@@ -203,7 +206,7 @@
try:
total = api.count(query)['total']
info = api.info()
- except:
+ except Exception:
raise click.ClickException('The Shodan API is unresponsive at the
moment, please try again later.')

# Print some summary information about the download request
@@ -275,7 +278,6 @@
raise click.ClickException(e.value)


-
@main.command()
def info():
"""Shows general information about your account"""
@@ -308,7 +310,6 @@

has_filters = len(filters) > 0

-
# Setup the output file handle
fout = None
if filename:
@@ -333,7 +334,7 @@
helpers.write_banner(fout, banner)

# Loop over all the fields and print the banner as a row
- for field in fields:
+ for i, field in enumerate(fields):
tmp = u''
value = get_banner_field(banner, field)
if value:
@@ -351,9 +352,10 @@
if color:
tmp = click.style(tmp, fg=COLORIZE_FIELDS.get(field,
'white'))

- # Add the field information to the row
- row += tmp
- row += separator
+ # Add the field information to the row
+ if i > 0:
+ row += separator
+ row += tmp

click.echo(row)

@@ -519,7 +521,7 @@
if len(values) > counter:
has_items = True
row[pos] = values[counter]['value']
- row[pos+1] = values[counter]['count']
+ row[pos + 1] = values[counter]['count']

pos += 2

@@ -545,7 +547,7 @@
@click.option('--asn', help='A comma-separated list of ASNs to grab data on.',
default=None, type=str)
@click.option('--alert', help='The network alert ID or "all" to subscribe to
all network alerts on your account.', default=None, type=str)
@click.option('--compresslevel', help='The gzip compression level (0-9; 0 = no
compression, 9 = most compression', default=9, type=int)
-def stream(color, fields, separator, limit, datadir, ports, quiet, timeout,
streamer, countries, asn, alert, compresslevel):
+def stream(color, fields, separator, limit, datadir, ports, quiet, timeout,
streamer, countries, asn, alert, compresslevel):
"""Stream data in real-time."""
# Setup the Shodan API
key = get_api_key()
@@ -641,9 +643,9 @@
if datadir:
cur_time = timestr()
if cur_time != last_time:
- last_time = cur_time
- fout.close()
- fout = open_streaming_file(datadir, last_time)
+ last_time = cur_time
+ fout.close()
+ fout = open_streaming_file(datadir, last_time)
helpers.write_banner(fout, banner)

# Print the banner information to stdout
@@ -706,7 +708,7 @@
click.echo(click.style('Not a honeypot', fg='green'))

click.echo('Score: {}'.format(score))
- except:
+ except Exception:
raise click.ClickException('Unable to calculate honeyscore')


@@ -725,5 +727,6 @@
except Exception as e:
raise click.ClickException(u'{}'.format(e))

+
if __name__ == '__main__':
main()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/shodan-1.10.4/shodan/alert.py
new/shodan-1.11.1/shodan/alert.py
--- old/shodan-1.10.4/shodan/alert.py 2015-11-20 01:41:53.000000000 +0100
+++ new/shodan-1.11.1/shodan/alert.py 1970-01-01 01:00:00.000000000 +0100
@@ -1,9 +0,0 @@
-class Alert:
- def __init__(self):
- self.id = None
- self.name = None
- self.api_key = None
- self.filters = None
- self.credits = None
- self.created = None
- self.expires = None
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/shodan-1.10.4/shodan/cli/alert.py
new/shodan-1.11.1/shodan/cli/alert.py
--- old/shodan-1.10.4/shodan/cli/alert.py 2018-09-02 00:56:08.000000000
+0200
+++ new/shodan-1.11.1/shodan/cli/alert.py 2019-02-24 10:31:35.000000000
+0100
@@ -1,8 +1,10 @@
import click
import shodan

+from operator import itemgetter
from shodan.cli.helpers import get_api_key

+
@click.group()
def alert():
"""Manage the network alerts for your account"""
@@ -25,23 +27,61 @@
raise click.ClickException(e.value)
click.echo("Alerts deleted")

+
@alert.command(name='create')
@click.argument('name', metavar='<name>')
-@click.argument('netblock', metavar='<netblock>')
-def alert_create(name, netblock):
+@click.argument('netblocks', metavar='<netblocks>', nargs=-1)
+def alert_create(name, netblocks):
"""Create a network alert to monitor an external network"""
key = get_api_key()

# Get the list
api = shodan.Shodan(key)
try:
- alert = api.create_alert(name, netblock)
+ alert = api.create_alert(name, netblocks)
except shodan.APIError as e:
raise click.ClickException(e.value)

click.secho('Successfully created network alert!', fg='green')
click.secho('Alert ID: {}'.format(alert['id']), fg='cyan')

+
+@alert.command(name='info')
+@click.argument('alert', metavar='<alert id>')
+def alert_info(alert):
+ """Show information about a specific alert"""
+ key = get_api_key()
+ api = shodan.Shodan(key)
+
+ try:
+ info = api.alerts(aid=alert)
+ except shodan.APIError as e:
+ raise click.ClickException(e.value)
+
+ click.secho(info['name'], fg='cyan')
+ click.secho('Created: ', nl=False, dim=True)
+ click.secho(info['created'], fg='magenta')
+
+ click.secho('Notifications: ', nl=False, dim=True)
+ if 'triggers' in info and info['triggers']:
+ click.secho('enabled', fg='green')
+ else:
+ click.echo('disabled')
+
+ click.echo('')
+ click.secho('Network Range(s):', dim=True)
+
+ for network in info['filters']['ip']:
+ click.echo(u' > {}'.format(click.style(network, fg='yellow')))
+
+ click.echo('')
+ if 'triggers' in info and info['triggers']:
+ click.secho('Triggers:', dim=True)
+ for trigger in info['triggers']:
+ click.echo(u' > {}'.format(click.style(trigger, fg='yellow')))
+ click.echo('')
+
+
@alert.command(name='list')
@click.option('--expired', help='Whether or not to show expired alerts.',
default=True, type=bool)
def alert_list(expired):
@@ -57,17 +97,21 @@

if len(results) > 0:
click.echo(u'# {:14} {:<21} {:<15s}'.format('Alert ID', 'Name', 'IP/
Network'))
- # click.echo('#' * 65)
+
for alert in results:
click.echo(
u'{:16} {:<30} {:<35} '.format(
- click.style(alert['id'], fg='yellow'),
+ click.style(alert['id'], fg='yellow'),
click.style(alert['name'], fg='cyan'),
click.style(', '.join(alert['filters']['ip']), fg='white')
),
nl=False
)

+ if 'triggers' in alert and alert['triggers']:
+ click.secho('Triggers: ', fg='magenta', nl=False)
+ click.echo(', '.join(alert['triggers'].keys()), nl=False)
+
if 'expired' in alert and alert['expired']:
click.secho('expired', fg='red')
else:
@@ -89,3 +133,68 @@
except shodan.APIError as e:
raise click.ClickException(e.value)
click.echo("Alert deleted")
+
+
+@alert.command(name='triggers')
+def alert_list_triggers():
+ """List the available notification triggers"""
+ key = get_api_key()
+
+ # Get the list
+ api = shodan.Shodan(key)
+ try:
+ results = api.alert_triggers()
+ except shodan.APIError as e:
+ raise click.ClickException(e.value)
+
+ if len(results) > 0:
+ click.secho('The following triggers can be enabled on alerts:',
dim=True)
+ click.echo('')
+
+ for trigger in sorted(results, key=itemgetter('name')):
+ click.secho('{:<12} '.format('Name'), dim=True, nl=False)
+ click.secho(trigger['name'], fg='yellow')
+
+ click.secho('{:<12} '.format('Description'), dim=True, nl=False)
+ click.secho(trigger['description'], fg='cyan')
+
+ click.secho('{:<12} '.format('Rule'), dim=True, nl=False)
+ click.echo(trigger['rule'])
+
+ click.echo('')
+ else:
+ click.echo("No triggers currently available.")
+
+
+@alert.command(name='enable')
+@click.argument('alert_id', metavar='<alert ID>')
+@click.argument('trigger', metavar='<trigger name>')
+def alert_enable_trigger(alert_id, trigger):
+ """Enable a trigger for the alert"""
+ key = get_api_key()
+
+ # Get the list
+ api = shodan.Shodan(key)
+ try:
+ api.enable_alert_trigger(alert_id, trigger)
+ except shodan.APIError as e:
+ raise click.ClickException(e.value)
+
+ click.secho('Successfully enabled the trigger: {}'.format(trigger),
fg='green')
+
+
+@alert.command(name='disable')
+@click.argument('alert_id', metavar='<alert ID>')
+@click.argument('trigger', metavar='<trigger name>')
+def alert_disable_trigger(alert_id, trigger):
+ """Disable a trigger for the alert"""
+ key = get_api_key()
+
+ # Get the list
+ api = shodan.Shodan(key)
+ try:
+ api.disable_alert_trigger(alert_id, trigger)
+ except shodan.APIError as e:
+ raise click.ClickException(e.value)
+
+ click.secho('Successfully disabled the trigger: {}'.format(trigger),
fg='green')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/shodan-1.10.4/shodan/cli/converter/__init__.py
new/shodan-1.11.1/shodan/cli/converter/__init__.py
--- old/shodan-1.10.4/shodan/cli/converter/__init__.py 2017-06-21
00:17:07.000000000 +0200
+++ new/shodan-1.11.1/shodan/cli/converter/__init__.py 2019-02-11
01:01:14.000000000 +0100
@@ -2,4 +2,4 @@
from .excel import ExcelConverter
from .geojson import GeoJsonConverter
from .images import ImagesConverter
-from .kml import KmlConverter
\ No newline at end of file
+from .kml import KmlConverter
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/shodan-1.10.4/shodan/cli/converter/base.py
new/shodan-1.11.1/shodan/cli/converter/base.py
--- old/shodan-1.10.4/shodan/cli/converter/base.py 2016-02-18
06:00:23.000000000 +0100
+++ new/shodan-1.11.1/shodan/cli/converter/base.py 2019-02-11
03:14:17.000000000 +0100
@@ -3,6 +3,6 @@

def __init__(self, fout):
self.fout = fout
-
+
def process(self, fout):
pass
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/shodan-1.10.4/shodan/cli/converter/csvc.py
new/shodan-1.11.1/shodan/cli/converter/csvc.py
--- old/shodan-1.10.4/shodan/cli/converter/csvc.py 2018-09-22
04:14:03.000000000 +0200
+++ new/shodan-1.11.1/shodan/cli/converter/csvc.py 2019-02-11
03:12:57.000000000 +0100
@@ -24,10 +24,13 @@
'os',
'asn',
'port',
+ 'tags',
+ 'timestamp',
'transport',
'product',
'version',
-
+ 'vulns',
+
'ssl.cipher.version',
'ssl.cipher.bits',
'ssl.cipher.name',
@@ -36,18 +39,23 @@
'ssl.cert.serial',
'ssl.cert.fingerprint.sha1',
'ssl.cert.fingerprint.sha256',
-
+
'html',
'title',
]
-
+
def process(self, files):
writer = csv_writer(self.fout, dialect=excel)
-
+
# Write the header
writer.writerow(self.fields)
-
+
for banner in iterate_files(files):
+ # The "vulns" property can't be nicely flattened as-is so we turn
+ # it into a list before processing the banner.
+ if 'vulns' in banner:
+ banner['vulns'] = banner['vulns'].keys()
+
try:
row = []
for field in self.fields:
@@ -56,33 +64,32 @@
writer.writerow(row)
except Exception:
pass
-
+
def banner_field(self, banner, flat_field):
# The provided field is a collapsed form of the actual field
fields = flat_field.split('.')
-
+
try:
current_obj = banner
for field in fields:
current_obj = current_obj[field]
-
+
# Convert a list into a concatenated string
if isinstance(current_obj, list):
current_obj = ','.join([str(i) for i in current_obj])
-
+
return current_obj
except Exception:
pass
-
+
return ''
-
+
def flatten(self, d, parent_key='', sep='.'):
items = []
for k, v in d.items():
new_key = parent_key + sep + k if parent_key else k
if isinstance(v, MutableMapping):
- # pylint: disable=E0602
- items.extend(flatten(v, new_key, sep=sep).items())
+ items.extend(self.flatten(v, new_key, sep=sep).items())
else:
items.append((new_key, v))
return dict(items)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/shodan-1.10.4/shodan/cli/converter/excel.py
new/shodan-1.11.1/shodan/cli/converter/excel.py
--- old/shodan-1.10.4/shodan/cli/converter/excel.py 2018-09-22
04:14:03.000000000 +0200
+++ new/shodan-1.11.1/shodan/cli/converter/excel.py 2019-02-11
03:14:03.000000000 +0100
@@ -23,7 +23,7 @@
'transport',
'product',
'version',
-
+
'http.server',
'http.title',
]
@@ -40,7 +40,7 @@
'http.server': 'Web Server',
'http.title': 'Website Title',
}
-
+
def process(self, files):
# Get the filename from the already-open file handle
filename = self.fout.name
@@ -55,14 +55,14 @@
bold = workbook.add_format({
'bold': 1,
})
-
+
# Create the main worksheet where all the raw data is shown
main_sheet = workbook.add_worksheet('Raw Data')

# Write the header
- main_sheet.write(0, 0, 'IP', bold) # The IP field can be either ip_str
or ipv6 so we treat it differently
+ main_sheet.write(0, 0, 'IP', bold) # The IP field can be either
ip_str or ipv6 so we treat it differently
main_sheet.set_column(0, 0, 20)
-
+
row = 0
col = 1
for field in self.fields:
@@ -80,7 +80,7 @@
for field in self.fields:
value = self.banner_field(banner, field)
data.append(value)
-
+
# Write those values to the main workbook
# Starting off w/ the special "IP" property
main_sheet.write_string(row, 0, get_ip(banner))
@@ -92,11 +92,11 @@
row += 1
except Exception:
pass
-
+
# Aggregate summary information
total += 1
ports[banner['port']] += 1
-
+
summary_sheet = workbook.add_worksheet('Summary')
summary_sheet.write(0, 0, 'Total', bold)
summary_sheet.write(0, 1, total)
@@ -109,22 +109,22 @@
summary_sheet.write(row, col, key)
summary_sheet.write(row, col + 1, value)
row += 1
-
+
def banner_field(self, banner, flat_field):
# The provided field is a collapsed form of the actual field
fields = flat_field.split('.')
-
+
try:
current_obj = banner
for field in fields:
current_obj = current_obj[field]
-
+
# Convert a list into a concatenated string
if isinstance(current_obj, list):
current_obj = ','.join([str(i) for i in current_obj])
-
+
return current_obj
except Exception:
pass
-
+
return ''
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/shodan-1.10.4/shodan/cli/converter/geojson.py
new/shodan-1.11.1/shodan/cli/converter/geojson.py
--- old/shodan-1.10.4/shodan/cli/converter/geojson.py 2018-09-22
04:14:03.000000000 +0200
+++ new/shodan-1.11.1/shodan/cli/converter/geojson.py 2019-02-11
03:19:42.000000000 +0100
@@ -2,39 +2,39 @@
from .base import Converter
from ...helpers import get_ip, iterate_files

+
class GeoJsonConverter(Converter):
-
+
def header(self):
self.fout.write("""{
"type": "FeatureCollection",
"features": [
""")
-
+
def footer(self):
self.fout.write("""{ }]}""")
-
+
def process(self, files):
# Write the header
self.header()
-
+
hosts = {}
for banner in iterate_files(files):
ip = get_ip(banner)
if not ip:
continue
-
+
if ip not in hosts:
hosts[ip] = banner
hosts[ip]['ports'] = []
-
+
hosts[ip]['ports'].append(banner['port'])
-
+
for ip, host in iter(hosts.items()):
self.write(host)
-
+
self.footer()
-
-
+
def write(self, host):
try:
ip = get_ip(host)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/shodan-1.10.4/shodan/cli/converter/images.py
new/shodan-1.11.1/shodan/cli/converter/images.py
--- old/shodan-1.10.4/shodan/cli/converter/images.py 2017-06-21
01:01:02.000000000 +0200
+++ new/shodan-1.11.1/shodan/cli/converter/images.py 2019-02-11
03:14:34.000000000 +0100
@@ -14,7 +14,7 @@
# special code in the Shodan CLI that relies on the "dirname" property to
let
# the user know where the images have been stored.
dirname = None
-
+
def process(self, files):
# Get the filename from the already-open file handle and use it as
# the directory name to store the images.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/shodan-1.10.4/shodan/cli/converter/kml.py
new/shodan-1.11.1/shodan/cli/converter/kml.py
--- old/shodan-1.10.4/shodan/cli/converter/kml.py 2018-09-22
04:14:03.000000000 +0200
+++ new/shodan-1.11.1/shodan/cli/converter/kml.py 2019-02-11
03:19:06.000000000 +0100
@@ -2,38 +2,38 @@
from .base import Converter
from ...helpers import iterate_files

+
class KmlConverter(Converter):
-
+
def header(self):
self.fout.write("""<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2";>
<Document>""")
-
+
def footer(self):
self.fout.write("""</Document></kml>""")
-
+
def process(self, files):
# Write the header
self.header()
-
+
hosts = {}
for banner in iterate_files(files):
ip = banner.get('ip_str', banner.get('ipv6', None))
if not ip:
continue
-
+
if ip not in hosts:
hosts[ip] = banner
hosts[ip]['ports'] = []
-
+
hosts[ip]['ports'].append(banner['port'])
-
+
for ip, host in iter(hosts.items()):
self.write(host)
-
+
self.footer()
-
-
+
def write(self, host):
try:
ip = host.get('ip_str', host.get('ipv6', None))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/shodan-1.10.4/shodan/cli/helpers.py
new/shodan-1.11.1/shodan/cli/helpers.py
--- old/shodan-1.10.4/shodan/cli/helpers.py 2018-09-22 04:14:03.000000000
+0200
+++ new/shodan-1.11.1/shodan/cli/helpers.py 2019-02-11 01:45:35.000000000
+0100
@@ -10,6 +10,12 @@

from .settings import SHODAN_CONFIG_DIR

+try:
+ basestring # Python 2
+except NameError:
+ basestring = (str, ) # Python 3
+
+
def get_api_key():
'''Returns the API key of the current logged-in user.'''
shodan_dir = os.path.expanduser(SHODAN_CONFIG_DIR)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/shodan-1.10.4/shodan/cli/host.py
new/shodan-1.11.1/shodan/cli/host.py
--- old/shodan-1.10.4/shodan/cli/host.py 2018-10-05 01:20:34.000000000
+0200
+++ new/shodan-1.11.1/shodan/cli/host.py 2019-02-11 01:46:29.000000000
+0100
@@ -64,9 +64,9 @@
for port in ports:
banner = {
'port': port,
- 'transport': 'tcp', # All the filtered services use TCP
- 'timestamp': host['data'][-1]['timestamp'], # Use the
timestamp of the oldest banner
- 'placeholder': True, # Don't store this banner when the file
is saved
+ 'transport': 'tcp', # All the filtered services use TCP
+ 'timestamp': host['data'][-1]['timestamp'], # Use the
timestamp of the oldest banner
+ 'placeholder': True, # Don't store this banner when the file
is saved
}
host['data'].append(banner)

@@ -94,7 +94,7 @@
# Show optional ssl info
if 'ssl' in banner:
if 'versions' in banner['ssl'] and banner['ssl']['versions']:
- click.echo('\t|-- SSL Versions: {}'.format(', '.join([version
for version in sorted(banner['ssl']['versions']) if not
version.startswith('-')])))
+ click.echo('\t|-- SSL Versions: {}'.format(', '.join([item for
item in sorted(banner['ssl']['versions']) if not version.startswith('-')])))
if 'dhparams' in banner['ssl'] and banner['ssl']['dhparams']:
click.echo('\t|-- Diffie-Hellman Parameters:')
click.echo('\t\t{:15s}{}\n\t\t{:15s}{}'.format('Bits:',
banner['ssl']['dhparams']['bits'], 'Generator:',
banner['ssl']['dhparams']['generator']))
@@ -119,4 +119,4 @@
HOST_PRINT = {
'pretty': host_print_pretty,
'tsv': host_print_tsv,
-}
\ No newline at end of file
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/shodan-1.10.4/shodan/cli/organization.py
new/shodan-1.11.1/shodan/cli/organization.py
--- old/shodan-1.10.4/shodan/cli/organization.py 2018-09-22
04:14:03.000000000 +0200
+++ new/shodan-1.11.1/shodan/cli/organization.py 2019-02-11
01:45:19.000000000 +0100
@@ -22,7 +22,7 @@
api.org.add_member(user, notify=not silent)
except shodan.APIError as e:
raise click.ClickException(e.value)
-
+
click.secho('Successfully added the new member', fg='green')


@@ -39,11 +39,11 @@
click.secho(organization['name'], fg='cyan')
click.secho('Access Level: ', nl=False, dim=True)
click.secho(humanize_api_plan(organization['upgrade_type']), fg='magenta')
-
+
if organization['domains']:
click.secho('Authorized Domains: ', nl=False, dim=True)
click.echo(', '.join(organization['domains']))
-
+
click.echo('')
click.secho('Administrators:', dim=True)

@@ -51,8 +51,8 @@
click.echo(u' > {:30}\t{:30}'.format(
click.style(admin['username'], fg='yellow'),
admin['email'])
- )
-
+ )
+
click.echo('')
if organization['members']:
click.secho('Members:', dim=True)
@@ -76,5 +76,5 @@
api.org.remove_member(user)
except shodan.APIError as e:
raise click.ClickException(e.value)
-
+
click.secho('Successfully removed the member', fg='green')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/shodan-1.10.4/shodan/cli/scan.py
new/shodan-1.11.1/shodan/cli/scan.py
--- old/shodan-1.10.4/shodan/cli/scan.py 2018-08-31 02:54:21.000000000
+0200
+++ new/shodan-1.11.1/shodan/cli/scan.py 2019-02-11 03:11:51.000000000
+0100
@@ -17,6 +17,35 @@
pass


+@scan.command(name='list')
+def scan_list():
+ """Show recently launched scans"""
+ key = get_api_key()
+
+ # Get the list
+ api = shodan.Shodan(key)
+ try:
+ scans = api.scans()
+ except shodan.APIError as e:
+ raise click.ClickException(e.value)
+
+ if len(scans) > 0:
+ click.echo(u'# {} Scans Total - Showing 10 most recent
scans:'.format(scans['total']))
+ click.echo(u'# {:20} {:<15} {:<10} {:<15s}'.format('Scan ID',
'Status', 'Size', 'Timestamp'))
+ # click.echo('#' * 65)
+ for scan in scans['matches'][:10]:
+ click.echo(
+ u'{:31} {:<24} {:<10} {:<15s}'.format(
+ click.style(scan['id'], fg='yellow'),
+ click.style(scan['status'], fg='cyan'),
+ scan['size'],
+ scan['created']
+ )
+ )
+ else:
+ click.echo("You haven't yet launched any scans.")
+
+
@scan.command(name='internet')
@click.option('--quiet', help='Disable the printing of information to the
screen.', default=False, is_flag=True)
@click.argument('port', type=int)
@@ -58,10 +87,9 @@

if not quiet:
click.echo('{0:<40} {1:<20} {2}'.format(
- click.style(helpers.get_ip(banner),
fg=COLORIZE_FIELDS['ip_str']),
- click.style(str(banner['port']),
fg=COLORIZE_FIELDS['port']),
- ';'.join(banner['hostnames'])
- )
+ click.style(helpers.get_ip(banner),
fg=COLORIZE_FIELDS['ip_str']),
+ click.style(str(banner['port']),
fg=COLORIZE_FIELDS['port']),
+ ';'.join(banner['hostnames']))
)
except shodan.APIError as e:
# We stop waiting for results if the scan has been
processed by the crawlers and
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/shodan-1.10.4/shodan/cli/worldmap.py
new/shodan-1.11.1/shodan/cli/worldmap.py
--- old/shodan-1.10.4/shodan/cli/worldmap.py 2018-09-22 04:14:03.000000000
+0200
+++ new/shodan-1.11.1/shodan/cli/worldmap.py 2019-02-11 01:44:09.000000000
+0100
@@ -108,14 +108,14 @@
TODO: filter out stuff that doesn't fit
TODO: make it possible to use "zoomed" maps
"""
- width = (self.corners[3]-self.corners[1])
- height = (self.corners[2]-self.corners[0])
+ width = (self.corners[3] - self.corners[1])
+ height = (self.corners[2] - self.corners[0])

# change to 0-180, 0-360
- abs_lat = -lat+90
- abs_lon = lon+180
- x = (abs_lon/360.0)*width + self.corners[1]
- y = (abs_lat/180.0)*height + self.corners[0]
+ abs_lat = -lat + 90
+ abs_lon = lon + 180
+ x = (abs_lon / 360.0) * width + self.corners[1]
+ y = (abs_lat / 180.0) * height + self.corners[0]
return int(x), int(y)

def set_data(self, data):
@@ -155,7 +155,7 @@
self.window.addstr(0, 0, self.map)

# FIXME: position to be defined in map config?
- row = self.corners[2]-6
+ row = self.corners[2] - 6
items_to_show = 5
for lat, lon, char, desc, attrs, color in self.data:
# to make this work almost everywhere. see
http://docs.python.org/2/library/curses.html
@@ -177,7 +177,7 @@
self.window.addstr(row, 1, det_show, attrs)
row += 1
items_to_show -= 1
- except StandardError:
+ except Exception:
# FIXME: check window size before addstr()
break
self.window.overwrite(target)
@@ -257,6 +257,7 @@
api = Shodan(get_api_key())
return launch_map(api)

+
if __name__ == '__main__':
import sys
sys.exit(main())
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/shodan-1.10.4/shodan/client.py
new/shodan-1.11.1/shodan/client.py
--- old/shodan-1.10.4/shodan/client.py 2018-09-22 04:14:03.000000000 +0200
+++ new/shodan-1.11.1/shodan/client.py 2019-02-11 01:04:51.000000000 +0100
@@ -176,7 +176,7 @@
:param key: The Shodan API key.
:type key: str
:param proxies: A proxies array for the requests library, e.g.
{'https': 'your proxy'}
- :type key: dict
+ :type proxies: dict
"""
self.api_key = key
self.base_url = 'https://api.shodan.io'
@@ -347,6 +347,16 @@

return self._request('/shodan/scan', params, method='post')

+ def scans(self, page=1):
+ """Get a list of scans submitted
+
+ :param page: Page through the list of scans 100 results at a time
+ :type page: int
+ """
+ return self._request('/shodan/scans', {
+ 'page': page,
+ })
+
def scan_internet(self, port, protocol):
"""Scan a network using Shodan

@@ -438,7 +448,7 @@
try:
yield banner
except GeneratorExit:
- return # exit out of the function
+ return # exit out of the function
page += 1
tries = 0
except Exception:
@@ -447,7 +457,7 @@
break

tries += 1
- time.sleep(1.0) # wait 1 second if the search errored out for
some reason
+ time.sleep(1.0) # wait 1 second if the search errored out for
some reason

def search_tokens(self, query):
"""Returns information about the search query itself (filters used
etc.)
@@ -507,8 +517,8 @@
def queries_tags(self, size=10):
"""Search the directory of saved search queries in Shodan.

- :param query: The number of tags to return
- :type page: int
+ :param size: The number of tags to return
+ :type size: int

:returns: A list of tags.
"""
@@ -518,12 +528,14 @@
return self._request('/shodan/query/tags', args)

def create_alert(self, name, ip, expires=0):
- """Search the directory of saved search queries in Shodan.
+ """Create a network alert/ private firehose for the specified IP
range(s)

- :param query: The number of tags to return
- :type page: int
+ :param name: Name of the alert
+ :type name: str
+ :param ip: Network range(s) to monitor
+ :type ip: str OR list of str

- :returns: A list of tags.
+ :returns: A dict describing the alert
"""
data = {
'name': name,
@@ -547,8 +559,7 @@

response = api_request(self.api_key, func, params={
'include_expired': include_expired,
- },
- proxies=self._session.proxies)
+ }, proxies=self._session.proxies)

return response

@@ -561,3 +572,17 @@

return response

+ def alert_triggers(self):
+ """Return a list of available triggers that can be enabled for alerts.
+
+ :returns: A list of triggers
+ """
+ return self._request('/shodan/alert/triggers', {})
+
+ def enable_alert_trigger(self, aid, trigger):
+ """Enable the given trigger on the alert."""
+ return self._request('/shodan/alert/{}/trigger/{}'.format(aid,
trigger), {}, method='put')
+
+ def disable_alert_trigger(self, aid, trigger):
+ """Disable the given trigger on the alert."""
+ return self._request('/shodan/alert/{}/trigger/{}'.format(aid,
trigger), {}, method='delete')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/shodan-1.10.4/shodan/exception.py
new/shodan-1.11.1/shodan/exception.py
--- old/shodan-1.10.4/shodan/exception.py 2018-08-18 03:27:55.000000000
+0200
+++ new/shodan-1.11.1/shodan/exception.py 2019-02-11 01:35:57.000000000
+0100
@@ -2,11 +2,10 @@
"""This exception gets raised whenever a non-200 status code was returned
by the Shodan API."""
def __init__(self, value):
self.value = value
-
+
def __str__(self):
return self.value


class APITimeout(APIError):
- pass
-
+ pass
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/shodan-1.10.4/shodan/helpers.py
new/shodan-1.11.1/shodan/helpers.py
--- old/shodan-1.10.4/shodan/helpers.py 2018-09-23 03:58:44.000000000 +0200
+++ new/shodan-1.11.1/shodan/helpers.py 2019-02-16 03:46:04.000000000 +0100
@@ -19,7 +19,7 @@
if isinstance(facet, basestring):
facet_str += facet
else:
- facet_str += '%s:%s' % (facet[0], facet[1])
+ facet_str += '{}:{}'.format(facet[0], facet[1])
facet_str += ','
return facet_str[:-1]

@@ -76,7 +76,7 @@
# Parse the text into JSON
try:
data = data.json()
- except:
+ except Exception:
raise APIError('Unable to parse JSON response')

# Raise an exception if an error occurred
@@ -113,12 +113,14 @@

for line in fin:
# Ensure the line has been decoded into a string to prevent errors
w/ Python3
- line = line.decode('utf-8')
+ if not isinstance(line, basestring):
+ line = line.decode('utf-8')

# Convert the JSON into a native Python object
banner = loads(line)
yield banner

+
def get_screenshot(banner):
if 'opts' in banner and 'screenshot' in banner['opts']:
return banner['opts']['screenshot']
@@ -159,14 +161,13 @@
>>> humanize_bytes(1024*1234*1111,1)
'1.3 GB'
"""
-
if bytes == 1:
return '1 byte'
if bytes < 1024:
return '%.*f %s' % (precision, bytes, "bytes")

suffixes = ['KB', 'MB', 'GB', 'TB', 'PB']
- multiple = 1024.0 #.0 force float on python 2
+ multiple = 1024.0 # .0 to force float on python 2
for suffix in suffixes:
bytes /= multiple
if bytes < multiple:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/shodan-1.10.4/shodan/stream.py
new/shodan-1.11.1/shodan/stream.py
--- old/shodan-1.10.4/shodan/stream.py 2018-08-01 08:07:51.000000000 +0200
+++ new/shodan-1.11.1/shodan/stream.py 2019-02-11 01:05:46.000000000 +0100
@@ -21,7 +21,7 @@

# The user doesn't want to use a timeout
# If the timeout is specified as 0 then we also don't want to have a
timeout
- if ( timeout and timeout <= 0 ) or ( timeout == 0 ):
+ if (timeout and timeout <= 0) or (timeout == 0):
timeout = None

# If the user requested a timeout then we need to disable heartbeat
messages
@@ -43,16 +43,16 @@
# not specific to Cloudflare.
if req.status_code != 524 or timeout >= 0:
break
- except Exception as e:
+ except Exception:
raise APIError('Unable to contact the Shodan Streaming API')

if req.status_code != 200:
try:
data = json.loads(req.text)
raise APIError(data['error'])
- except APIError as e:
+ except APIError:
raise
- except Exception as e:
+ except Exception:
pass
raise APIError('Invalid API key or you do not have access to the
Streaming API')
if req.encoding is None:
@@ -78,9 +78,9 @@
try:
for line in self._iter_stream(stream, raw):
yield line
- except requests.exceptions.ConnectionError as e:
+ except requests.exceptions.ConnectionError:
raise APIError('Stream timed out')
- except ssl.SSLError as e:
+ except ssl.SSLError:
raise APIError('Stream timed out')

def asn(self, asn, raw=False, timeout=None):
@@ -123,4 +123,3 @@
stream = self._create_stream('/shodan/ports/%s' % ','.join([str(port)
for port in ports]), 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.10.4/shodan/threatnet.py
new/shodan-1.11.1/shodan/threatnet.py
--- old/shodan-1.10.4/shodan/threatnet.py 2018-08-31 03:40:23.000000000
+0200
+++ new/shodan-1.11.1/shodan/threatnet.py 2019-02-11 01:18:29.000000000
+0100
@@ -24,13 +24,13 @@
try:
req = requests.get(self.base_url + name, params={'key':
self.parent.api_key},
stream=True, proxies=self.proxies)
- except:
+ except Exception:
raise APIError('Unable to contact the Shodan Streaming API')

if req.status_code != 200:
try:
raise APIError(req.json()['error'])
- except:
+ except Exception:
pass
raise APIError('Invalid API key or you do not have access to
the Streaming API')
return req
@@ -65,4 +65,3 @@
self.api_key = key
self.base_url = 'https://api.shodan.io'
self.stream = self.Stream(self)
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/shodan-1.10.4/shodan.egg-info/PKG-INFO
new/shodan-1.11.1/shodan.egg-info/PKG-INFO
--- old/shodan-1.10.4/shodan.egg-info/PKG-INFO 2018-10-05 03:00:20.000000000
+0200
+++ new/shodan-1.11.1/shodan.egg-info/PKG-INFO 2019-02-24 10:58:50.000000000
+0100
@@ -1,6 +1,6 @@
-Metadata-Version: 1.1
+Metadata-Version: 2.1
Name: shodan
-Version: 1.10.4
+Version: 1.11.1
Summary: Python library and command-line utility for Shodan
(https://developer.shodan.io)
Home-page: http://github.com/achillean/shodan-python/tree/master
Author: John Matherly
@@ -26,6 +26,7 @@
- `Fast/ bulk IP lookups
<https://help.shodan.io/developer-fundamentals/looking-up-ip-info>`_
- Streaming API support for real-time consumption of Shodan firehose
- `Network alerts (aka private firehose)
<https://help.shodan.io/guides/how-to-monitor-network>`_
+ - `Manage Email Notifications
<https://asciinema.org/a/7WvyDtNxn0YeNU70ozsxvXDmL>`_
- Exploit search API fully implemented
- Bulk data downloads
- `Command-line interface <https://cli.shodan.io>`_
@@ -96,3 +97,4 @@
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Description-Content-Type: text/x-rst
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/shodan-1.10.4/shodan.egg-info/SOURCES.txt
new/shodan-1.11.1/shodan.egg-info/SOURCES.txt
--- old/shodan-1.10.4/shodan.egg-info/SOURCES.txt 2018-10-05
03:00:20.000000000 +0200
+++ new/shodan-1.11.1/shodan.egg-info/SOURCES.txt 2019-02-24
10:58:50.000000000 +0100
@@ -18,7 +18,6 @@
docs/examples/query-summary.rst
shodan/__init__.py
shodan/__main__.py
-shodan/alert.py
shodan/client.py
shodan/exception.py
shodan/helpers.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/shodan-1.10.4/shodan.egg-info/requires.txt
new/shodan-1.11.1/shodan.egg-info/requires.txt
--- old/shodan-1.10.4/shodan.egg-info/requires.txt 2018-10-05
03:00:20.000000000 +0200
+++ new/shodan-1.11.1/shodan.egg-info/requires.txt 2019-02-24
10:58:50.000000000 +0100
@@ -1,5 +1,5 @@
-XlsxWriter
click
click-plugins
colorama
requests>=2.2.1
+XlsxWriter
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/shodan-1.10.4/tests/test_shodan.py
new/shodan-1.11.1/tests/test_shodan.py
--- old/shodan-1.10.4/tests/test_shodan.py 2018-09-22 04:14:03.000000000
+0200
+++ new/shodan-1.11.1/tests/test_shodan.py 2019-02-11 00:53:38.000000000
+0100
@@ -9,148 +9,148 @@

class ShodanTests(unittest.TestCase):

- api = None
- FACETS = [
- 'port',
- ('domain', 1)
- ]
- QUERIES = {
- 'simple': 'cisco-ios',
- 'minify': 'apache',
- 'advanced': 'apache port:443',
- 'empty': 'asdasdasdasdasdasdasdasdasdhjihjkjk',
- }
-
- def setUp(self):
- self.api = shodan.Shodan(open('SHODAN-API-KEY').read().strip())
-
- def test_search_simple(self):
- results = self.api.search(self.QUERIES['simple'])
-
- # Make sure the properties exist
- self.assertIn('matches', results)
- self.assertIn('total', results)
-
- # Make sure no error occurred
- self.assertNotIn('error', results)
-
- # Make sure some values were returned
- self.assertTrue(results['matches'])
- self.assertTrue(results['total'])
-
- # A regular search shouldn't have the optional info
- self.assertNotIn('opts', results['matches'][0])
-
- def test_search_empty(self):
- results = self.api.search(self.QUERIES['empty'])
- self.assertTrue(len(results['matches']) == 0)
- self.assertEqual(results['total'], 0)
-
- def test_search_facets(self):
- results = self.api.search(self.QUERIES['simple'],
facets=self.FACETS)
-
- self.assertTrue(results['facets']['port'])
- self.assertEqual(len(results['facets']['domain']), 1)
-
- def test_count_simple(self):
- results = self.api.count(self.QUERIES['simple'])
-
- # Make sure the properties exist
- self.assertIn('matches', results)
- self.assertIn('total', results)
-
- # Make sure no error occurred
- self.assertNotIn('error', results)
-
- # Make sure no values were returned
- self.assertFalse(results['matches'])
- self.assertTrue(results['total'])
-
- def test_count_facets(self):
- results = self.api.count(self.QUERIES['simple'],
facets=self.FACETS)
-
- self.assertTrue(results['facets']['port'])
- self.assertEqual(len(results['facets']['domain']), 1)
-
- def test_host_details(self):
- host = self.api.host('147.228.101.7')
-
- self.assertEqual('147.228.101.7', host['ip_str'])
- self.assertFalse(isinstance(host['ip'], basestring))
-
- def test_search_minify(self):
- results = self.api.search(self.QUERIES['minify'], minify=False)
- self.assertIn('opts', results['matches'][0])
-
- def test_exploits_search(self):
- results = self.api.exploits.search('apache')
- self.assertIn('matches', results)
- self.assertIn('total', results)
- self.assertTrue(results['matches'])
-
- def test_exploits_search_paging(self):
- results = self.api.exploits.search('apache', page=1)
- match1 = results['matches'][0]
- results = self.api.exploits.search('apache', page=2)
- match2 = results['matches'][0]
-
- self.assertNotEqual(match1['_id'], match2['_id'])
-
- def test_exploits_search_facets(self):
- results = self.api.exploits.search('apache', facets=['source',
('author', 1)])
- self.assertIn('facets', results)
- self.assertTrue(results['facets']['source'])
- self.assertTrue(len(results['facets']['author']) == 1)
-
- def test_exploits_count(self):
- results = self.api.exploits.count('apache')
- self.assertIn('matches', results)
- self.assertIn('total', results)
- self.assertTrue(len(results['matches']) == 0)
-
- def test_exploits_count_facets(self):
- results = self.api.exploits.count('apache', facets=['source',
('author', 1)])
- self.assertEqual(len(results['matches']), 0)
- self.assertIn('facets', results)
- self.assertTrue(results['facets']['source'])
- self.assertTrue(len(results['facets']['author']) == 1)
-
- # Test error responses
- def test_invalid_key(self):
- api = shodan.Shodan('garbage')
- raised = False
- try:
- api.search('something')
- except shodan.APIError as e:
- raised = True
-
- self.assertTrue(raised)
-
- def test_invalid_host_ip(self):
- raised = False
- try:
- host = self.api.host('test')
- except shodan.APIError as e:
- raised = True
-
- self.assertTrue(raised)
-
- def test_search_empty_query(self):
- raised = False
- try:
- self.api.search('')
- except shodan.APIError as e:
- raised = True
- self.assertTrue(raised)
-
- def test_search_advanced_query(self):
- # The free API plan can't use filters
- raised = False
- try:
- self.api.search(self.QUERIES['advanced'])
- except shodan.APIError as e:
- raised = True
- self.assertTrue(raised)
+ api = None
+ FACETS = [
+ 'port',
+ ('domain', 1)
+ ]
+ QUERIES = {
+ 'simple': 'cisco-ios',
+ 'minify': 'apache',
+ 'advanced': 'apache port:443',
+ 'empty': 'asdasdasdasdasdasdasdasdasdhjihjkjk',
+ }
+
+ def setUp(self):
+ self.api = shodan.Shodan(open('SHODAN-API-KEY').read().strip())
+
+ def test_search_simple(self):
+ results = self.api.search(self.QUERIES['simple'])
+
+ # Make sure the properties exist
+ self.assertIn('matches', results)
+ self.assertIn('total', results)
+
+ # Make sure no error occurred
+ self.assertNotIn('error', results)
+
+ # Make sure some values were returned
+ self.assertTrue(results['matches'])
+ self.assertTrue(results['total'])
+
+ # A regular search shouldn't have the optional info
+ self.assertNotIn('opts', results['matches'][0])
+
+ def test_search_empty(self):
+ results = self.api.search(self.QUERIES['empty'])
+ self.assertTrue(len(results['matches']) == 0)
+ self.assertEqual(results['total'], 0)
+
+ def test_search_facets(self):
+ results = self.api.search(self.QUERIES['simple'], facets=self.FACETS)
+
+ self.assertTrue(results['facets']['port'])
+ self.assertEqual(len(results['facets']['domain']), 1)
+
+ def test_count_simple(self):
+ results = self.api.count(self.QUERIES['simple'])
+
+ # Make sure the properties exist
+ self.assertIn('matches', results)
+ self.assertIn('total', results)
+
+ # Make sure no error occurred
+ self.assertNotIn('error', results)
+
+ # Make sure no values were returned
+ self.assertFalse(results['matches'])
+ self.assertTrue(results['total'])
+
+ def test_count_facets(self):
+ results = self.api.count(self.QUERIES['simple'], facets=self.FACETS)
+
+ self.assertTrue(results['facets']['port'])
+ self.assertEqual(len(results['facets']['domain']), 1)
+
+ def test_host_details(self):
+ host = self.api.host('147.228.101.7')
+
+ self.assertEqual('147.228.101.7', host['ip_str'])
+ self.assertFalse(isinstance(host['ip'], basestring))
+
+ def test_search_minify(self):
+ results = self.api.search(self.QUERIES['minify'], minify=False)
+ self.assertIn('opts', results['matches'][0])
+
+ def test_exploits_search(self):
+ results = self.api.exploits.search('apache')
+ self.assertIn('matches', results)
+ self.assertIn('total', results)
+ self.assertTrue(results['matches'])
+
+ def test_exploits_search_paging(self):
+ results = self.api.exploits.search('apache', page=1)
+ match1 = results['matches'][0]
+ results = self.api.exploits.search('apache', page=2)
+ match2 = results['matches'][0]
+
+ self.assertNotEqual(match1['_id'], match2['_id'])
+
+ def test_exploits_search_facets(self):
+ results = self.api.exploits.search('apache', facets=['source',
('author', 1)])
+ self.assertIn('facets', results)
+ self.assertTrue(results['facets']['source'])
+ self.assertTrue(len(results['facets']['author']) == 1)
+
+ def test_exploits_count(self):
+ results = self.api.exploits.count('apache')
+ self.assertIn('matches', results)
+ self.assertIn('total', results)
+ self.assertTrue(len(results['matches']) == 0)
+
+ def test_exploits_count_facets(self):
+ results = self.api.exploits.count('apache', facets=['source',
('author', 1)])
+ self.assertEqual(len(results['matches']), 0)
+ self.assertIn('facets', results)
+ self.assertTrue(results['facets']['source'])
+ self.assertTrue(len(results['facets']['author']) == 1)
+
+ # Test error responses
+ def test_invalid_key(self):
+ api = shodan.Shodan('garbage')
+ raised = False
+ try:
+ api.search('something')
+ except shodan.APIError:
+ raised = True
+
+ self.assertTrue(raised)
+
+ def test_invalid_host_ip(self):
+ raised = False
+ try:
+ self.api.host('test')
+ except shodan.APIError:
+ raised = True
+
+ self.assertTrue(raised)
+
+ def test_search_empty_query(self):
+ raised = False
+ try:
+ self.api.search('')
+ except shodan.APIError:
+ raised = True
+ self.assertTrue(raised)
+
+ def test_search_advanced_query(self):
+ # The free API plan can't use filters
+ raised = False
+ try:
+ self.api.search(self.QUERIES['advanced'])
+ except shodan.APIError:
+ raised = True
+ self.assertTrue(raised)


if __name__ == '__main__':


< Previous Next >
This Thread