Пример #1
0
    def test_check_live_cached(self):
        from safety.constants import CACHE_FILE

        # lets clear the cache first
        try:
            with open(CACHE_FILE, 'w') as f:
                f.write(json.dumps({}))
        except Exception:
            pass

        reqs = StringIO("insecure-package==0.1")
        packages = util.read_requirements(reqs)

        vulns, _ = safety.check(packages=packages,
                                key=None,
                                db_mirror=False,
                                cached=60 * 60,
                                ignore_vulns={},
                                ignore_severity_rules=None,
                                proxy={},
                                telemetry=False)
        self.assertEqual(len(vulns), 1)

        reqs = StringIO("insecure-package==0.1")
        packages = util.read_requirements(reqs)
        # make a second call to use the cache
        vulns, _ = safety.check(packages=packages,
                                key=None,
                                db_mirror=False,
                                cached=60 * 60,
                                ignore_vulns={},
                                ignore_severity_rules=None,
                                proxy={},
                                telemetry=False)
        self.assertEqual(len(vulns), 1)
Пример #2
0
 def check(self):
     vulns = check(self.requirements)
     if vulns:
         self.menu_item.icon = ICONS.RED
     else:
         self.menu_item.icon = ICONS.GREEN
     return vulns
Пример #3
0
    def run(self,
            filename,
            file,
            db_path: path = '',
            cve_ignore: typed_list(str) = []):
        """
        Checks for vulnerable package versions in requirements files.

        :param db_path:           Path to a local vulnerability database.
        :param cve_ignore:        A list of CVE number to be ignore.
        """
        db_path = self.db_path if not db_path else db_path
        packages = list(
            Package(key=req.key, version=req.specs[0][1])
            for req in self.try_parse_requirements(file)
            if len(req.specs) == 1 and req.specs[0][0] == '==')

        if not packages:
            return

        for vulnerability in safety.check(packages,
                                          key=None,
                                          db_mirror=db_path,
                                          cached=False,
                                          ignore_ids=cve_ignore,
                                          proxy=None):
            if 'cve' in vulnerability.vuln_id.strip().lower():
                message_template = (
                    '{vuln.name}{vuln.spec} is vulnerable to {vuln.vuln_id} '
                    'and your project is using {vuln.version}.')
            else:
                message_template = (
                    '{vuln.name}{vuln.spec} is vulnerable to '
                    'pyup.io-{vuln.vuln_id} and your project is using '
                    '{vuln.version}.')

            # StopIteration should not ever happen so skipping its branch
            line_number, line = next(  # pragma: no branch
                (index, line) for index, line in enumerate(file, start=1)
                if vulnerability.name in line)
            version_spec_match = re.search(r'[=<>]+(\S+?)(?:$|\s|#)', line)
            source_range = SourceRange.from_values(
                filename,
                line_number,
                version_spec_match.start(1) + 1,
                line_number,
                version_spec_match.end(1) + 1,
            )

            yield Result(
                self,
                message_template.format(vuln=vulnerability),
                additional_info=vulnerability.advisory,
                affected_code=(source_range, ),
            )
Пример #4
0
    def run(self, filename, file,
            db_path: path = '',
            cve_ignore: typed_list(str) = []):
        """
        Checks for vulnerable package versions in requirements files.

        :param db_path:           Path to a local vulnerability database.
        :param cve_ignore:        A list of CVE number to be ignore.
        """
        db_path = self.db_path if not db_path else db_path
        packages = list(
            Package(key=req.key, version=req.specs[0][1])
            for req in self.try_parse_requirements(file)
            if len(req.specs) == 1 and req.specs[0][0] == '=='
        )

        if not packages:
            return

        for vulnerability in safety.check(packages, key=None,
                                          db_mirror=db_path, cached=False,
                                          ignore_ids=cve_ignore):
            if 'cve' in vulnerability.vuln_id.strip().lower():
                message_template = (
                    '{vuln.name}{vuln.spec} is vulnerable to {vuln.vuln_id} '
                    'and your project is using {vuln.version}.'
                )
            else:
                message_template = (
                    '{vuln.name}{vuln.spec} is vulnerable to '
                    'pyup.io-{vuln.vuln_id} and your project is using '
                    '{vuln.version}.'
                )

            # StopIteration should not ever happen so skipping its branch
            line_number, line = next(  # pragma: no branch
                (index, line) for index, line in enumerate(file, start=1)
                if vulnerability.name in line
            )
            version_spec_match = re.search(r'[=<>]+(\S+?)(?:$|\s|#)', line)
            source_range = SourceRange.from_values(
                filename,
                line_number,
                version_spec_match.start(1) + 1,
                line_number,
                version_spec_match.end(1) + 1,
            )

            yield Result(
                self,
                message_template.format(vuln=vulnerability),
                additional_info=vulnerability.advisory,
                affected_code=(source_range, ),
            )
Пример #5
0
    def test_check_from_file(self):
        reqs = StringIO("Django==1.8.1")
        packages = util.read_requirements(reqs)

        vulns, _ = safety.check(
            packages=packages,
            key=None,
            db_mirror=os.path.join(os.path.dirname(os.path.realpath(__file__)),
                                   "test_db"),
            cached=0,
            ignore_vulns={},
            ignore_severity_rules=None,
            proxy={},
            telemetry=False)
        self.assertEqual(len(vulns), 2)
Пример #6
0
def safety_provider(dependencies):
    """Pypi source is scanned with `safety`."""
    for dependency in dependencies:
        packages = [
            safetyutil.Package(key=dependency['name'],
                               version=dependency['version'])
        ]
        vulns = safety.check(packages=packages,
                             key=Config.SAFETY_API_KEY,
                             db_mirror='',
                             cached=False,
                             ignore_ids=[])
        for vuln in vulns:
            Vulnerability.get_or_create(artifact=dependency['id'],
                                        provider="safety",
                                        reference=vuln.vuln_id,
                                        details=vuln._asdict())
Пример #7
0
    def run(self, filename, file):
        """
        Checks for vulnerable package versions in requirements files.
        """
        packages = list(
            Package(key=req.key, version=req.specs[0][1])
            for req in self.try_parse_requirements(file)
            if len(req.specs) == 1 and req.specs[0][0] == '=='
        )

        if not packages:
            return

        for vulnerability in safety.check(packages=packages):
            if vulnerability.is_cve:
                message_template = (
                    '{vuln.name}{vuln.spec} is vulnerable to {vuln.cve_id} '
                    'and your project is using {vuln.version}.'
                )
            else:
                message_template = (
                    '{vuln.name}{vuln.spec} is vulnerable and your project is '
                    'using {vuln.version}.'
                )

            # StopIteration should not ever happen so skipping its branch
            line_number, line = next(  # pragma: no branch
                (index, line) for index, line in enumerate(file, start=1)
                if vulnerability.name in line
            )
            version_spec_match = re.search(r'[=<>]+(\S+?)(?:$|\s|#)', line)
            source_range = SourceRange.from_values(
                filename,
                line_number,
                version_spec_match.start(1) + 1,
                line_number,
                version_spec_match.end(1) + 1,
            )

            yield Result(
                self,
                message_template.format(vuln=vulnerability),
                additional_info=vulnerability.description,
                affected_code=(source_range, ),
            )
Пример #8
0
def check(key, db, json, full_report, bare, stdin, files, cache, ignore):

    if files and stdin:
        click.secho("Can't read from --stdin and --file at the same time, exiting", fg="red")
        sys.exit(-1)

    if files:
        packages = list(itertools.chain.from_iterable(read_requirements(f, resolve=True) for f in files))
    elif stdin:
        packages = list(read_requirements(sys.stdin))
    else:
        packages = get_installed_distributions()

    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
            )
        )
        sys.exit(-1 if vulns else 0)
    except InvalidKeyError:
        click.secho("Your API Key '{key}' is invalid. See {link}".format(
            key=key, link='https://goo.gl/O7Y1rS'),
            fg="red")
        sys.exit(-1)
    except DatabaseFileNotFoundError:
        click.secho("Unable to load vulnerability database from {db}".format(db=db), fg="red")
        sys.exit(-1)
    except DatabaseFetchError:
        click.secho("Unable to load vulnerability database", fg="red")
        sys.exit(-1)
Пример #9
0
def check(ctx, key, db, full_report, stdin, files, cache, ignore, output, json,
          bare, proxy_protocol, proxy_host, proxy_port, exit_code, policy_file,
          save_json):
    """
    Find vulnerabilities in Python dependencies at the target provided.

    """
    LOG.info('Running check command')

    try:
        packages = get_packages(files, stdin)
        proxy_dictionary = get_proxy_dict(proxy_protocol, proxy_host,
                                          proxy_port)

        announcements = []
        if not db:
            LOG.info('Not local DB used, Getting announcements')
            announcements = safety.get_announcements(
                key=key,
                proxy=proxy_dictionary,
                telemetry=ctx.parent.telemetry)

        ignore_severity_rules = None
        ignore, ignore_severity_rules, exit_code = get_processed_options(
            policy_file, ignore, ignore_severity_rules, exit_code)

        is_env_scan = not stdin and not files
        params = {
            'stdin': stdin,
            'files': files,
            'policy_file': policy_file,
            'continue_on_error': not exit_code,
            'ignore_severity_rules': ignore_severity_rules
        }
        LOG.info('Calling the check function')
        vulns, db_full = safety.check(
            packages=packages,
            key=key,
            db_mirror=db,
            cached=cache,
            ignore_vulns=ignore,
            ignore_severity_rules=ignore_severity_rules,
            proxy=proxy_dictionary,
            include_ignored=True,
            is_env_scan=is_env_scan,
            telemetry=ctx.parent.telemetry,
            params=params)
        LOG.debug('Vulnerabilities returned: %s', vulns)
        LOG.debug('full database returned is None: %s', db_full is None)

        LOG.info('Safety is going to calculate remediations')
        remediations = safety.calculate_remediations(vulns, db_full)

        LOG.info(
            'Safety is going to render the vulnerabilities report using %s output',
            output)
        output_report = SafetyFormatter(output=output).render_vulnerabilities(
            announcements, vulns, remediations, full_report, packages)

        # Announcements are send to stderr if not terminal, it doesn't depend on "exit_code" value
        if announcements and (not sys.stdout.isatty() and os.environ.get(
                "SAFETY_OS_DESCRIPTION", None) != 'run'):
            LOG.info(
                'sys.stdout is not a tty, announcements are going to be send to stderr'
            )
            click.secho(SafetyFormatter(
                output='text').render_announcements(announcements),
                        fg="red",
                        file=sys.stderr)

        found_vulns = list(filter(lambda v: not v.ignored, vulns))
        LOG.info('Vulnerabilities found (Not ignored): %s', len(found_vulns))
        LOG.info('All vulnerabilities found (ignored and Not ignored): %s',
                 len(vulns))

        if save_json:
            default_name = 'safety-report.json'
            json_report = output_report

            if output != 'json':
                json_report = SafetyFormatter(
                    output='json').render_vulnerabilities(
                        announcements, vulns, remediations, full_report,
                        packages)
            if os.path.isdir(save_json):
                save_json = os.path.join(save_json, default_name)

            with open(save_json, 'w+') as output_json_file:
                output_json_file.write(json_report)

        click.secho(output_report,
                    nl=should_add_nl(output, found_vulns),
                    file=sys.stdout)

        if exit_code and found_vulns:
            LOG.info('Exiting with default code for vulnerabilities found')
            sys.exit(EXIT_CODE_VULNERABILITIES_FOUND)

        sys.exit(EXIT_CODE_OK)

    except SafetyError as e:
        LOG.exception('Expected SafetyError happened: %s', e)
        output_exception(e, exit_code_output=exit_code)
    except Exception as e:
        LOG.exception('Unexpected Exception happened: %s', e)
        exception = e if isinstance(e, SafetyException) else SafetyException(
            info=e)
        output_exception(exception, exit_code_output=exit_code)