diff --git a/README.md b/README.md index 5bfc9a8..b3f2134 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Scanning 19 packages... ## Installation -The easiest way to install the `export` plugin is via the `plugin add` command of Poetry. +The easiest way to install the `audit` plugin is via the `plugin add` command of Poetry. ```bash poetry plugin add poetry-audit-plugin @@ -38,20 +38,36 @@ pip install poetry-audit-plugin * `--json`: Export the result in JSON format. * `--ignore-code`: Ignore some vulnerabilities IDs. Receive a list of IDs. For example: + ```bash poetry audit --ignore-code=CVE-2022-42969,CVE-2020-10684 ``` * `--ignore-package`: Ignore some packages. Receive a list of packages. For example: + ```bash poetry audit --json --ignore-package=py,ansible-tower-cli ``` + +* `--proxy-protocol`, `--proxy-host`, `--proxy-port`: Proxy to access Safety DB. For example: + +```bash +poetry audit --proxy-protocol=http --proxy-host=localhost --proxy-port=3128 +``` + +* `--cache-sec`: How long Safety DB can be cached locally. For example: + +```bash +poetry audit --cache-sec=60 +``` + ## Exit codes `poetry audit` will exit with a code indicating its status. * `0`: Vulnerabilities were not found. * `1`: One or more vulnerabilities were found. +* Others: Something wrong happened. ## Develop poetry-audit-plugin @@ -69,6 +85,7 @@ Once you've done it, you can start developing poetry-audit-plugin. You can use t ```sh cd tests/assets/no_vulnerabilities +poetry shell poetry audit ``` diff --git a/poetry_audit_plugin/command.py b/poetry_audit_plugin/command.py index c3968ec..49cb6e0 100644 --- a/poetry_audit_plugin/command.py +++ b/poetry_audit_plugin/command.py @@ -1,16 +1,24 @@ +import copy import json import sys -from typing import Any, Dict, List +from typing import Any, Dict, List, Tuple from cleo.helpers import option from poetry.console.commands.command import Command from poetry_audit_plugin import __version__ +from poetry_audit_plugin.constants import ( + EXIT_CODE_OK, + EXIT_CODE_OPTION_INVALID, + EXIT_CODE_VULNERABILITY_FOUND, +) +from poetry_audit_plugin.errors import SafetyDBAccessError, SafetyDBSessionBuildError from poetry_audit_plugin.safety import ( Package, + Vulnerability, VulnerablePackage, + build_safety_db_session, check_vulnerable_packages, - suppress_vulnerable_packages, ) @@ -19,17 +27,59 @@ class AuditCommand(Command): description = "Check vulnerabilities in dependencies" options = [ - option("json", None, "Generate a JSON payload with the information of vulnerable packages.", flag=True), - option("ignore-code", None, "Ignore specified vulnerability codes", flag=False), - option("ignore-package", None, "Ignore specified packages", flag=False), + option( + long_name="json", + description="Generate a JSON payload with the information of vulnerable packages.", + flag=True, + ), + option( + long_name="ignore-code", + description="Ignore specified vulnerability codes.", + flag=False, + ), + option( + long_name="ignore-package", + description="Ignore specified packages.", + flag=False, + ), + option( + long_name="proxy-protocol", + description="Protocol of proxy to access Safety DB.", + flag=False, + value_required=False, + default="http", + ), + option( + long_name="proxy-host", + description="Host of proxy to access Safety DB.", + flag=False, + value_required=False, + ), + option( + long_name="proxy-port", + description="Port of proxy to access Safety DB.", + flag=False, + value_required=False, + default="80", + ), + option( + long_name="cache-sec", + description="How long Safety DB can be cached locally.", + flag=False, + value_required=False, + default="0", + ), ] def handle(self) -> None: self.is_quiet = self.option("json") + self.line("<b># poetry audit report</b>") + self.line("") + + self.validate_options() self.validate_lock_file() - self.line("<b># poetry audit report</b>") self.line("<info>Loading...</info>") locked_repo = self.poetry.locker.locked_repository() @@ -40,22 +90,45 @@ def handle(self) -> None: self.line(f"<info>Scanning {len(packages)} packages...</info>") self.line("") - all_vulnerable_packages = check_vulnerable_packages(packages) - ignored_packages: List[str] = self.option("ignore-package").split(",") if self.option("ignore-package") else [] ignored_codes: List[str] = self.option("ignore-code").split(",") if self.option("ignore-code") else [] is_ignore = bool(len(ignored_packages) or len(ignored_codes)) - vulnerable_packages, amount_of_ignored_vulnerabilities = suppress_vulnerable_packages( - all_vulnerable_packages, ignored_packages, ignored_codes - ) + try: + # TODO: Pass auth key to build_client_session function for advanced safety usage. + session = build_safety_db_session( + proxy_protocol=self.option("proxy-protocol"), + proxy_host=self.option("proxy-host"), + proxy_port=int(self.option("proxy-port")) if self.option("proxy-port") else None, + ) + except SafetyDBSessionBuildError as e: + self.chatty_line_error(f"<error>Error occured while building Safety DB session.</error>") + self.chatty_line_error("") + self.chatty_line_error(str(e)) + sys.exit(e.get_exit_code()) + try: + all_vulnerable_packages = check_vulnerable_packages( + session, packages, int(self.option("cache-sec")) if self.option("cache-sec") else 0 + ) + except SafetyDBAccessError as e: + self.chatty_line_error(f"<error>Error occured while accessing Safety DB.</error>") + self.chatty_line_error("") + self.chatty_line_error(str(e)) + sys.exit(e.get_exit_code()) + vulnerable_packages, amount_of_ignored_vulnerabilities = self.filter_vulnerable_packages( + all_vulnerable_packages, + ignored_packages, + ignored_codes, + ) max_line_lengths = self.calculate_line_length(vulnerable_packages) amount_of_vulnerable_packages = len(vulnerable_packages) if self.option("json"): json_report = self.get_json_report(vulnerable_packages) self.chatty_line(json_report) if amount_of_vulnerable_packages > 0: - sys.exit(1) + sys.exit(EXIT_CODE_VULNERABILITY_FOUND) + else: + sys.exit(EXIT_CODE_OK) else: amount_of_vulnerabilities = 0 for vulnerable_package in vulnerable_packages: @@ -82,10 +155,10 @@ def handle(self) -> None: self.line( f"<error>{amount_of_vulnerabilities}</error> <b>vulnerabilities found in {amount_of_vulnerable_packages} packages</b>" ) - sys.exit(1) + sys.exit(EXIT_CODE_VULNERABILITY_FOUND) else: - self.line("<b>Vulnerabilities not found</b> ✨✨") - sys.exit(0) + self.line("<b>No vulnerabilities found</b> ✨✨") + sys.exit(EXIT_CODE_OK) def line(self, *args: Any, **kwargs: Any) -> None: if not self.is_quiet: @@ -99,13 +172,34 @@ def chatty_line(self, *args: Any, **kwargs: Any) -> None: super().line(*args, **kwargs) def chatty_line_error(self, *args: Any, **kwargs: Any) -> None: - super().line(*args, **kwargs) + super().line_error(*args, **kwargs) + + def validate_options(self) -> None: + errors: List[str] = [] + if self.option("proxy-host") and (not self.option("proxy-protocol") or not self.option("proxy-port")): + errors.append("proxy-protocol and proxy-port should not be empty when proxy-host is specified.") + + if self.option("proxy-protocol") and (self.option("proxy-protocol") not in ["http", "https"]): + errors.append("proxy-protocol should be http or https.") + + if self.option("proxy-port") and not self.option("proxy-port").isnumeric(): + errors.append("proxy-port should be number.") + + if self.option("cache-sec") and not self.option("cache-sec").isnumeric(): + errors.append("cache-sec be number") + + if errors: + self.chatty_line_error("<error>Command line option(s) are invalid</error>") + for error in errors: + self.chatty_line_error(error) + sys.exit(EXIT_CODE_OPTION_INVALID) def validate_lock_file(self) -> None: + # Ref: https://github.com/python-poetry/poetry/blob/1.2.0b1/src/poetry/console/commands/export.py#L40 locker = self.poetry.locker if not locker.is_locked(): self.line_error("<comment>The lock file does not exist. Locking.</comment>") - option = "quiet" if self.is_quiet() else None + option = "quiet" if self.is_quiet else None self.call("lock", option) self.line("") @@ -131,8 +225,7 @@ def calculate_line_length(self, vulnerable_packages: List[VulnerablePackage]) -> else: line_length = len(getattr(vulnerability, key)) - max_line_length = max_line_lengths[key] - if line_length > max_line_length: + if line_length > max_line_lengths[key]: max_line_lengths[key] = line_length return max_line_lengths @@ -152,6 +245,39 @@ def get_json_report(self, vulnerable_packages: List[VulnerablePackage]) -> str: } return json.dumps(json_report_dict, indent=2) + def filter_vulnerable_packages( + self, vulnerable_packages: List[VulnerablePackage], ignored_packages: List[str], ignored_codes: List[str] + ) -> Tuple[List[VulnerablePackage], int]: + filtered_vulnerable_packages: List[VulnerablePackage] = [] + amount_of_ignored_vulnerabilities = 0 + + is_ignore_packages = len(ignored_packages) > 0 + is_ignore_codes = len(ignored_codes) > 0 + + for vulnerable_package in vulnerable_packages: + filtered_vulnerable_package = copy.copy(vulnerable_package) + if is_ignore_packages: + if vulnerable_package.name in ignored_packages: + amount_of_ignored_vulnerabilities += len(vulnerable_package.vulnerabilities) + continue + + if is_ignore_codes: + filtered_vulnerabilities: List[Vulnerability] = [] + for vulnerability in vulnerable_package.vulnerabilities: + if vulnerability.cve not in ignored_codes: + filtered_vulnerabilities.append(vulnerability) + else: + amount_of_ignored_vulnerabilities += 1 + + if len(filtered_vulnerabilities): + filtered_vulnerable_package.vulnerabilities = filtered_vulnerabilities + else: + continue + + filtered_vulnerable_packages.append(filtered_vulnerable_package) + + return filtered_vulnerable_packages, amount_of_ignored_vulnerabilities + def factory(): return AuditCommand() diff --git a/poetry_audit_plugin/constants.py b/poetry_audit_plugin/constants.py new file mode 100644 index 0000000..3122292 --- /dev/null +++ b/poetry_audit_plugin/constants.py @@ -0,0 +1,5 @@ +EXIT_CODE_OK = 0 +EXIT_CODE_VULNERABILITY_FOUND = 1 +EXIT_CODE_OPTION_INVALID = 64 +EXIT_CODE_SAFETY_DB_SESSION_BUILD_ERROR = 65 +EXIT_CODE_SAFETY_DB_ACCESS_ERROR = 66 diff --git a/poetry_audit_plugin/errors.py b/poetry_audit_plugin/errors.py new file mode 100644 index 0000000..75d0dbc --- /dev/null +++ b/poetry_audit_plugin/errors.py @@ -0,0 +1,22 @@ +from poetry_audit_plugin.constants import ( + EXIT_CODE_SAFETY_DB_ACCESS_ERROR, + EXIT_CODE_SAFETY_DB_SESSION_BUILD_ERROR, +) + + +class SafetyDBSessionBuildError(Exception): + def __init__(self, message) -> None: + self.message = message + super().__init__(self.message) + + def get_exit_code(self) -> int: + return EXIT_CODE_SAFETY_DB_SESSION_BUILD_ERROR + + +class SafetyDBAccessError(Exception): + def __init__(self, message) -> None: + self.message = message + super().__init__(self.message) + + def get_exit_code(self) -> int: + return EXIT_CODE_SAFETY_DB_ACCESS_ERROR diff --git a/poetry_audit_plugin/safety.py b/poetry_audit_plugin/safety.py index 2e2bcc1..9361b6f 100644 --- a/poetry_audit_plugin/safety.py +++ b/poetry_audit_plugin/safety.py @@ -1,8 +1,11 @@ -from typing import Any, Dict, Iterator, List, Tuple +from typing import Any, Dict, Iterator, List, Optional from packaging.specifiers import SpecifierSet +from safety.auth import build_client_session from safety.safety import fetch_database +from poetry_audit_plugin.errors import SafetyDBAccessError, SafetyDBSessionBuildError + class Package: def __init__(self, name: str, version: str) -> None: @@ -34,35 +37,69 @@ def format(self) -> Dict[str, Any]: } -def get_vulnerable_entry(pkg_name: str, spec: str, db_full: Dict[str, Any]) -> Iterator[Dict[str, Any]]: - for entry in db_full.get(pkg_name, []): +def build_safety_db_session( + key: Optional[str] = None, + proxy_protocol: Optional[str] = None, + proxy_host: Optional[str] = None, + proxy_port: Optional[int] = None, +) -> Any: + # Ref: https://github.com/pyupio/safety/blob/3.0.1/safety/auth/cli_utils.py#L130 + proxy_config: Optional[Dict[str, str]] = None + if proxy_host and proxy_port and proxy_protocol: + proxy_config = {"https": f"{proxy_protocol}://{proxy_host}:{str(proxy_port)}"} + try: + # Note: proxy_config is ignored when it's invalid or inaccessible inside build_client_session + session, _ = build_client_session(api_key=key, proxies=proxy_config) + except Exception as e: + raise SafetyDBSessionBuildError(str(e)) + + return session + + +def get_vulnerable_entry(pkg_name: str, spec: str, db_full: Dict[str, Dict[str, Any]]) -> Iterator[Dict[str, Any]]: + for entry in db_full.get("vulnerable_packages", {}).get(pkg_name, []): for entry_spec in entry.get("specs", []): if entry_spec == spec: yield entry -def check_vulnerable_packages(packages: List[Package]) -> List[VulnerablePackage]: - db: Dict[str, Any] = fetch_database() - db_full: Dict[str, Any] = {} +def check_vulnerable_packages(session: Any, packages: List[Package], cache_sec: int = 0) -> List[VulnerablePackage]: + """ + Check vulnerabilities in given packages by checking Safety DB. + + If cache_sec is not 0, Safety DB is cached in $HOME/.safety/200/ and it can be used for next scan. + """ + # Ref: https://github.com/pyupio/safety/blob/2.3.5/safety/safety.py#L320 + # Ref: https://github.com/pyupio/safety/blob/3.0.1/safety/scan/finder/handlers.py#L50 + try: + db: Dict[str, Dict[str, Any]] = fetch_database( + session, full=False, db=False, cached=cache_sec, telemetry=False, from_cache=True + ) + db_full: Dict[str, Dict[str, Any]] = fetch_database( + session, full=True, db=False, cached=cache_sec, telemetry=False, from_cache=True + ) + except Exception as e: + raise SafetyDBAccessError(str(e)) + vulnerable_packages: List[VulnerablePackage] = [] for pkg in packages: name = pkg.name.replace("_", "-").lower() vulnerabilities: List[Vulnerability] = [] - if name in frozenset(db.keys()): - specifiers: List[str] = db[name] - for specifier in specifiers: - spec_set = SpecifierSet(specifiers=specifier) - if spec_set.contains(pkg.version): - if not db_full: - db_full = fetch_database(full=True) - for data in get_vulnerable_entry(pkg_name=name, spec=specifier, db_full=db_full): - cve = data.get("cve") - if cve: - cve = cve.split(",")[0].strip() - if data.get("id"): - vulnerabilities.append( - Vulnerability(advisory=data.get("advisory", ""), cve=cve, spec=specifier) - ) + if name not in db.get("vulnerable_packages", {}).keys(): + continue + + specifiers: List[str] = db["vulnerable_packages"][name] + for specifier in specifiers: + spec_set = SpecifierSet(specifiers=specifier) + if not spec_set.contains(pkg.version): + continue + + for entry in get_vulnerable_entry(pkg_name=name, spec=specifier, db_full=db_full): + for cve in entry.get("ids", []): + if cve.get("type") in ["cve", "pve"] and cve.get("id"): + vulnerabilities.append( + Vulnerability(advisory=entry.get("advisory", ""), cve=cve["id"], spec=specifier) + ) if vulnerabilities: vulnerable_packages.append( @@ -70,36 +107,3 @@ def check_vulnerable_packages(packages: List[Package]) -> List[VulnerablePackage ) return vulnerable_packages - - -def suppress_vulnerable_packages( - vulnerable_packages: List[VulnerablePackage], ignored_packages: List[str], ignored_codes: List[str] -) -> Tuple[List[VulnerablePackage], int]: - filtered_vulnerable_packages: List[VulnerablePackage] = [] - amount_of_ignored_vulnerabilities = 0 - - is_ignore_packages = len(ignored_packages) > 0 - is_ignore_codes = len(ignored_codes) > 0 - - for vulnerable_package in vulnerable_packages: - if is_ignore_packages: - if vulnerable_package.name in ignored_packages: - amount_of_ignored_vulnerabilities += len(vulnerable_package.vulnerabilities) - continue - - if is_ignore_codes: - filtered_vulnerabilities: List[Vulnerability] = [] - for vulnerability in vulnerable_package.vulnerabilities: - if vulnerability.cve not in ignored_codes: - filtered_vulnerabilities.append(vulnerability) - else: - amount_of_ignored_vulnerabilities += 1 - - if len(filtered_vulnerabilities): - vulnerable_package.vulnerabilities = filtered_vulnerabilities - else: - continue - - filtered_vulnerable_packages.append(vulnerable_package) - - return filtered_vulnerable_packages, amount_of_ignored_vulnerabilities diff --git a/pyproject.toml b/pyproject.toml index e0f73b0..accb473 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,10 +21,10 @@ classifiers = [ "Topic :: System :: Installation/Setup", "Topic :: System :: Software Distribution", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", "License :: OSI Approved :: MIT License" ] keywords = ["poetry", "vulnerabilities", "security", "audit"] @@ -32,7 +32,7 @@ keywords = ["poetry", "vulnerabilities", "security", "audit"] [tool.poetry.dependencies] python = "^3.8" poetry = "^1.6.1" -safety = "^2.3.5" +safety = "^3.0.0" [tool.poetry.group.dev.dependencies] pytest = "^6.2.5" diff --git a/tests/assets/no_vulnerabilities/poetry.lock b/tests/assets/no_vulnerabilities/poetry.lock index c3585c5..2024428 100644 --- a/tests/assets/no_vulnerabilities/poetry.lock +++ b/tests/assets/no_vulnerabilities/poetry.lock @@ -1,643 +1,17 @@ -[[package]] -name = "cachecontrol" -version = "0.12.10" -description = "httplib2 caching for requests" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -lockfile = {version = ">=0.9", optional = true, markers = "extra == \"filecache\""} -msgpack = ">=0.5.2" -requests = "*" - -[package.extras] -filecache = ["lockfile (>=0.9)"] -redis = ["redis (>=2.10.5)"] - -[[package]] -name = "cachy" -version = "0.3.0" -description = "Cachy provides a simple yet effective caching library." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[package.extras] -redis = ["redis (>=3.3.6,<4.0.0)"] -memcached = ["python-memcached (>=1.59,<2.0)"] -msgpack = ["msgpack-python (>=0.5,<0.6)"] - -[[package]] -name = "certifi" -version = "2021.10.8" -description = "Python package for providing Mozilla's CA Bundle." -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "charset-normalizer" -version = "2.0.12" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "main" -optional = false -python-versions = ">=3.5.0" - -[package.extras] -unicode_backport = ["unicodedata2"] - -[[package]] -name = "cleo" -version = "1.0.0a4" -description = "Cleo allows you to create beautiful and testable command-line interfaces." -category = "main" -optional = false -python-versions = ">=3.6,<4.0" - -[package.dependencies] -crashtest = ">=0.3.1,<0.4.0" -pylev = ">=1.3.0,<2.0.0" - -[[package]] -name = "crashtest" -version = "0.3.1" -description = "Manage Python errors with ease" -category = "main" -optional = false -python-versions = ">=3.6,<4.0" - -[[package]] -name = "cryptography" -version = "36.0.2" -description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." -category = "main" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "distlib" -version = "0.3.4" -description = "Distribution utilities" -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "entrypoints" -version = "0.3" -description = "Discover and load entry points from installed packages." -category = "main" -optional = false -python-versions = ">=2.7" - -[[package]] -name = "filelock" -version = "3.6.0" -description = "A platform independent file lock." -category = "main" -optional = false -python-versions = ">=3.7" - -[package.extras] -docs = ["furo (>=2021.8.17b43)", "sphinx (>=4.1)", "sphinx-autodoc-typehints (>=1.12)"] -testing = ["covdefaults (>=1.2.0)", "coverage (>=4)", "pytest (>=4)", "pytest-cov", "pytest-timeout (>=1.4.2)"] - -[[package]] -name = "html5lib" -version = "1.1" -description = "HTML parser based on the WHATWG HTML specification" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[package.dependencies] -six = ">=1.9" -webencodings = "*" - -[package.extras] -all = ["genshi", "chardet (>=2.2)", "lxml"] -chardet = ["chardet (>=2.2)"] -genshi = ["genshi"] -lxml = ["lxml"] - -[[package]] -name = "idna" -version = "3.3" -description = "Internationalized Domain Names in Applications (IDNA)" -category = "main" -optional = false -python-versions = ">=3.5" - -[[package]] -name = "importlib-metadata" -version = "4.11.3" -description = "Read metadata from Python packages" -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -zipp = ">=0.5" - -[package.extras] -docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"] -perf = ["ipython"] -testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "importlib-resources (>=1.3)"] - -[[package]] -name = "jeepney" -version = "0.8.0" -description = "Low-level, pure Python DBus protocol wrapper." -category = "main" -optional = false -python-versions = ">=3.7" - -[package.extras] -test = ["pytest", "pytest-trio", "pytest-asyncio (>=0.17)", "testpath", "trio", "async-timeout"] -trio = ["trio", "async-generator"] - -[[package]] -name = "keyring" -version = "23.5.0" -description = "Store and access your passwords safely." -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -importlib-metadata = ">=3.6" -jeepney = {version = ">=0.4.2", markers = "sys_platform == \"linux\""} -pywin32-ctypes = {version = "<0.1.0 || >0.1.0,<0.1.1 || >0.1.1", markers = "sys_platform == \"win32\""} -SecretStorage = {version = ">=3.2", markers = "sys_platform == \"linux\""} - -[package.extras] -docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)", "jaraco.tidelift (>=1.4)"] -testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-black (>=0.3.7)", "pytest-mypy"] +# This file is automatically @generated by Poetry 1.8.1 and should not be changed by hand. [[package]] -name = "lockfile" -version = "0.12.2" -description = "Platform-independent file locking module" -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "msgpack" -version = "1.0.3" -description = "MessagePack (de)serializer." -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "packaging" -version = "20.9" -description = "Core utilities for Python packages" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[package.dependencies] -pyparsing = ">=2.0.2" - -[[package]] -name = "pexpect" -version = "4.8.0" -description = "Pexpect allows easy control of interactive console applications." -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -ptyprocess = ">=0.5" - -[[package]] -name = "pkginfo" -version = "1.8.2" -description = "Query metadatdata from sdists / bdists / installed packages." -category = "main" -optional = false -python-versions = "*" - -[package.extras] -testing = ["coverage", "nose"] - -[[package]] -name = "platformdirs" -version = "2.5.1" -description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "main" -optional = false -python-versions = ">=3.7" - -[package.extras] -docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"] -test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] - -[[package]] -name = "poetry" -version = "1.2.0b1" -description = "Python dependency management and packaging made easy." -category = "main" -optional = false -python-versions = ">=3.7,<4.0" - -[package.dependencies] -cachecontrol = {version = ">=0.12.9,<0.13.0", extras = ["filecache"]} -cachy = ">=0.3.0,<0.4.0" -cleo = ">=1.0.0a4,<2.0.0" -crashtest = ">=0.3.0,<0.4.0" -entrypoints = ">=0.3,<0.4" -html5lib = ">=1.0,<2.0" -keyring = ">=21.2.0" -packaging = ">=20.4,<21.0" -pexpect = ">=4.7.0,<5.0.0" -pkginfo = ">=1.5,<2.0" -poetry-core = ">=1.1.0a7,<2.0.0" -requests = ">=2.18,<3.0" -requests-toolbelt = ">=0.9.1,<0.10.0" -shellingham = ">=1.1,<2.0" -tomlkit = ">=0.7.0,<1.0.0" -urllib3 = ">=1.26.0,<2.0.0" -virtualenv = "*" - -[[package]] -name = "poetry-core" -version = "1.1.0a7" -description = "Poetry PEP 517 Build Backend" -category = "main" -optional = false -python-versions = ">=3.7,<4.0" - -[[package]] -name = "poetry-opeco17-test-plugin" -version = "1.0.0" +name = "opeco17-dummy-package" +version = "0.1.0" description = "" -category = "main" -optional = false -python-versions = ">=3.7,<4.0" - -[package.dependencies] -poetry = ">=1.2.0b1dev0,<2.0.0" - -[[package]] -name = "ptyprocess" -version = "0.7.0" -description = "Run a subprocess in a pseudo terminal" -category = "main" optional = false -python-versions = "*" - -[[package]] -name = "pylev" -version = "1.4.0" -description = "A pure Python Levenshtein implementation that's not freaking GPL'd." -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "pyparsing" -version = "3.0.8" -description = "pyparsing module - Classes and methods to define and execute parsing grammars" -category = "main" -optional = false -python-versions = ">=3.6.8" - -[package.extras] -diagrams = ["railroad-diagrams", "jinja2"] - -[[package]] -name = "pywin32-ctypes" -version = "0.2.0" -description = "" -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "requests" -version = "2.27.1" -description = "Python HTTP for Humans." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" - -[package.dependencies] -certifi = ">=2017.4.17" -charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""} -idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} -urllib3 = ">=1.21.1,<1.27" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] -use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] - -[[package]] -name = "requests-toolbelt" -version = "0.9.1" -description = "A utility belt for advanced users of python-requests" -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -requests = ">=2.0.1,<3.0.0" - -[[package]] -name = "secretstorage" -version = "3.3.1" -description = "Python bindings to FreeDesktop.org Secret Service API" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -cryptography = ">=2.0" -jeepney = ">=0.6" - -[[package]] -name = "shellingham" -version = "1.4.0" -description = "Tool to Detect Surrounding Shell" -category = "main" -optional = false -python-versions = "!=3.0,!=3.1,!=3.2,!=3.3,>=2.6" - -[[package]] -name = "six" -version = "1.16.0" -description = "Python 2 and 3 compatibility utilities" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" - -[[package]] -name = "tomlkit" -version = "0.10.1" -description = "Style preserving TOML library" -category = "main" -optional = false -python-versions = ">=3.6,<4.0" - -[[package]] -name = "urllib3" -version = "1.26.9" -description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" - -[package.extras] -brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"] -secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] -socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] - -[[package]] -name = "virtualenv" -version = "20.14.1" -description = "Virtual Python Environment builder" -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" - -[package.dependencies] -distlib = ">=0.3.1,<1" -filelock = ">=3.2,<4" -platformdirs = ">=2,<3" -six = ">=1.9.0,<2" - -[package.extras] -docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=21.3)"] -testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)"] - -[[package]] -name = "webencodings" -version = "0.5.1" -description = "Character encoding aliases for legacy web content" -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "zipp" -version = "3.8.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -category = "main" -optional = false -python-versions = ">=3.7" - -[package.extras] -docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"] -testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"] +python-versions = ">=3.10,<4.0" +files = [ + {file = "opeco17_dummy_package-0.1.0-py3-none-any.whl", hash = "sha256:57ba5f4732a2d308a4189bafd5a2a06f97ba8fcb2c92e1c17a2ed5452a39ae75"}, + {file = "opeco17_dummy_package-0.1.0.tar.gz", hash = "sha256:db558a510f52c651b502e3e576fc7f712e753ea35c80599c01469db7df17661c"}, +] [metadata] -lock-version = "1.1" -python-versions = "^3.9" -content-hash = "2ff55d310a7b1d66a7ecd23f0bc85076a6479091cf3bcdabe9e152331e0f0082" - -[metadata.files] -cachecontrol = [ - {file = "CacheControl-0.12.10-py2.py3-none-any.whl", hash = "sha256:b0d43d8f71948ef5ebdee5fe236b86c6ffc7799370453dccb0e894c20dfa487c"}, - {file = "CacheControl-0.12.10.tar.gz", hash = "sha256:d8aca75b82eec92d84b5d6eb8c8f66ea16f09d2adb09dbca27fe2d5fc8d3732d"}, -] -cachy = [ - {file = "cachy-0.3.0-py2.py3-none-any.whl", hash = "sha256:338ca09c8860e76b275aff52374330efedc4d5a5e45dc1c5b539c1ead0786fe7"}, - {file = "cachy-0.3.0.tar.gz", hash = "sha256:186581f4ceb42a0bbe040c407da73c14092379b1e4c0e327fdb72ae4a9b269b1"}, -] -certifi = [ - {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"}, - {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"}, -] -charset-normalizer = [ - {file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"}, - {file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"}, -] -cleo = [ - {file = "cleo-1.0.0a4-py3-none-any.whl", hash = "sha256:cdd0c3458c15ced3a9f0204b1e53a1b4bee3c56ebcb3ac54c872a56acc657a09"}, - {file = "cleo-1.0.0a4.tar.gz", hash = "sha256:a103a065d031b7d936ee88a6b93086a69bd9c1b40fa2ebfe8c056285a66b481d"}, -] -crashtest = [ - {file = "crashtest-0.3.1-py3-none-any.whl", hash = "sha256:300f4b0825f57688b47b6d70c6a31de33512eb2fa1ac614f780939aa0cf91680"}, - {file = "crashtest-0.3.1.tar.gz", hash = "sha256:42ca7b6ce88b6c7433e2ce47ea884e91ec93104a4b754998be498a8e6c3d37dd"}, -] -cryptography = [ - {file = "cryptography-36.0.2-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:4e2dddd38a5ba733be6a025a1475a9f45e4e41139d1321f412c6b360b19070b6"}, - {file = "cryptography-36.0.2-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:4881d09298cd0b669bb15b9cfe6166f16fc1277b4ed0d04a22f3d6430cb30f1d"}, - {file = "cryptography-36.0.2-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ea634401ca02367c1567f012317502ef3437522e2fc44a3ea1844de028fa4b84"}, - {file = "cryptography-36.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:7be666cc4599b415f320839e36367b273db8501127b38316f3b9f22f17a0b815"}, - {file = "cryptography-36.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8241cac0aae90b82d6b5c443b853723bcc66963970c67e56e71a2609dc4b5eaf"}, - {file = "cryptography-36.0.2-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b2d54e787a884ffc6e187262823b6feb06c338084bbe80d45166a1cb1c6c5bf"}, - {file = "cryptography-36.0.2-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:c2c5250ff0d36fd58550252f54915776940e4e866f38f3a7866d92b32a654b86"}, - {file = "cryptography-36.0.2-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:ec6597aa85ce03f3e507566b8bcdf9da2227ec86c4266bd5e6ab4d9e0cc8dab2"}, - {file = "cryptography-36.0.2-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ca9f686517ec2c4a4ce930207f75c00bf03d94e5063cbc00a1dc42531511b7eb"}, - {file = "cryptography-36.0.2-cp36-abi3-win32.whl", hash = "sha256:f64b232348ee82f13aac22856515ce0195837f6968aeaa94a3d0353ea2ec06a6"}, - {file = "cryptography-36.0.2-cp36-abi3-win_amd64.whl", hash = "sha256:53e0285b49fd0ab6e604f4c5d9c5ddd98de77018542e88366923f152dbeb3c29"}, - {file = "cryptography-36.0.2-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:32db5cc49c73f39aac27574522cecd0a4bb7384e71198bc65a0d23f901e89bb7"}, - {file = "cryptography-36.0.2-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b3d199647468d410994dbeb8cec5816fb74feb9368aedf300af709ef507e3e"}, - {file = "cryptography-36.0.2-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:da73d095f8590ad437cd5e9faf6628a218aa7c387e1fdf67b888b47ba56a17f0"}, - {file = "cryptography-36.0.2-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:0a3bf09bb0b7a2c93ce7b98cb107e9170a90c51a0162a20af1c61c765b90e60b"}, - {file = "cryptography-36.0.2-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8897b7b7ec077c819187a123174b645eb680c13df68354ed99f9b40a50898f77"}, - {file = "cryptography-36.0.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82740818f2f240a5da8dfb8943b360e4f24022b093207160c77cadade47d7c85"}, - {file = "cryptography-36.0.2-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:1f64a62b3b75e4005df19d3b5235abd43fa6358d5516cfc43d87aeba8d08dd51"}, - {file = "cryptography-36.0.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e167b6b710c7f7bc54e67ef593f8731e1f45aa35f8a8a7b72d6e42ec76afd4b3"}, - {file = "cryptography-36.0.2.tar.gz", hash = "sha256:70f8f4f7bb2ac9f340655cbac89d68c527af5bb4387522a8413e841e3e6628c9"}, -] -distlib = [ - {file = "distlib-0.3.4-py2.py3-none-any.whl", hash = "sha256:6564fe0a8f51e734df6333d08b8b94d4ea8ee6b99b5ed50613f731fd4089f34b"}, - {file = "distlib-0.3.4.zip", hash = "sha256:e4b58818180336dc9c529bfb9a0b58728ffc09ad92027a3f30b7cd91e3458579"}, -] -entrypoints = [ - {file = "entrypoints-0.3-py2.py3-none-any.whl", hash = "sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19"}, - {file = "entrypoints-0.3.tar.gz", hash = "sha256:c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"}, -] -filelock = [ - {file = "filelock-3.6.0-py3-none-any.whl", hash = "sha256:f8314284bfffbdcfa0ff3d7992b023d4c628ced6feb957351d4c48d059f56bc0"}, - {file = "filelock-3.6.0.tar.gz", hash = "sha256:9cd540a9352e432c7246a48fe4e8712b10acb1df2ad1f30e8c070b82ae1fed85"}, -] -html5lib = [ - {file = "html5lib-1.1-py2.py3-none-any.whl", hash = "sha256:0d78f8fde1c230e99fe37986a60526d7049ed4bf8a9fadbad5f00e22e58e041d"}, - {file = "html5lib-1.1.tar.gz", hash = "sha256:b2e5b40261e20f354d198eae92afc10d750afb487ed5e50f9c4eaf07c184146f"}, -] -idna = [ - {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, - {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, -] -importlib-metadata = [ - {file = "importlib_metadata-4.11.3-py3-none-any.whl", hash = "sha256:1208431ca90a8cca1a6b8af391bb53c1a2db74e5d1cef6ddced95d4b2062edc6"}, - {file = "importlib_metadata-4.11.3.tar.gz", hash = "sha256:ea4c597ebf37142f827b8f39299579e31685c31d3a438b59f469406afd0f2539"}, -] -jeepney = [ - {file = "jeepney-0.8.0-py3-none-any.whl", hash = "sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755"}, - {file = "jeepney-0.8.0.tar.gz", hash = "sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806"}, -] -keyring = [ - {file = "keyring-23.5.0-py3-none-any.whl", hash = "sha256:b0d28928ac3ec8e42ef4cc227822647a19f1d544f21f96457965dc01cf555261"}, - {file = "keyring-23.5.0.tar.gz", hash = "sha256:9012508e141a80bd1c0b6778d5c610dd9f8c464d75ac6774248500503f972fb9"}, -] -lockfile = [ - {file = "lockfile-0.12.2-py2.py3-none-any.whl", hash = "sha256:6c3cb24f344923d30b2785d5ad75182c8ea7ac1b6171b08657258ec7429d50fa"}, - {file = "lockfile-0.12.2.tar.gz", hash = "sha256:6aed02de03cba24efabcd600b30540140634fc06cfa603822d508d5361e9f799"}, -] -msgpack = [ - {file = "msgpack-1.0.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:96acc674bb9c9be63fa8b6dabc3248fdc575c4adc005c440ad02f87ca7edd079"}, - {file = "msgpack-1.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2c3ca57c96c8e69c1a0d2926a6acf2d9a522b41dc4253a8945c4c6cd4981a4e3"}, - {file = "msgpack-1.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0a792c091bac433dfe0a70ac17fc2087d4595ab835b47b89defc8bbabcf5c73"}, - {file = "msgpack-1.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c58cdec1cb5fcea8c2f1771d7b5fec79307d056874f746690bd2bdd609ab147"}, - {file = "msgpack-1.0.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f97c0f35b3b096a330bb4a1a9247d0bd7e1f3a2eba7ab69795501504b1c2c39"}, - {file = "msgpack-1.0.3-cp310-cp310-win32.whl", hash = "sha256:36a64a10b16c2ab31dcd5f32d9787ed41fe68ab23dd66957ca2826c7f10d0b85"}, - {file = "msgpack-1.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c1ba333b4024c17c7591f0f372e2daa3c31db495a9b2af3cf664aef3c14354f7"}, - {file = "msgpack-1.0.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c2140cf7a3ec475ef0938edb6eb363fa704159e0bf71dde15d953bacc1cf9d7d"}, - {file = "msgpack-1.0.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f4c22717c74d44bcd7af353024ce71c6b55346dad5e2cc1ddc17ce8c4507c6b"}, - {file = "msgpack-1.0.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d733a15ade190540c703de209ffbc42a3367600421b62ac0c09fde594da6ec"}, - {file = "msgpack-1.0.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7e03b06f2982aa98d4ddd082a210c3db200471da523f9ac197f2828e80e7770"}, - {file = "msgpack-1.0.3-cp36-cp36m-win32.whl", hash = "sha256:3d875631ecab42f65f9dce6f55ce6d736696ced240f2634633188de2f5f21af9"}, - {file = "msgpack-1.0.3-cp36-cp36m-win_amd64.whl", hash = "sha256:40fb89b4625d12d6027a19f4df18a4de5c64f6f3314325049f219683e07e678a"}, - {file = "msgpack-1.0.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6eef0cf8db3857b2b556213d97dd82de76e28a6524853a9beb3264983391dc1a"}, - {file = "msgpack-1.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d8c332f53ffff01953ad25131272506500b14750c1d0ce8614b17d098252fbc"}, - {file = "msgpack-1.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c0903bd93cbd34653dd63bbfcb99d7539c372795201f39d16fdfde4418de43a"}, - {file = "msgpack-1.0.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bf1e6bfed4860d72106f4e0a1ab519546982b45689937b40257cfd820650b920"}, - {file = "msgpack-1.0.3-cp37-cp37m-win32.whl", hash = "sha256:d02cea2252abc3756b2ac31f781f7a98e89ff9759b2e7450a1c7a0d13302ff50"}, - {file = "msgpack-1.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:2f30dd0dc4dfe6231ad253b6f9f7128ac3202ae49edd3f10d311adc358772dba"}, - {file = "msgpack-1.0.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:f201d34dc89342fabb2a10ed7c9a9aaaed9b7af0f16a5923f1ae562b31258dea"}, - {file = "msgpack-1.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bb87f23ae7d14b7b3c21009c4b1705ec107cb21ee71975992f6aca571fb4a42a"}, - {file = "msgpack-1.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a3a5c4b16e9d0edb823fe54b59b5660cc8d4782d7bf2c214cb4b91a1940a8ef"}, - {file = "msgpack-1.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f74da1e5fcf20ade12c6bf1baa17a2dc3604958922de8dc83cbe3eff22e8b611"}, - {file = "msgpack-1.0.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:73a80bd6eb6bcb338c1ec0da273f87420829c266379c8c82fa14c23fb586cfa1"}, - {file = "msgpack-1.0.3-cp38-cp38-win32.whl", hash = "sha256:9fce00156e79af37bb6db4e7587b30d11e7ac6a02cb5bac387f023808cd7d7f4"}, - {file = "msgpack-1.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:9b6f2d714c506e79cbead331de9aae6837c8dd36190d02da74cb409b36162e8a"}, - {file = "msgpack-1.0.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:89908aea5f46ee1474cc37fbc146677f8529ac99201bc2faf4ef8edc023c2bf3"}, - {file = "msgpack-1.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:973ad69fd7e31159eae8f580f3f707b718b61141838321c6fa4d891c4a2cca52"}, - {file = "msgpack-1.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da24375ab4c50e5b7486c115a3198d207954fe10aaa5708f7b65105df09109b2"}, - {file = "msgpack-1.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a598d0685e4ae07a0672b59792d2cc767d09d7a7f39fd9bd37ff84e060b1a996"}, - {file = "msgpack-1.0.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4c309a68cb5d6bbd0c50d5c71a25ae81f268c2dc675c6f4ea8ab2feec2ac4e2"}, - {file = "msgpack-1.0.3-cp39-cp39-win32.whl", hash = "sha256:494471d65b25a8751d19c83f1a482fd411d7ca7a3b9e17d25980a74075ba0e88"}, - {file = "msgpack-1.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:f01b26c2290cbd74316990ba84a14ac3d599af9cebefc543d241a66e785cf17d"}, - {file = "msgpack-1.0.3.tar.gz", hash = "sha256:51fdc7fb93615286428ee7758cecc2f374d5ff363bdd884c7ea622a7a327a81e"}, -] -packaging = [ - {file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"}, - {file = "packaging-20.9.tar.gz", hash = "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5"}, -] -pexpect = [ - {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"}, - {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"}, -] -pkginfo = [ - {file = "pkginfo-1.8.2-py2.py3-none-any.whl", hash = "sha256:c24c487c6a7f72c66e816ab1796b96ac6c3d14d49338293d2141664330b55ffc"}, - {file = "pkginfo-1.8.2.tar.gz", hash = "sha256:542e0d0b6750e2e21c20179803e40ab50598d8066d51097a0e382cba9eb02bff"}, -] -platformdirs = [ - {file = "platformdirs-2.5.1-py3-none-any.whl", hash = "sha256:bcae7cab893c2d310a711b70b24efb93334febe65f8de776ee320b517471e227"}, - {file = "platformdirs-2.5.1.tar.gz", hash = "sha256:7535e70dfa32e84d4b34996ea99c5e432fa29a708d0f4e394bbcb2a8faa4f16d"}, -] -poetry = [ - {file = "poetry-1.2.0b1-py3-none-any.whl", hash = "sha256:e3d68c88492550c48df10c738e962f1f770ad71e715bab878a46f527e1ce81d2"}, - {file = "poetry-1.2.0b1.tar.gz", hash = "sha256:26cf8d309a74fff25d768219c2215a989a530acab886c01de3db07ab70bc7abf"}, -] -poetry-core = [ - {file = "poetry-core-1.1.0a7.tar.gz", hash = "sha256:4622ae680842ac9b1b9c3b0e8dc467c2e291d1a5c434b6bd413907a2e5571d92"}, - {file = "poetry_core-1.1.0a7-py3-none-any.whl", hash = "sha256:724e8b5368f270461e622396305d0c2e760ec9d4c14d072e6b944da9384c67de"}, -] -poetry-opeco17-test-plugin = [ - {file = "poetry-opeco17-test-plugin-1.0.0.tar.gz", hash = "sha256:5b734dc66ecbbf7cb1fb6b068e99c20e83f83e4b48ef723b6e0cbd92430b3500"}, - {file = "poetry_opeco17_test_plugin-1.0.0-py3-none-any.whl", hash = "sha256:15cd6780040b6eeabc5310bc4bcdd6af2317fe81e42dcc9f3694742e9ce0bc89"}, -] -ptyprocess = [ - {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, - {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, -] -pylev = [ - {file = "pylev-1.4.0-py2.py3-none-any.whl", hash = "sha256:7b2e2aa7b00e05bb3f7650eb506fc89f474f70493271a35c242d9a92188ad3dd"}, - {file = "pylev-1.4.0.tar.gz", hash = "sha256:9e77e941042ad3a4cc305dcdf2b2dec1aec2fbe3dd9015d2698ad02b173006d1"}, -] -pyparsing = [ - {file = "pyparsing-3.0.8-py3-none-any.whl", hash = "sha256:ef7b523f6356f763771559412c0d7134753f037822dad1b16945b7b846f7ad06"}, - {file = "pyparsing-3.0.8.tar.gz", hash = "sha256:7bf433498c016c4314268d95df76c81b842a4cb2b276fa3312cfb1e1d85f6954"}, -] -pywin32-ctypes = [ - {file = "pywin32-ctypes-0.2.0.tar.gz", hash = "sha256:24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942"}, - {file = "pywin32_ctypes-0.2.0-py2.py3-none-any.whl", hash = "sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98"}, -] -requests = [ - {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"}, - {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"}, -] -requests-toolbelt = [ - {file = "requests-toolbelt-0.9.1.tar.gz", hash = "sha256:968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0"}, - {file = "requests_toolbelt-0.9.1-py2.py3-none-any.whl", hash = "sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f"}, -] -secretstorage = [ - {file = "SecretStorage-3.3.1-py3-none-any.whl", hash = "sha256:422d82c36172d88d6a0ed5afdec956514b189ddbfb72fefab0c8a1cee4eaf71f"}, - {file = "SecretStorage-3.3.1.tar.gz", hash = "sha256:fd666c51a6bf200643495a04abb261f83229dcb6fd8472ec393df7ffc8b6f195"}, -] -shellingham = [ - {file = "shellingham-1.4.0-py2.py3-none-any.whl", hash = "sha256:536b67a0697f2e4af32ab176c00a50ac2899c5a05e0d8e2dadac8e58888283f9"}, - {file = "shellingham-1.4.0.tar.gz", hash = "sha256:4855c2458d6904829bd34c299f11fdeed7cfefbf8a2c522e4caea6cd76b3171e"}, -] -six = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] -tomlkit = [ - {file = "tomlkit-0.10.1-py3-none-any.whl", hash = "sha256:3eba517439dcb2f84cf39f4f85fd2c3398309823a3c75ac3e73003638daf7915"}, - {file = "tomlkit-0.10.1.tar.gz", hash = "sha256:3c517894eadef53e9072d343d37e4427b8f0b6200a70b7c9a19b2ebd1f53b951"}, -] -urllib3 = [ - {file = "urllib3-1.26.9-py2.py3-none-any.whl", hash = "sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14"}, - {file = "urllib3-1.26.9.tar.gz", hash = "sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e"}, -] -virtualenv = [ - {file = "virtualenv-20.14.1-py2.py3-none-any.whl", hash = "sha256:e617f16e25b42eb4f6e74096b9c9e37713cf10bf30168fb4a739f3fa8f898a3a"}, - {file = "virtualenv-20.14.1.tar.gz", hash = "sha256:ef589a79795589aada0c1c5b319486797c03b67ac3984c48c669c0e4f50df3a5"}, -] -webencodings = [ - {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, - {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, -] -zipp = [ - {file = "zipp-3.8.0-py3-none-any.whl", hash = "sha256:c4f6e5bbf48e74f7a38e7cc5b0480ff42b0ae5178957d564d18932525d5cf099"}, - {file = "zipp-3.8.0.tar.gz", hash = "sha256:56bf8aadb83c24db6c4b577e13de374ccfb67da2078beba1d037c17980bf43ad"}, -] +lock-version = "2.0" +python-versions = "^3.10" +content-hash = "ff9fc901b270620b6ca62d914aeaef60996163f92b8e0b263d685af8f0d9b2a1" diff --git a/tests/assets/no_vulnerabilities/pyproject.toml b/tests/assets/no_vulnerabilities/pyproject.toml index a9c2976..6f92f03 100644 --- a/tests/assets/no_vulnerabilities/pyproject.toml +++ b/tests/assets/no_vulnerabilities/pyproject.toml @@ -1,14 +1,14 @@ [tool.poetry] -name = "no-vulnerabilities" +name = "poetry-audit-demo" version = "0.1.0" description = "" authors = ["opeco17 <opeco17@gmail.com>"] readme = "README.md" -packages = [{include = "no_vulnerabilities"}] [tool.poetry.dependencies] -python = "^3.9" -poetry-opeco17-test-plugin = "^1.0.0" +python = "^3.10" +opeco17-dummy-package = "^0.1.0" + [build-system] requires = ["poetry-core"] diff --git a/tests/test_main.py b/tests/test_main.py index a0764cc..afb948a 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -5,9 +5,12 @@ from subprocess import CompletedProcess from typing import List +from poetry_audit_plugin.constants import * + # At least there're following vulnerabilities in these packages. DEV_VULNERABILITY_PACKAGE = "ansible-runner" DEV_VULNERABILITY_CODE1 = "PVE-2021-36995" +DEV_VULNERABILITY_CODE2 = "CVE-2021-4041" MAIN_VULNERABILITY_PACKAGE = "ansible-tower-cli" MAIN_VULNERABILITY_CODE1 = "CVE-2020-1735" MAIN_VULNERABILITY_CODE2 = "CVE-2020-1738" @@ -20,13 +23,15 @@ def copy_assets(source_name: str, testing_dir: Path) -> None: shutil.copytree(package_path, testing_dir) -def run_audit(testing_dir: Path, args: List[str] = []) -> CompletedProcess: +def run_audit(testing_dir: Path, use_cache: bool, *args: str) -> CompletedProcess: + commands = [ + "poetry", + "audit", + ] + list(args) + if use_cache: + commands.append("--cache-sec=60") result = subprocess.run( - [ - "poetry", - "audit", - ] - + args, + commands, cwd=testing_dir, stdout=subprocess.PIPE, stderr=subprocess.PIPE, @@ -38,164 +43,169 @@ def run_audit(testing_dir: Path, args: List[str] = []) -> CompletedProcess: def test_no_vulnerabilities_basic_report(tmp_path: Path) -> None: testing_dir = tmp_path / "testing_package" copy_assets("no_vulnerabilities", testing_dir) - result = run_audit(testing_dir=testing_dir) + result = run_audit(testing_dir, True) assert "poetry audit report" in result.stdout - assert "Vulnerabilities not found" in result.stdout - assert result.returncode == 0 + assert "No vulnerabilities found" in result.stdout + assert result.returncode == EXIT_CODE_OK def test_vulnerabilities_in_main_basic_report(tmp_path: Path) -> None: testing_dir = tmp_path / "testing_package" copy_assets("vulnerabilities_in_main", testing_dir) - result = run_audit(testing_dir=testing_dir) + result = run_audit(testing_dir, True) assert "poetry audit report" in result.stdout assert MAIN_VULNERABILITY_PACKAGE in result.stdout assert MAIN_VULNERABILITY_CODE1 in result.stdout + assert MAIN_VULNERABILITY_CODE2 in result.stdout assert "vulnerabilities found" in result.stdout - assert result.returncode == 1 + assert "No vulnerabilities found" not in result.stdout + assert result.returncode == EXIT_CODE_VULNERABILITY_FOUND def test_vulnerabilities_in_dev_basic_report(tmp_path: Path) -> None: testing_dir = tmp_path / "testing_package" copy_assets("vulnerabilities_in_dev", testing_dir) - result = run_audit(testing_dir=testing_dir) + result = run_audit(testing_dir, True) assert "poetry audit report" in result.stdout assert DEV_VULNERABILITY_PACKAGE in result.stdout assert DEV_VULNERABILITY_CODE1 in result.stdout + assert DEV_VULNERABILITY_CODE2 in result.stdout assert "vulnerabilities found" in result.stdout - assert result.returncode == 1 + assert "No vulnerabilities found" not in result.stdout + assert result.returncode == EXIT_CODE_VULNERABILITY_FOUND def test_vulnerabilities_in_main_dev_basic_report(tmp_path: Path) -> None: testing_dir = tmp_path / "testing_package" copy_assets("vulnerabilities_in_main_dev", testing_dir) - result = run_audit(testing_dir=testing_dir) + result = run_audit(testing_dir, True) assert "poetry audit report" in result.stdout assert DEV_VULNERABILITY_PACKAGE in result.stdout assert MAIN_VULNERABILITY_PACKAGE in result.stdout assert DEV_VULNERABILITY_CODE1 in result.stdout + assert DEV_VULNERABILITY_CODE2 in result.stdout assert MAIN_VULNERABILITY_CODE1 in result.stdout + assert MAIN_VULNERABILITY_CODE2 in result.stdout assert "vulnerabilities found" in result.stdout - assert result.returncode == 1 + assert "No vulnerabilities found" not in result.stdout + assert result.returncode == EXIT_CODE_VULNERABILITY_FOUND def test_no_vulnerabilities_json_report(tmp_path: Path) -> None: testing_dir = tmp_path / "testing_package" copy_assets("no_vulnerabilities", testing_dir) - result = run_audit(testing_dir=testing_dir, args=["--json"]) + result = run_audit(testing_dir, True, "--json") result_dict = json.loads(result.stdout) vulnerabilitie_names = [vulnerability["name"] for vulnerability in result_dict["vulnerabilities"]] + assert "poetry audit report" not in result.stdout + assert "metadata" in result_dict.keys() assert len(vulnerabilitie_names) == 0 - assert result.returncode == 0 + assert result.returncode == EXIT_CODE_OK def test_vulnerabilities_in_main_json_report(tmp_path: Path) -> None: testing_dir = tmp_path / "testing_package" copy_assets("vulnerabilities_in_main", testing_dir) - result = run_audit(testing_dir=testing_dir, args=["--json"]) + result = run_audit(testing_dir, True, "--json") result_dict = json.loads(result.stdout) vulnerabilitie_names = [vulnerability["name"] for vulnerability in result_dict["vulnerabilities"]] + assert "poetry audit report" not in result.stdout + assert "metadata" in result_dict.keys() assert MAIN_VULNERABILITY_PACKAGE in vulnerabilitie_names assert MAIN_VULNERABILITY_CODE1 in result.stdout - assert result.returncode == 1 + assert MAIN_VULNERABILITY_CODE2 in result.stdout + assert result.returncode == EXIT_CODE_VULNERABILITY_FOUND def test_vulnerabilities_in_dev_json_report(tmp_path: Path) -> None: testing_dir = tmp_path / "testing_package" copy_assets("vulnerabilities_in_dev", testing_dir) - result = run_audit(testing_dir=testing_dir, args=["--json"]) + result = run_audit(testing_dir, True, "--json") result_dict = json.loads(result.stdout) vulnerabilitie_names = [vulnerability["name"] for vulnerability in result_dict["vulnerabilities"]] + assert "poetry audit report" not in result.stdout + assert "metadata" in result_dict.keys() assert DEV_VULNERABILITY_PACKAGE in vulnerabilitie_names assert DEV_VULNERABILITY_CODE1 in result.stdout - assert result.returncode == 1 + assert DEV_VULNERABILITY_CODE2 in result.stdout + assert result.returncode == EXIT_CODE_VULNERABILITY_FOUND def test_vulnerabilities_in_main_dev_json_report(tmp_path: Path) -> None: testing_dir = tmp_path / "testing_package" copy_assets("vulnerabilities_in_main_dev", testing_dir) - result = run_audit(testing_dir=testing_dir, args=["--json"]) + result = run_audit(testing_dir, True, "--json") result_dict = json.loads(result.stdout) vulnerabilitie_names = [vulnerability["name"] for vulnerability in result_dict["vulnerabilities"]] + assert "poetry audit report" not in result.stdout + assert "metadata" in result_dict.keys() assert DEV_VULNERABILITY_PACKAGE in vulnerabilitie_names assert MAIN_VULNERABILITY_PACKAGE in vulnerabilitie_names assert DEV_VULNERABILITY_CODE1 in result.stdout + assert DEV_VULNERABILITY_CODE2 in result.stdout assert MAIN_VULNERABILITY_CODE1 in result.stdout - assert result.returncode == 1 + assert MAIN_VULNERABILITY_CODE2 in result.stdout + assert result.returncode == EXIT_CODE_VULNERABILITY_FOUND def test_vulnerabilities_code_in_main_basic_report_with_ignoring_codes(tmp_path: Path) -> None: testing_dir = tmp_path / "testing_package" copy_assets("vulnerabilities_in_main", testing_dir) - result = run_audit( - testing_dir=testing_dir, args=[f"--ignore-code={MAIN_VULNERABILITY_CODE1},{MAIN_VULNERABILITY_CODE2}"] - ) + result = run_audit(testing_dir, True, f"--ignore-code={MAIN_VULNERABILITY_CODE1}") assert "poetry audit report" in result.stdout assert MAIN_VULNERABILITY_PACKAGE in result.stdout assert "vulnerabilities found in" in result.stdout assert "vulnerabilities found but ignored" in result.stdout assert MAIN_VULNERABILITY_CODE1 not in result.stdout - assert MAIN_VULNERABILITY_CODE2 not in result.stdout - assert result.returncode == 1 + assert MAIN_VULNERABILITY_CODE2 in result.stdout + assert result.returncode == EXIT_CODE_VULNERABILITY_FOUND def test_vulnerabilities_in_main_dev_basic_report_with_ignoring_codes(tmp_path: Path) -> None: testing_dir = tmp_path / "testing_package" copy_assets("vulnerabilities_in_main_dev", testing_dir) - result = run_audit( - testing_dir=testing_dir, args=[f"--ignore-code={MAIN_VULNERABILITY_CODE1},{MAIN_VULNERABILITY_CODE2}"] - ) + result = run_audit(testing_dir, True, f"--ignore-code={MAIN_VULNERABILITY_CODE1},{DEV_VULNERABILITY_CODE1}") assert "poetry audit report" in result.stdout assert DEV_VULNERABILITY_PACKAGE in result.stdout assert MAIN_VULNERABILITY_PACKAGE in result.stdout assert MAIN_VULNERABILITY_CODE1 not in result.stdout - assert MAIN_VULNERABILITY_CODE2 not in result.stdout + assert MAIN_VULNERABILITY_CODE2 in result.stdout + assert DEV_VULNERABILITY_CODE1 not in result.stdout + assert DEV_VULNERABILITY_CODE2 in result.stdout assert "vulnerabilities found in" in result.stdout assert "vulnerabilities found but ignored" in result.stdout - assert result.returncode == 1 + assert result.returncode == EXIT_CODE_VULNERABILITY_FOUND def test_vulnerabilities_in_dev_basic_report_with_ignoring_codes(tmp_path: Path) -> None: testing_dir = tmp_path / "testing_package" copy_assets("vulnerabilities_in_dev", testing_dir) - result = run_audit( - testing_dir=testing_dir, args=["--ignore-code={MAIN_VULNERABILITY_CODE1},{MAIN_VULNERABILITY_CODE2}"] - ) + result = run_audit(testing_dir, True, f"--ignore-code={DEV_VULNERABILITY_CODE1}") assert "poetry audit report" in result.stdout assert DEV_VULNERABILITY_PACKAGE in result.stdout + assert DEV_VULNERABILITY_CODE1 not in result.stdout + assert DEV_VULNERABILITY_CODE2 in result.stdout assert "vulnerabilities found in" in result.stdout assert "vulnerabilities found but ignored" in result.stdout - assert result.returncode == 1 - - -def test_vulnerabilities_in_main_dev_basic_report_with_ignoring_packages(tmp_path: Path) -> None: - testing_dir = tmp_path / "testing_package" - copy_assets("vulnerabilities_in_main_dev", testing_dir) - result = run_audit(testing_dir=testing_dir, args=[f"--ignore-package={MAIN_VULNERABILITY_PACKAGE}"]) - - assert "poetry audit report" in result.stdout - assert "vulnerabilities found but ignored" in result.stdout - assert DEV_VULNERABILITY_PACKAGE in result.stdout - assert MAIN_VULNERABILITY_PACKAGE not in result.stdout - assert result.returncode == 1 + assert result.returncode == EXIT_CODE_VULNERABILITY_FOUND def test_vulnerabilities_in_main_dev_json_report_with_ignoring_codes(tmp_path: Path) -> None: testing_dir = tmp_path / "testing_package" copy_assets("vulnerabilities_in_main_dev", testing_dir) result = run_audit( - testing_dir=testing_dir, args=["--json", f"--ignore-code={MAIN_VULNERABILITY_CODE1},{MAIN_VULNERABILITY_CODE2}"] + testing_dir, True, "--json", f"--ignore-code={MAIN_VULNERABILITY_CODE1},{DEV_VULNERABILITY_CODE1}" ) result_dict = json.loads(result.stdout) vulnerability_names: List[str] = [] @@ -205,46 +215,165 @@ def test_vulnerabilities_in_main_dev_json_report_with_ignoring_codes(tmp_path: P for detail in vuln["vulns"]: vulnerability_codes.append(detail["cve"]) - assert DEV_VULNERABILITY_PACKAGE in vulnerability_names + assert "poetry audit report" not in result.stdout assert MAIN_VULNERABILITY_PACKAGE in vulnerability_names + assert DEV_VULNERABILITY_PACKAGE in vulnerability_names assert MAIN_VULNERABILITY_CODE1 not in vulnerability_codes - assert MAIN_VULNERABILITY_CODE2 not in vulnerability_codes - assert result.returncode == 1 + assert MAIN_VULNERABILITY_CODE2 in vulnerability_codes + assert DEV_VULNERABILITY_CODE1 not in result.stdout + assert DEV_VULNERABILITY_CODE2 in result.stdout + assert result.returncode == EXIT_CODE_VULNERABILITY_FOUND -def test_no_vulnerabilities_in_main_basic_report_with_ignoring_packages(tmp_path: Path) -> None: +def test_vulnerabilities_in_main_dev_basic_report_with_ignoring_main_packages(tmp_path: Path) -> None: testing_dir = tmp_path / "testing_package" - copy_assets("vulnerabilities_in_main", testing_dir) - result = run_audit(testing_dir=testing_dir, args=[f"--ignore-package={MAIN_VULNERABILITY_PACKAGE}"]) + copy_assets("vulnerabilities_in_main_dev", testing_dir) + result = run_audit(testing_dir, True, f"--ignore-package={MAIN_VULNERABILITY_PACKAGE}") assert "poetry audit report" in result.stdout assert "vulnerabilities found but ignored" in result.stdout assert MAIN_VULNERABILITY_PACKAGE not in result.stdout - assert result.returncode == 0 + assert DEV_VULNERABILITY_PACKAGE in result.stdout + assert result.returncode == EXIT_CODE_VULNERABILITY_FOUND + + +def test_vulnerabilities_in_main_dev_basic_report_with_ignoring_dev_packages(tmp_path: Path) -> None: + testing_dir = tmp_path / "testing_package" + copy_assets("vulnerabilities_in_main_dev", testing_dir) + result = run_audit(testing_dir, True, f"--ignore-package={DEV_VULNERABILITY_PACKAGE}") + + assert "poetry audit report" in result.stdout + assert "vulnerabilities found but ignored" in result.stdout + assert MAIN_VULNERABILITY_PACKAGE in result.stdout + assert DEV_VULNERABILITY_PACKAGE not in result.stdout + assert result.returncode == EXIT_CODE_VULNERABILITY_FOUND -def test_vulnerabilities_in_main_dev_json_report_with_ignoring_packages(tmp_path: Path) -> None: +def test_vulnerabilities_in_main_dev_json_report_with_ignoring_main_packages(tmp_path: Path) -> None: testing_dir = tmp_path / "testing_package" copy_assets("vulnerabilities_in_main_dev", testing_dir) - result = run_audit(testing_dir=testing_dir, args=["--json", f"--ignore-package={MAIN_VULNERABILITY_PACKAGE}"]) + result = run_audit(testing_dir, True, "--json", f"--ignore-package={MAIN_VULNERABILITY_PACKAGE}") result_dict = json.loads(result.stdout) vulnerabilitie_names = [] for vuln in result_dict["vulnerabilities"]: vulnerabilitie_names.append(vuln["name"]) - assert DEV_VULNERABILITY_PACKAGE in vulnerabilitie_names + assert "poetry audit report" not in result.stdout assert MAIN_VULNERABILITY_PACKAGE not in vulnerabilitie_names - assert result.returncode == 1 + assert DEV_VULNERABILITY_PACKAGE in vulnerabilitie_names + assert result.returncode == EXIT_CODE_VULNERABILITY_FOUND -def test_no_vulnerabilities_in_main_json_report_with_ignoring_packages(tmp_path: Path) -> None: +def test_vulnerabilities_in_main_dev_json_report_with_ignoring_dev_packages(tmp_path: Path) -> None: testing_dir = tmp_path / "testing_package" - copy_assets("vulnerabilities_in_main", testing_dir) - result = run_audit(testing_dir=testing_dir, args=["--json", f"--ignore-package={MAIN_VULNERABILITY_PACKAGE}"]) + copy_assets("vulnerabilities_in_main_dev", testing_dir) + result = run_audit(testing_dir, True, "--json", f"--ignore-package={DEV_VULNERABILITY_PACKAGE}") result_dict = json.loads(result.stdout) vulnerabilitie_names = [] for vuln in result_dict["vulnerabilities"]: vulnerabilitie_names.append(vuln["name"]) - assert MAIN_VULNERABILITY_PACKAGE not in vulnerabilitie_names - assert result.returncode == 0 + assert "poetry audit report" not in result.stdout + assert MAIN_VULNERABILITY_PACKAGE in vulnerabilitie_names + assert DEV_VULNERABILITY_PACKAGE not in vulnerabilitie_names + assert result.returncode == EXIT_CODE_VULNERABILITY_FOUND + + +def test_no_vulnerabilities_basic_report_with_valid_proxy_config(tmp_path: Path) -> None: + testing_dir = tmp_path / "testing_package" + copy_assets("no_vulnerabilities", testing_dir) + result = run_audit(testing_dir, False, "--proxy-protocol=http", "--proxy-host=localhost", "--proxy-port=3128") + + assert "poetry audit report" in result.stdout + assert result.returncode == EXIT_CODE_OK + + +def test_no_vulnerabilities_basic_report_with_invalid_string_proxy_port(tmp_path: Path) -> None: + testing_dir = tmp_path / "testing_package" + copy_assets("no_vulnerabilities", testing_dir) + result = run_audit(testing_dir, True, "--proxy-host=localhost", "--proxy-port=string") + + assert "poetry audit report" in result.stdout + assert "Command line option(s) are invalid" in result.stderr + assert result.returncode == EXIT_CODE_OPTION_INVALID + + +def test_no_vulnerabilities_basic_report_with_invalid_empty_proxy_port(tmp_path: Path) -> None: + testing_dir = tmp_path / "testing_package" + copy_assets("no_vulnerabilities", testing_dir) + result = run_audit(testing_dir, True, "--proxy-host=localhost", "--proxy-port=''") + + assert "poetry audit report" in result.stdout + assert "Command line option(s) are invalid" in result.stderr + assert result.returncode == EXIT_CODE_OPTION_INVALID + + +def test_no_vulnerabilities_basic_report_with_invalid_string_proxy_protocol(tmp_path: Path) -> None: + testing_dir = tmp_path / "testing_package" + copy_assets("no_vulnerabilities", testing_dir) + result = run_audit(testing_dir, True, "--proxy-host=localhost", "--proxy-protocol='tcp'") + + assert "poetry audit report" in result.stdout + assert "Command line option(s) are invalid" in result.stderr + assert result.returncode == EXIT_CODE_OPTION_INVALID + + +def test_no_vulnerabilities_basic_report_with_invalid_empty_proxy_protocol(tmp_path: Path) -> None: + testing_dir = tmp_path / "testing_package" + copy_assets("no_vulnerabilities", testing_dir) + result = run_audit(testing_dir, True, "--proxy-host=localhost", "--proxy-protocol=''") + + assert "poetry audit report" in result.stdout + assert "Command line option(s) are invalid" in result.stderr + assert result.returncode == EXIT_CODE_OPTION_INVALID + + +def test_no_vulnerabilities_json_report_with_valid_proxy_config(tmp_path: Path) -> None: + testing_dir = tmp_path / "testing_package" + copy_assets("no_vulnerabilities", testing_dir) + result = run_audit( + testing_dir, False, "--json", "--proxy-protocol=http", "--proxy-host=localhost", "--proxy-port=3128" + ) + + assert "poetry audit report" not in result.stdout + assert result.returncode == EXIT_CODE_OK + + +def test_no_vulnerabilities_json_report_with_invalid_string_proxy_port(tmp_path: Path) -> None: + testing_dir = tmp_path / "testing_package" + copy_assets("no_vulnerabilities", testing_dir) + result = run_audit(testing_dir, True, "--json", "--proxy-host=localhost", "--proxy-port=string") + + assert "poetry audit report" not in result.stdout + assert "Command line option(s) are invalid" in result.stderr + assert result.returncode == EXIT_CODE_OPTION_INVALID + + +def test_no_vulnerabilities_json_report_with_invalid_empty_proxy_port(tmp_path: Path) -> None: + testing_dir = tmp_path / "testing_package" + copy_assets("no_vulnerabilities", testing_dir) + result = run_audit(testing_dir, True, "--json", "--proxy-host=localhost", "--proxy-port=''") + + assert "poetry audit report" not in result.stdout + assert "Command line option(s) are invalid" in result.stderr + assert result.returncode == EXIT_CODE_OPTION_INVALID + + +def test_no_vulnerabilities_json_report_with_invalid_string_proxy_protocol(tmp_path: Path) -> None: + testing_dir = tmp_path / "testing_package" + copy_assets("no_vulnerabilities", testing_dir) + result = run_audit(testing_dir, True, "--json", "--proxy-host=localhost", "--proxy-protocol='tcp'") + + assert "poetry audit report" not in result.stdout + assert "Command line option(s) are invalid" in result.stderr + assert result.returncode == EXIT_CODE_OPTION_INVALID + + +def test_no_vulnerabilities_json_report_with_invalid_empty_proxy_protocol(tmp_path: Path) -> None: + testing_dir = tmp_path / "testing_package" + copy_assets("no_vulnerabilities", testing_dir) + result = run_audit(testing_dir, True, "--json", "--proxy-host=localhost", "--proxy-protocol=''") + + assert "poetry audit report" not in result.stdout + assert "Command line option(s) are invalid" in result.stderr + assert result.returncode == EXIT_CODE_OPTION_INVALID