def analyze(self):
        pkgs_required = self.requirements_txt.packages
        advice_list = []

        for pkg_req in pkgs_required:
            pkg_installed = self.installed_packages.get_by_name(pkg_req.name)

            if not pkg_installed:
                advice = Advice(
                    analyzer=self,
                    severity='error',
                    message=("Required dependency '%s' is not installed" %
                             pkg_req.as_display_name()),
                )
                advice_list.append(advice)

            if pkg_installed and not pkg_req.is_satisfied_by(pkg_installed):
                advice = Advice(
                    analyzer=self,
                    severity='error',
                    message=
                    "Required dependency '%s' is not satisfied by installed '%s'"
                    % (
                        pkg_req.as_display_name(),
                        pkg_installed.as_display_name(),
                    ),
                )
                advice_list.append(advice)

        return AdviceList(advice_list=advice_list)
Esempio n. 2
0
def test_advice_equality():
    analyzer1 = RequiredNotInstalledAnalyzer(
        requirements_txt=None,
        installed_packages=None,
    )

    adv1 = Advice(analyzer=analyzer1,
                  severity='error',
                  message='Package not good')

    # different analyzer

    # different severity
    adv3 = Advice(analyzer=analyzer1,
                  severity='warn',
                  message='Package not good')

    # different message
    adv4 = Advice(analyzer=analyzer1,
                  severity='error',
                  message='Package very good')

    assert not adv1 == None  # noqa
    assert adv1 == adv1
    assert not adv1 != adv1

    assert adv1 != adv3
    assert not adv1 == adv3

    assert adv1 != adv4
    assert not adv1 == adv4

    # Test repr
    assert repr(adv1) == ("<Advice analyzer='RequiredNotInstalledAnalyzer', "
                          "severity='error', message='Package not good'>")
Esempio n. 3
0
def test_advice_list_equality():
    advl1 = AdviceList(advice_list=[
        Advice(analyzer=None, severity='info', message='It rains'),
        Advice(analyzer=None, severity='debug', message='It snows'),
    ], )

    # missing one advice
    advl2 = AdviceList(advice_list=[
        Advice(analyzer=None, severity='info', message='It rains'),
    ], )

    # severity differs
    advl3 = AdviceList(advice_list=[
        Advice(analyzer=None, severity='error', message='It rains'),
        Advice(analyzer=None, severity='debug', message='It snows'),
    ], )

    assert not advl1 == None  # noqa
    assert advl1 == advl1
    assert not advl1 != advl1

    assert advl1 != advl2
    assert not advl1 == advl2

    assert advl1 != advl3
    assert not advl1 == advl3

    # Test repr
    assert repr(advl1) == ("<AdviceList num_advice=2>")

    # XXX improve this
    assert advl1.has_problems() is False
    assert advl2.has_problems() is False
    assert advl3.has_problems() is True
Esempio n. 4
0
    def analyze(self):
        advice_list = []

        pkgs_installed = self.installed_packages.packages

        for pkg_ins in pkgs_installed:

            pkg_req = self.requirements_txt.get_by_name(pkg_ins.name)
            dependents = self.site_packages.get_package_dependents(pkg_ins.name)

            # The installed dependency is not a transitive dependency
            if not dependents:

                # Check if it's a required dependency
                if not pkg_req and pkg_ins.name not in self.VIRTUAL_DEPENDENCIES:
                    advice = Advice(
                        analyzer=self,
                        severity='warn',
                        message=(
                            "Installed non-transitive dependency '%s' is not required"
                        ) % (
                            pkg_ins.as_display_name(),
                        ),
                    )
                    advice_list.append(advice)

            # The installed dependency is a transitive dependency
            elif dependents:

                # Check if it's a required dependency
                if pkg_req:
                    # TODO: Here we should pick a dependent which is also a
                    # requirement, rather than (possibly) getting an untracked
                    # top level package (which will be reported in the case
                    # above anyway)
                    first_dependent = dependents[0]
                    dependent_req = self.requirements_txt.get_by_name(
                        first_dependent,
                        ignore_case=True,
                    )

                    # This is displayed as:
                    #   requests==2.18.4' is a transitive dependency of 'datadog==0.16.0'
                    # and is a bit misleading, because we know it's the package
                    # by name that is a transitive dependency, not by exact
                    # version...
                    advice = Advice(
                        analyzer=self,
                        severity='info',
                        message=(
                            "Required dependency '%s' is a transitive dependency of '%s'"
                        ) % (
                            pkg_req.as_display_name(),
                            (dependent_req.as_display_name() if dependent_req
                             else first_dependent),
                        ),
                    )
                    advice_list.append(advice)

        return AdviceList(advice_list=advice_list)
Esempio n. 5
0
def test_required_installed_analyzer():
    pkgs_reqs = RequirementsTxt(packages=[
        PackageRequirement(name='coverage', operator='<', version='3.6'),
        PackageRequirement(name='parsimonious', operator='<=', version='0.5'),
    ], )

    pkgs_installed = InstalledPackages(
        packages=[
            InstalledPackage(name='coverage', version='3.5'),  # ok
            InstalledPackage(name='parsimonious', version='0.5'),  # ok
        ], )

    analyzer = RequiredInstalledAnalyzer(
        requirements_txt=pkgs_reqs,
        installed_packages=pkgs_installed,
    )
    advice_list = analyzer.analyze()

    expected_advice_list = AdviceList(advice_list=[
        Advice(
            analyzer=analyzer,
            severity='debug',
            message=("Required dependency 'coverage<3.6' "
                     "is satisfied by installed 'coverage-3.5'"),
        ),
        Advice(
            analyzer=analyzer,
            severity='debug',
            message=("Required dependency 'parsimonious<=0.5' "
                     "is satisfied by installed 'parsimonious-0.5'"),
        ),
    ], )

    assert expected_advice_list == advice_list
Esempio n. 6
0
def test_advice_format_display_line():
    analyzer1 = RequiredNotInstalledAnalyzer(
        requirements_txt=None,
        installed_packages=None,
    )

    adv1 = Advice(analyzer=analyzer1,
                  severity='error',
                  message='Package not good')

    assert adv1.format_display_line() == (
        '[RequiredNotInstalled] error: Package not good')
Esempio n. 7
0
    def analyze(self):
        advice_list = []

        pkgs = [
            DistributionStub(pkg) for pkg in self.installed_packages.packages
        ]
        vulns = self.check_func(
            packages=pkgs,
            key=None,
            db_mirror=None,
            cached=None,
            ignore_ids=[],
        )

        for vuln in vulns:
            message = (
                "Installed dependency '%s' has a known vulnerability in '%s'\n    %s"
            ) % (
                '%s-%s' % (vuln.name, vuln.version),
                '%s%s' % (vuln.name, vuln.spec),
                vuln.advisory,
            )

            advice = Advice(
                analyzer=self,
                severity='warn',
                message=message,
            )
            advice_list.append(advice)

        return AdviceList(advice_list=advice_list)
Esempio n. 8
0
def test_is_vulnerable_analyzer():
    pkgs_installed = InstalledPackages(packages=[
        InstalledPackage(name='click', version='1.8'),
        InstalledPackage(name='tornado', version='2.2.0'),
    ], )

    analyzer = IsVulnerableAnalyzer(installed_packages=pkgs_installed, )

    # mock checker
    analyzer.check_func = mocked_check_func

    advice_list = analyzer.analyze()

    expected_advice_list = AdviceList(advice_list=[
        Advice(
            analyzer=analyzer,
            severity='warn',
            message=("Installed dependency 'tornado-2.2.0' "
                     "has a known vulnerability in 'tornado<2.2.1'\n"
                     "    CRLF injection vulnerability in the "
                     "tornado.web.RequestHandler.set_header function "
                     "in Tornado before 2.2.1 allows remote attackers "
                     "to inject arbitrary HTTP headers and conduct HTTP "
                     "response splitting attacks via crafted input."),
        ),
    ], )

    assert expected_advice_list == advice_list
Esempio n. 9
0
    def analyze(self):
        pkgs_required = self.requirements_txt.packages
        advice_list = []

        for pkg_req in pkgs_required:

            # Check if the requirement can be upgraded
            pkg_releases = self.available_packages.get_by_name(pkg_req.name)
            pkg_release = pkg_releases.get_more_recent_than_requirement(
                pkg_req)

            if pkg_release:
                advice = Advice(
                    analyzer=self,
                    severity='info',
                    message="Required dependency '%s' can be upgraded to '%s'"
                    % (
                        pkg_req.as_display_name(),
                        pkg_release.as_display_name_single(),
                    ),
                )
                advice_list.append(advice)

            # Check if the installed version can be updated
            pkg_installed = self.installed_packages.get_by_name(pkg_req.name)
            if pkg_installed:
                pkg_release = pkg_releases.update_installed(
                    pkg_req, pkg_installed)

                if pkg_release:
                    advice = Advice(
                        analyzer=self,
                        severity='info',
                        message=
                        "Installed dependency '%s' can be updated to '%s'" % (
                            pkg_installed.as_display_name(),
                            pkg_release.as_display_name_single(),
                        ),
                    )
                    advice_list.append(advice)

        return AdviceList(advice_list=advice_list)
def test_required_not_installed_analyzer():
    pkgs_reqs = RequirementsTxt(packages=[
        PackageRequirement(name='coverage', operator='<', version='3.6'),
        PackageRequirement(name='parsimonious', operator='<=', version='0.5'),
        PackageRequirement(name='six', operator='>=', version='0.5'),
    ], )

    pkgs_installed = InstalledPackages(
        packages=[
            InstalledPackage(name='coverage', version='3.5'),  # ok
            # parsimonious missing
            InstalledPackage(name='six', version='0.4.9'),  # version too low
        ], )

    analyzer = RequiredNotInstalledAnalyzer(
        requirements_txt=pkgs_reqs,
        installed_packages=pkgs_installed,
    )
    advice_list = analyzer.analyze()

    expected_advice_list = AdviceList(advice_list=[
        Advice(
            analyzer=analyzer,
            severity='error',
            message=(
                "Required dependency 'parsimonious<=0.5' is not installed"),
        ),
        Advice(
            analyzer=analyzer,
            severity='error',
            message=("Required dependency 'six>=0.5' "
                     "is not satisfied by installed 'six-0.4.9'"),
        ),
    ], )

    assert expected_advice_list == advice_list
Esempio n. 11
0
def test_is_unused_analyzer():
    pkgs_reqs = RequirementsTxt(packages=[
        PackageRequirement(name='click', operator='==', version='1.8'),
        PackageRequirement(name='six', operator='==', version='3.5'),
    ], )

    pkgs_installed = InstalledPackages(packages=[
        InstalledPackage(name='click', version='1.8'),
        InstalledPackage(name='six', version='3.5'),
    ], )

    # mock: the top level of the package is the name of the package
    site_packages = SitePackages(
        python_path=None,
        installed_package_names=[],
    )
    site_packages.get_package_top_levels = lambda pkg_name: [pkg_name]

    # mock: grep says six is used, nothing else is
    git_grep = GitGrep(basedir=None)
    git_grep.package_is_imported = lambda pkg_name: (True if pkg_name == 'six'
                                                     else False)

    analyzer = IsUnusedAnalyzer(
        requirements_txt=pkgs_reqs,
        installed_packages=pkgs_installed,
        site_packages=site_packages,
        grep=git_grep,
    )
    advice_list = analyzer.analyze()

    expected_advice_list = AdviceList(advice_list=[
        Advice(
            analyzer=analyzer,
            severity='info',
            message=("Required dependency 'click==1.8' "
                     "is never imported (click)"),
        ),
    ], )

    assert expected_advice_list == advice_list
Esempio n. 12
0
    def analyze(self):
        advice_list = []

        pkgs_required = self.requirements_txt.packages

        for pkg_req in pkgs_required:

            # If the package is installed we can discover its top level
            # importable names
            pkg_ins = self.installed_packages.get_by_name(pkg_req.name)
            if pkg_ins:

                # If it have top level names we can see if they are ever
                # imported in the code
                top_levels = self.site_packages.get_package_top_levels(
                    pkg_ins.name)
                if top_levels:

                    # Check every top level name
                    is_imported = False
                    for top_level in top_levels:
                        if self.grep.package_is_imported(top_level):
                            is_imported = True

                    if not is_imported:
                        advice = Advice(
                            analyzer=self,
                            severity='info',
                            message=
                            ("Required dependency '%s' is never imported (%s)")
                            %
                            (pkg_req.as_display_name(), ', '.join(top_levels)),
                        )
                        advice_list.append(advice)

        return AdviceList(advice_list=advice_list)
Esempio n. 13
0
def test_advice_ctor():
    with pytest.raises(ValueError):
        Advice(analyzer=None, severity='extreme', message='OK')
Esempio n. 14
0
def test_can_be_upgraded_analyzer():
    pkgs_available = AvailablePackages(
        packages=[
            PackageReleases(name='abc', versions=('8.5', '8.6')),
            PackageReleases(name='click', versions=('1.8', '1.9')),
            PackageReleases(name='coverage', versions=('3.5', '3.6', '4.0', '4.1')),
            PackageReleases(name='ply', versions=('0.5', '0.5.1')),
            PackageReleases(name='six', versions=('0.8', '0.9')),
        ],
    )

    pkgs_reqs = RequirementsTxt(
        packages=[
            PackageRequirement(name='abc', operator='<=', version='9.0'),
            PackageRequirement(name='click', operator=None, version=None),
            PackageRequirement(name='coverage', operator='<', version='4.0'),  # -> 4.1
            PackageRequirement(name='ply', operator='==', version='0.5'),  # -> 0.5.1
            PackageRequirement(name='six', operator='==', version='0.9'),
        ],
    )

    pkgs_installed = InstalledPackages(
        packages=[
            InstalledPackage(name='abc', version='8.5'),  # -> 8.6
            InstalledPackage(name='click', version='1.8'),  # -> 1.9
            InstalledPackage(name='coverage', version='3.5'),  # -> 3.6
        ],
    )

    analyzer = CanBeUpgradedAnalyzer(
        requirements_txt=pkgs_reqs,
        installed_packages=pkgs_installed,
        available_packages=pkgs_available,
    )
    advice_list = analyzer.analyze()

    expected_advice_list = AdviceList(
        advice_list=[
            Advice(
                analyzer=analyzer,
                severity='info',
                message=(
                    "Installed dependency 'abc-8.5' "
                    "can be updated to 'abc-8.6'"
                ),
            ),
            Advice(
                analyzer=analyzer,
                severity='info',
                message=(
                    "Installed dependency 'click-1.8' "
                    "can be updated to 'click-1.9'"
                ),
            ),
            Advice(
                analyzer=analyzer,
                severity='info',
                message=(
                    "Required dependency 'coverage<4.0' "
                    "can be upgraded to 'coverage-4.1'"
                ),
            ),
            Advice(
                analyzer=analyzer,
                severity='info',
                message=(
                    "Installed dependency 'coverage-3.5' "
                    "can be updated to 'coverage-3.6'"
                ),
            ),
            Advice(
                analyzer=analyzer,
                severity='info',
                message=(
                    "Required dependency 'ply==0.5' "
                    "can be upgraded to 'ply-0.5.1'"
                ),
            ),
        ],
    )

    assert expected_advice_list == advice_list
Esempio n. 15
0
def test_can_be_upgraded_analyzer():
    pkgs_reqs = RequirementsTxt(
        packages=[
            PackageRequirement(name='locustio', operator='==',
                               version='0.8a2'),  # top level
            PackageRequirement(name='Flask', operator='==',
                               version='0.12.2'),  # one level down
            PackageRequirement(name='Jinja2', operator='==',
                               version='2.9.6'),  # two levels down
        ], )

    pkgs_installed = InstalledPackages(
        packages=[
            # locustio dependency closure
            InstalledPackage(name='Flask', version='0.12.2'),
            InstalledPackage(name='Jinja2', version='2.9.6'),
            InstalledPackage(name='MarkupSafe', version='1.0'),
            InstalledPackage(name='Werkzeug', version='0.12.2'),
            InstalledPackage(name='certifi', version='2017.7.27.1'),
            InstalledPackage(name='chardet', version='3.0.4'),
            InstalledPackage(name='click', version='6.7'),
            InstalledPackage(name='gevent', version='1.2.2'),
            InstalledPackage(name='greenlet', version='0.4.12'),
            InstalledPackage(name='idna', version='2.6'),
            InstalledPackage(name='itsdangerous', version='0.24'),
            InstalledPackage(name='locustio', version='0.8a2'),
            InstalledPackage(name='msgpack-python', version='0.4.8'),
            InstalledPackage(name='pyzmq', version='15.2.0'),
            InstalledPackage(name='requests', version='2.18.4'),
            InstalledPackage(name='six', version='1.11.0'),
            InstalledPackage(name='urllib3', version='1.22'),

            # top level dependencies, but not reported as they are known
            # "virtual" dependencies
            InstalledPackage(name='pkg-resources', version='0.0.0'),
            InstalledPackage(name='setuptools', version='36.5.0'),

            # top level dependency that is not required - should be reported
            InstalledPackage(name='ipdb', version='0.10.3'),
        ], )

    installed_names = [pkg.name for pkg in pkgs_installed.packages]
    site_packages = SitePackages(
        python_path=None,
        installed_package_names=installed_names,
    )
    site_packages._query_cache = EXAMPLE_RESULTS_DICT

    analyzer = IsTransitiveDepAnalyzer(
        requirements_txt=pkgs_reqs,
        installed_packages=pkgs_installed,
        site_packages=site_packages,
    )
    advice_list = analyzer.analyze()

    expected_advice_list = AdviceList(advice_list=[
        Advice(
            analyzer=analyzer,
            severity='info',
            message=("Required dependency 'Flask==0.12.2' "
                     "is a transitive dependency of 'locustio==0.8a2'"),
        ),
        Advice(
            analyzer=analyzer,
            severity='info',
            message=("Required dependency 'Jinja2==2.9.6' "
                     "is a transitive dependency of 'locustio==0.8a2'"),
        ),
        Advice(
            analyzer=analyzer,
            severity='warn',
            message=("Installed non-transitive dependency 'ipdb-0.10.3' "
                     "is not required"),
        ),
    ], )

    assert expected_advice_list == advice_list