Repository: pyupio/safety

Issue:
pip 10 api breakage
Quoting distutils-sig:

> We're in the process of starting to plan for a release of pip (the
long-awaited pip 10). We're likely still a month or two away from a
release, but now is the time for people to start ensuring that
everything works for them. One key change in the new version will be
that all of the internal APIs of pip will no longer be available, so
any code that currently calls functions in the "pip" namespace will
break. Calling pip's internal APIs has never been supported, and
always carried a risk of such breakage, so projects doing so should,
in theory, be prepared for such things. However, reality is not always
that simple, and we are aware that people will need time to deal with
the implications.

> Just in case it's not clear, simply finding where the internal APIs
have moved to and calling them under the new names is *not* what
people should do. We can't stop people calling the internal APIs,
obviously, but the idea of this change is to give people the incentive
to find a supported approach, not just to annoy people who are doing
things we don't want them to ;-)

> So please - if you're calling pip's internals in your code, take the
opportunity *now* to check out the in-development version of pip, and
ensure your project will still work when pip 10 is released.

> And many thanks to anyone else who helps by testing out the new
version, as well :-)

> Thanks,
Paul
_________

Safety uses `pip.get_installed_distributions` which has moved to https://github.com/pypa/pip/blob/master/src/pip/_internal/utils/misc.py#L333

Comments:

Patch:
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..57f3ee6
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,3 @@
+FROM python:3.6-slim
+RUN pip install --trusted-host pypi.python.org safety
+CMD ["python"]
diff --git a/README.md b/README.md
index ae903dc..4cecdb6 100644
--- a/README.md
+++ b/README.md
@@ -110,6 +110,19 @@ echo "insecure-package==0.1" | safety check --stdin
 
 *For more examples, take a look at the [options](#options) section.*
 
+## Using Safety in Docker
+
+Safety can be easily executed as Docker container. To build the container just execute:
+```
+docker build -t safety-docker .
+```
+
+The container can be used just as described in the [examples](#examples) section.
+```
+echo "insecure-package==0.1" | docker run -i --rm safety-docker safety check --stdin
+cat requirements_dev.txt | docker run -i --rm safety-docker safety check --stdin
+```
+
 ## Using Safety with a CI service
 
 Safety works great in your CI pipeline. It returns a non-zero exit status if it finds a vulnerability. 
@@ -139,7 +152,7 @@ and displays a status on GitHub.
 
 Safety is free and open source (MIT Licensed). The underlying open vulnerability database is updated once per month.
 
-To get access to all vulnerabilites as soon as they are added, you need a [Safety API key](https://github.com/pyupio/safety/blob/master/docs/api_key.md) that comes with a paid [pyup.io](https://pyup.io) account, starting at $14.99 for individuals, or $49.99 for organizations.
+To get access to all vulnerabilites as soon as they are added, you need a [Safety API key](https://github.com/pyupio/safety/blob/master/docs/api_key.md) that comes with a paid [pyup.io](https://pyup.io) account, starting at $99 for organizations.
 
 ## Options
 
diff --git a/requirements_dev.txt b/requirements_dev.txt
index b8fb533..a634ab3 100644
--- a/requirements_dev.txt
+++ b/requirements_dev.txt
@@ -1,9 +1,8 @@
 pip==9.0.3
-wheel==0.29.0
-watchdog==0.8.3
+wheel==0.32.3
+watchdog==0.9.0
 flake8==3.3.0
-coverage==4.4
-cryptography==2.3
-Sphinx==1.5.5
-PyYAML==3.12
-
+PyYAML==3.13
+cryptography==2.4.2
+coverage==4.5.2
+Sphinx==1.8.3
\ No newline at end of file
diff --git a/safety/cli.py b/safety/cli.py
index 37ae3ac..2991ff3 100644
--- a/safety/cli.py
+++ b/safety/cli.py
@@ -10,14 +10,6 @@ from safety.util import read_requirements
 from safety.errors import DatabaseFetchError, DatabaseFileNotFoundError, InvalidKeyError
 
 
-try:
-    # pip 9
-    from pip import get_installed_distributions
-except ImportError:
-    # pip 10
-    from pip._internal.utils.misc import get_installed_distributions
-
-
 @click.group()
 @click.version_option(version=__version__)
 def cli():
@@ -57,20 +49,25 @@ def check(key, db, json, full_report, bare, stdin, files, cache, ignore):
     elif stdin:
         packages = list(read_requirements(sys.stdin))
     else:
-        packages = get_installed_distributions()
+        import pkg_resources
+        packages = [
+            d for d in pkg_resources.working_set
+            if d.key not in {"python", "wsgiref", "argparse"}
+        ]
 
     try:
         vulns = safety.check(packages=packages, key=key, db_mirror=db, cached=cache, ignore_ids=ignore)
         click.secho(report(
-            vulns=vulns,
-            full=full_report,
-            json_report=json,
-            bare_report=bare,
-            checked_packages=len(packages),
-            db=db,
-            key=key
+                vulns=vulns,
+                full=full_report,
+                json_report=json,
+                bare_report=bare,
+                checked_packages=len(packages),
+                db=db,
+                key=key
+                ),
+            nl=False if bare and not vulns else True
             )
-        )
         sys.exit(-1 if vulns else 0)
     except InvalidKeyError:
         click.secho("Your API Key '{key}' is invalid. See {link}".format(
diff --git a/safety/formatter.py b/safety/formatter.py
index 8bc57ec..c19bff1 100644
--- a/safety/formatter.py
+++ b/safety/formatter.py
@@ -3,6 +3,7 @@ import platform
 import sys
 import json
 import os
+import textwrap
 
 # python 2.7 compat
 try:
@@ -110,9 +111,10 @@ class SheetReport(object):
 
                     descr = get_advisory(vuln)
 
-                    for chunk in [descr[i:i + 76] for i in range(0, len(descr), 76)]:
-
-                        for line in chunk.splitlines():
+                    for pn, paragraph in enumerate(descr.replace('\r', '').split('\n\n')):
+                        if pn:
+                            table.append("│ {:76} │".format(''))
+                        for line in textwrap.wrap(paragraph, width=76):
                             try:
                                 table.append("│ {:76} │".format(line.encode('utf-8')))
                             except TypeError:
diff --git a/setup.py b/setup.py
index f1b7a1d..a3c50b7 100644
--- a/setup.py
+++ b/setup.py
@@ -18,7 +18,7 @@ except TypeError:
         history = history_file.read()
 
 requirements = [
-    'pip',
+    'setuptools',
     'Click>=6.0',
     'requests',
     'packaging',


Test patch:
diff --git a/tests/test_safety.py b/tests/test_safety.py
index 0777254..c67f568 100644
--- a/tests/test_safety.py
+++ b/tests/test_safety.py
@@ -9,9 +9,8 @@ Tests for `safety` module.
 """
 
 
-import sys
 import unittest
-from contextlib import contextmanager
+import textwrap
 from click.testing import CliRunner
 
 from safety import safety
@@ -40,6 +39,8 @@ class TestSafetyCLI(unittest.TestCase):
 
 class TestFormatter(unittest.TestCase):
 
+    maxDiff = None
+
     def test_get_terminal_size(self):
         try:
             formatter.get_terminal_size()
@@ -56,6 +57,50 @@ class TestFormatter(unittest.TestCase):
         assert 'pyup.io\'s DB' == formatter.get_used_db(key='foo', db='')
         assert 'local DB' == formatter.get_used_db(key=None, db='/usr/local/some-db')
 
+    def test_full_report(self):
+        vulns = [
+            safety.Vulnerability(
+                name='libfoo',
+                spec='<2.0.0',
+                version='1.9.3',
+                advisory='libfoo prior to version 2.0.0 had a vulnerability'
+                         + ' blah' * 15 + '.\r\n\r\n'
+                         + 'All users are urged to upgrade please.\r\n',
+                vuln_id=1234,
+            ),
+        ]
+        full_report = formatter.SheetReport.render(
+            vulns, full=True, checked_packages=5, used_db='test DB')
+        self.assertMultiLineEqual(full_report + "\n", textwrap.dedent(r"""
+            ╒══════════════════════════════════════════════════════════════════════════════╕
+            │                                                                              │
+            │                               /$$$$$$            /$$                         │
+            │                              /$$__  $$          | $$                         │
+            │           /$$$$$$$  /$$$$$$ | $$  \__//$$$$$$  /$$$$$$   /$$   /$$           │
+            │          /$$_____/ |____  $$| $$$$   /$$__  $$|_  $$_/  | $$  | $$           │
+            │         |  $$$$$$   /$$$$$$$| $$_/  | $$$$$$$$  | $$    | $$  | $$           │
+            │          \____  $$ /$$__  $$| $$    | $$_____/  | $$ /$$| $$  | $$           │
+            │          /$$$$$$$/|  $$$$$$$| $$    |  $$$$$$$  |  $$$$/|  $$$$$$$           │
+            │         |_______/  \_______/|__/     \_______/   \___/   \____  $$           │
+            │                                                          /$$  | $$           │
+            │                                                         |  $$$$$$/           │
+            │  by pyup.io                                              \______/            │
+            │                                                                              │
+            ╞══════════════════════════════════════════════════════════════════════════════╡
+            │ REPORT                                                                       │
+            │ checked 5 packages, using test DB                                            │
+            ╞════════════════════════════╤═══════════╤══════════════════════════╤══════════╡
+            │ package                    │ installed │ affected                 │ ID       │
+            ╞════════════════════════════╧═══════════╧══════════════════════════╧══════════╡
+            │ libfoo                     │ 1.9.3     │ <2.0.0                   │     1234 │
+            ╞══════════════════════════════════════════════════════════════════════════════╡
+            │ libfoo prior to version 2.0.0 had a vulnerability blah blah blah blah blah   │
+            │ blah blah blah blah blah blah blah blah blah blah.                           │
+            │                                                                              │
+            │ All users are urged to upgrade please.                                       │
+            ╘══════════════════════════════════════════════════════════════════════════════╛
+            """.lstrip('\n')))
+
 
 class TestSafety(unittest.TestCase):