ossaudit-ng 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. ossaudit_ng-1.0.0/.coveragerc +7 -0
  2. ossaudit_ng-1.0.0/.isort.cfg +3 -0
  3. ossaudit_ng-1.0.0/.pylintrc +65 -0
  4. ossaudit_ng-1.0.0/.style.yapf +8 -0
  5. ossaudit_ng-1.0.0/LICENSE +26 -0
  6. ossaudit_ng-1.0.0/MANIFEST.in +4 -0
  7. ossaudit_ng-1.0.0/Makefile +52 -0
  8. ossaudit_ng-1.0.0/PKG-INFO +50 -0
  9. ossaudit_ng-1.0.0/README.md +118 -0
  10. ossaudit_ng-1.0.0/mypy.ini +21 -0
  11. ossaudit_ng-1.0.0/ossaudit/__init__.py +68 -0
  12. ossaudit_ng-1.0.0/ossaudit/__main__.py +14 -0
  13. ossaudit_ng-1.0.0/ossaudit/audit.py +142 -0
  14. ossaudit_ng-1.0.0/ossaudit/cache.py +58 -0
  15. ossaudit_ng-1.0.0/ossaudit/cli.py +176 -0
  16. ossaudit_ng-1.0.0/ossaudit/const.py +17 -0
  17. ossaudit_ng-1.0.0/ossaudit/option.py +74 -0
  18. ossaudit_ng-1.0.0/ossaudit/packages.py +157 -0
  19. ossaudit_ng-1.0.0/ossaudit_ng.egg-info/PKG-INFO +50 -0
  20. ossaudit_ng-1.0.0/ossaudit_ng.egg-info/SOURCES.txt +52 -0
  21. ossaudit_ng-1.0.0/ossaudit_ng.egg-info/dependency_links.txt +1 -0
  22. ossaudit_ng-1.0.0/ossaudit_ng.egg-info/entry_points.txt +2 -0
  23. ossaudit_ng-1.0.0/ossaudit_ng.egg-info/requires.txt +5 -0
  24. ossaudit_ng-1.0.0/ossaudit_ng.egg-info/top_level.txt +1 -0
  25. ossaudit_ng-1.0.0/requirements/README.md +20 -0
  26. ossaudit_ng-1.0.0/requirements/requirements-dev-1.txt +1 -0
  27. ossaudit_ng-1.0.0/requirements/requirements-dev-2.txt +1 -0
  28. ossaudit_ng-1.0.0/requirements/requirements-dev-3.txt +15 -0
  29. ossaudit_ng-1.0.0/requirements/requirements.txt +10 -0
  30. ossaudit_ng-1.0.0/setup.cfg +7 -0
  31. ossaudit_ng-1.0.0/setup.py +74 -0
  32. ossaudit_ng-1.0.0/tests/__init__.py +3 -0
  33. ossaudit_ng-1.0.0/tests/__pycache__/__init__.cpython-313.pyc +0 -0
  34. ossaudit_ng-1.0.0/tests/__pycache__/helpers.cpython-313.pyc +0 -0
  35. ossaudit_ng-1.0.0/tests/__pycache__/test_audit.cpython-313-pytest-9.0.3.pyc +0 -0
  36. ossaudit_ng-1.0.0/tests/__pycache__/test_audit.cpython-313.pyc +0 -0
  37. ossaudit_ng-1.0.0/tests/__pycache__/test_cache.cpython-313-pytest-9.0.3.pyc +0 -0
  38. ossaudit_ng-1.0.0/tests/__pycache__/test_cache.cpython-313.pyc +0 -0
  39. ossaudit_ng-1.0.0/tests/__pycache__/test_cli.cpython-313-pytest-9.0.3.pyc +0 -0
  40. ossaudit_ng-1.0.0/tests/__pycache__/test_cli.cpython-313.pyc +0 -0
  41. ossaudit_ng-1.0.0/tests/__pycache__/test_option.cpython-313-pytest-9.0.3.pyc +0 -0
  42. ossaudit_ng-1.0.0/tests/__pycache__/test_option.cpython-313.pyc +0 -0
  43. ossaudit_ng-1.0.0/tests/__pycache__/test_packages.cpython-313-pytest-9.0.3.pyc +0 -0
  44. ossaudit_ng-1.0.0/tests/__pycache__/test_packages.cpython-313.pyc +0 -0
  45. ossaudit_ng-1.0.0/tests/__pycache__/test_scan.cpython-313-pytest-9.0.3.pyc +0 -0
  46. ossaudit_ng-1.0.0/tests/data/vulns01.json +49 -0
  47. ossaudit_ng-1.0.0/tests/helpers.py +30 -0
  48. ossaudit_ng-1.0.0/tests/test_audit.py +249 -0
  49. ossaudit_ng-1.0.0/tests/test_cache.py +170 -0
  50. ossaudit_ng-1.0.0/tests/test_cli.py +201 -0
  51. ossaudit_ng-1.0.0/tests/test_option.py +186 -0
  52. ossaudit_ng-1.0.0/tests/test_packages.py +216 -0
  53. ossaudit_ng-1.0.0/tests/test_scan.py +89 -0
@@ -0,0 +1,7 @@
1
+ [run]
2
+ branch = true
3
+ source = ossaudit
4
+ omit = ossaudit/__main__.py
5
+
6
+ [report]
7
+ fail_under = 100
@@ -0,0 +1,3 @@
1
+ [settings]
2
+ include_trailing_comma = true
3
+ multi_line_output = 3
@@ -0,0 +1,65 @@
1
+ [MASTER]
2
+ persistent=no
3
+
4
+ ignore=.git
5
+
6
+ load-plugins=
7
+ pylint.extensions.bad_builtin,
8
+ pylint.extensions.check_elif,
9
+ pylint.extensions.docparams,
10
+ pylint.extensions.docstyle,
11
+ pylint.extensions.mccabe,
12
+ pylint.extensions.overlapping_exceptions,
13
+ pylint.extensions.redefined_variable_type,
14
+ pylint_quotes,
15
+
16
+ accept-no-param-doc=no
17
+ accept-no-raise-doc=no
18
+ accept-no-yields-doc=no
19
+
20
+ extension-pkg-whitelist=
21
+ unsafe-load-any-extension=no
22
+
23
+ [MESSAGES CONTROL]
24
+ confidence=
25
+ enable=all
26
+ disable=
27
+ docstring-first-line-empty,
28
+ invalid-name,
29
+ locally-disabled,
30
+ locally-enabled,
31
+ missing-docstring,
32
+ missing-param-doc,
33
+ missing-raises-doc,
34
+ missing-return-type-doc,
35
+ missing-type-doc,
36
+ missing-yield-doc,
37
+ missing-yield-type-doc,
38
+ suppressed-message,
39
+ too-few-public-methods,
40
+ too-many-ancestors,
41
+ too-many-arguments,
42
+ use-symbolic-message-instead,
43
+
44
+ [VARIABLES]
45
+ init-import=yes
46
+
47
+ [BASIC]
48
+ good-names=f,i,j,k,m,db,ex,_
49
+ argument-naming-style=snake_case
50
+ attr-naming-style=snake_case
51
+ class-naming-style=PascalCase
52
+ const-naming-style=UPPER_CASE
53
+ function-naming-style=snake_case
54
+ method-naming-style=snake_case
55
+ module-naming-style=snake_case
56
+ variable-naming-style=snake_case
57
+
58
+ [FORMAT]
59
+ expected-line-ending-format=LF
60
+ max-line-length=79
61
+
62
+ [PYLINT-QUOTES]
63
+ string-quote=double
64
+ triple-quote=double
65
+ docstring-quote=double
@@ -0,0 +1,8 @@
1
+ [style]
2
+ based_on_style = pep8
3
+ blank_line_before_module_docstring = true
4
+ coalesce_brackets = true
5
+ dedent_closing_brackets = true
6
+ join_multiple_lines = false
7
+ split_before_first_argument = true
8
+ split_complex_comprehension = true
@@ -0,0 +1,26 @@
1
+ Copyright (c) 2019, Hans Jerry Illikainen <hji@dyntopia.com>
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions
6
+ are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright
9
+ notice, this list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright
12
+ notice, this list of conditions and the following disclaimer in
13
+ the documentation and/or other materials provided with the
14
+ distribution.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20
+ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,4 @@
1
+ include LICENSE README.md Makefile
2
+ include mypy.ini .coveragerc .isort.cfg .pylintrc .style.yapf
3
+ recursive-include requirements *
4
+ recursive-include tests *
@@ -0,0 +1,52 @@
1
+ .PHONY: all dist install install-dev test qa
2
+
3
+ all:
4
+
5
+ install:
6
+ pip3 install -r requirements/requirements.txt --no-binary :all:
7
+ ./setup.py install
8
+
9
+ install-dev:
10
+ pip3 install -r requirements/requirements.txt --no-binary :all:
11
+ pip3 install -r requirements/requirements-dev-1.txt --no-binary :all:
12
+ pip3 install -r requirements/requirements-dev-2.txt --no-binary :all:
13
+ pip3 install -r requirements/requirements-dev-3.txt --no-binary :all:
14
+ ./setup.py develop
15
+
16
+ test:
17
+ python3 -m unittest -q
18
+
19
+ qa:
20
+ coverage run -m unittest -q
21
+ coverage report -m
22
+ isort --check-only --diff --recursive .
23
+ mypy .
24
+ pycodestyle .
25
+ pyflakes .
26
+ pylint --output-format parseable setup.py ossaudit tests
27
+ yapf --diff --recursive .
28
+ ossaudit \
29
+ --file requirements/requirements.txt \
30
+ --file requirements/requirements-dev-1.txt \
31
+ --file requirements/requirements-dev-2.txt \
32
+ --file requirements/requirements-dev-3.txt
33
+
34
+ dist:
35
+ rm -rf dist tmp
36
+
37
+ tag="$$(git tag --sort=-creatordate |head -n 1)" && \
38
+ git verify-tag "$$tag" && \
39
+ git clone --shared --branch "$$tag" . tmp
40
+
41
+ cd tmp && \
42
+ ./setup.py sdist && \
43
+ gpg --detach-sign --armor "$$(ls dist/*.tar.gz)"
44
+
45
+ mv tmp/dist .
46
+ rm -rf tmp
47
+ gpg --verify dist/*.asc
48
+
49
+ upload:
50
+ git push --tags
51
+ tag="$$(git tag --sort=-creatordate |head -n 1 |cut -dv -f2)" && \
52
+ twine upload dist/*$$tag.tar.gz
@@ -0,0 +1,50 @@
1
+ Metadata-Version: 2.4
2
+ Name: ossaudit-ng
3
+ Version: 1.0.0
4
+ Summary: Audit python packages for known vulnerabilities using Sonatype OSS Index v3 API
5
+ Home-page: https://github.com/stuartz/ossaudit
6
+ Author: Stuart Zurcher
7
+ Author-email: stuartz.ccrx@gmail.com
8
+ Maintainer: Stuart Zurcher
9
+ Maintainer-email: stuartz.ccrx@gmail.com
10
+ License: BSD-2-Clause
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Intended Audience :: Information Technology
14
+ Classifier: Intended Audience :: System Administrators
15
+ Classifier: License :: OSI Approved
16
+ Classifier: License :: OSI Approved :: BSD License
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.8
20
+ Classifier: Programming Language :: Python :: 3.9
21
+ Classifier: Programming Language :: Python :: 3.10
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Classifier: Programming Language :: Python :: 3.13
25
+ Classifier: Topic :: Security
26
+ Classifier: Topic :: Software Development
27
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
28
+ Classifier: Topic :: Software Development :: Testing
29
+ Classifier: Topic :: Utilities
30
+ Requires-Python: >=3.5
31
+ License-File: LICENSE
32
+ Requires-Dist: appdirs
33
+ Requires-Dist: click
34
+ Requires-Dist: dparse
35
+ Requires-Dist: requests
36
+ Requires-Dist: texttable
37
+ Dynamic: author
38
+ Dynamic: author-email
39
+ Dynamic: classifier
40
+ Dynamic: description
41
+ Dynamic: home-page
42
+ Dynamic: license
43
+ Dynamic: license-file
44
+ Dynamic: maintainer
45
+ Dynamic: maintainer-email
46
+ Dynamic: requires-dist
47
+ Dynamic: requires-python
48
+ Dynamic: summary
49
+
50
+ Fork of ossaudit by Hans Jerry Illikainen (https://github.com/illikainen/ossaudit). Includes some portions of PRs from sseide. See https://github.com/stuartz/ossaudit
@@ -0,0 +1,118 @@
1
+
2
+ # ossaudit
3
+ A fork of https://github.com/illikainen/ossaudit.git
4
+ This fork includes JSON output, config file support, Bearer token auth, and HTTP/HTTPS proxy support.
5
+
6
+ ## About
7
+
8
+ `ossaudit` uses [Sonatype OSS Index][1] to audit Python packages for
9
+ known vulnerabilities.
10
+
11
+ It can check installed packages and/or packages specified in dependency
12
+ files. The following formats are supported with [dparse][2]:
13
+
14
+ - PIP requirement files
15
+ - Pipfile
16
+ - Pipfile.lock
17
+ - tox.ini
18
+ - conda.yml
19
+
20
+
21
+ ## Installation
22
+
23
+ ### Normal
24
+
25
+
26
+ ```sh
27
+ ./setup.py develop
28
+ ```
29
+
30
+
31
+ ## Usage
32
+
33
+ ```sh
34
+ $ ossaudit --help
35
+ Usage: ossaudit [OPTIONS]
36
+
37
+ Options:
38
+ -c, --config TEXT Configuration file.
39
+ -i, --installed Audit installed packages.
40
+ -f, --file FILENAME Audit packages in file (can be specified multiple
41
+ times).
42
+ --token TEXT Token for authentication.
43
+ --column TEXT Column to show (can be specified multiple times or passed a comma separated list).
44
+ [default: name, version, title] additional optons -- id, cve, cvss_score, description
45
+ --ignore-id TEXT Ignore a vulnerability by Sonatype ID or CVE (can be
46
+ specified multiple times).
47
+ --ignore-cache Temporarily ignore existing cache.
48
+ --reset-cache Remove existing cache.
49
+ --json Output vulnerabilities as json list
50
+ --json-full Output all dependencies found and their vulnerabilities
51
+ as json list (columns given are ignored)
52
+ --http-proxy TEXT HTTP proxy URL.
53
+ --https-proxy TEXT HTTPS proxy URL.
54
+ --help Show this message and exit.
55
+ ```
56
+
57
+ ## As import
58
+ ```python
59
+ from ossaudit import scan
60
+ list_of_vulnerabilites = scan(installed=True) # Pass options as args
61
+ for v in list_of_vulnerabilites:
62
+ # v is type <class 'ossaudit.audit.Vulnerability'>
63
+ print(v.name, v.version, v.title, v.cvss_score)
64
+ ```
65
+
66
+ ## Configuration
67
+
68
+ [Appdirs][3] is used to determine storage paths. This means that the
69
+ location of the configuration file is platform-specific:
70
+
71
+ - `*nix`: `~/.config/ossaudit/config.ini`
72
+ - `macOS`: `~/Library/Preferences/ossaudit/config.ini`
73
+ - `Windows`: `C:\Users\<username>\AppData\Local\ossaudit\ossaudit\config.ini`
74
+
75
+ It can be overridden with the `--config` command-line argument and with
76
+ the `OSSAUDIT_CONFIG` environment variable.
77
+
78
+ Example configuration:
79
+
80
+ ```ini
81
+ [ossaudit]
82
+ # Optional: OSS Index API token (Bearer token auth).
83
+ # A free account and token can be created at https://ossindex.sonatype.org/
84
+ #token = string
85
+
86
+ # Optional: comma-separated list of columns to show.
87
+ # Default: name, version, title
88
+ # Supported: id, name, version, cve, cvss_score, title, description
89
+ #columns = name,version,title,cvss_score
90
+
91
+ # Optional: comma-separated list of vulnerability IDs (Sonatype ID or CVE) to ignore.
92
+ #ignore_ids = x,y,z
93
+
94
+ # Optional: Ignore cache
95
+ #ignore_cache = True
96
+
97
+ # Optional: Reset the cache
98
+ #reset_cache = True
99
+
100
+ # Optional: Output format to json
101
+ #json = True
102
+
103
+ # Optional: Full output of OSS Index results to json
104
+ #json_full = True
105
+
106
+ # Optional: HTTP/HTTPS proxy (can also be set via HTTP_PROXY/HTTPS_PROXY env vars)
107
+ #http_proxy = http://proxy.example.com:8080
108
+ #https_proxy = http://proxy.example.com:8080
109
+ ```
110
+
111
+ Authentication is **not** required. However, requests are rate limited
112
+ and authenticated requests are less restricted. A free account and API
113
+ token can be created on [OSS Index][1].
114
+
115
+
116
+ [1]: https://ossindex.sonatype.org/
117
+ [2]: https://github.com/pyupio/dparse
118
+ [3]: https://github.com/ActiveState/appdirs
@@ -0,0 +1,21 @@
1
+ [mypy]
2
+ check_untyped_defs = true
3
+
4
+ disallow_any_decorated = true
5
+ disallow_incomplete_defs = true
6
+ disallow_subclassing_any = true
7
+ disallow_untyped_calls = true
8
+ disallow_untyped_decorators = true
9
+ disallow_untyped_defs = true
10
+
11
+ ignore_missing_imports = true
12
+
13
+ no_implicit_optional = true
14
+
15
+ strict_optional = true
16
+
17
+ warn_no_return = true
18
+ warn_redundant_casts = true
19
+ warn_return_any = true
20
+ warn_unused_configs = true
21
+ warn_unused_ignores = true
@@ -0,0 +1,68 @@
1
+ # Copyright (c) 2019, Hans Jerry Illikainen <hji@dyntopia.com>
2
+ #
3
+ # SPDX-License-Identifier: BSD-2-Clause
4
+
5
+ __version__ = "1.0.0"
6
+ __project__ = "ossaudit-ng"
7
+
8
+ from typing import Dict, List, Optional
9
+
10
+ from .audit import AuditError, Vulnerability, components, flatten_vuln_list
11
+ from . import packages as _packages
12
+
13
+ __all__ = ["AuditError", "Vulnerability", "scan"]
14
+
15
+
16
+ def scan(
17
+ installed: bool = False,
18
+ files: Optional[List[str]] = None,
19
+ token: Optional[str] = None,
20
+ ignore_ids: Optional[List[str]] = None,
21
+ ignore_cache: bool = False,
22
+ proxies: Optional[Dict[str, str]] = None,
23
+ ) -> List[Vulnerability]:
24
+ """Audit packages and return a list of vulnerabilities.
25
+
26
+ Parameters
27
+ ----------
28
+ installed:
29
+ Audit currently installed packages.
30
+ files:
31
+ Paths to requirements files to audit.
32
+ token:
33
+ Sonatype OSS Index API token.
34
+ ignore_ids:
35
+ Sonatype IDs or CVE IDs to exclude from results.
36
+ ignore_cache:
37
+ Skip the local cache and always query the API.
38
+ proxies:
39
+ Proxy URLs keyed by scheme, e.g. ``{"https": "http://proxy:8080"}``.
40
+
41
+ Returns
42
+ -------
43
+ List[Vulnerability]
44
+ Every vulnerability found, minus any in *ignore_ids*.
45
+
46
+ Raises
47
+ ------
48
+ AuditError
49
+ On API authentication, rate-limit, or unexpected status errors.
50
+ """
51
+ pkgs = [] # type: list
52
+ if installed:
53
+ pkgs += _packages.get_installed()
54
+ if files:
55
+ handles = [open(f) for f in files]
56
+ try:
57
+ pkgs += _packages.get_from_files(handles)
58
+ finally:
59
+ for h in handles:
60
+ h.close()
61
+
62
+ all_coordinates = components(pkgs, token, proxies, ignore_cache)
63
+ vulns = flatten_vuln_list(all_coordinates)
64
+
65
+ if ignore_ids:
66
+ vulns = [v for v in vulns if v.id not in ignore_ids and v.cve not in ignore_ids]
67
+
68
+ return vulns
@@ -0,0 +1,14 @@
1
+ # Copyright (c) 2019, Hans Jerry Illikainen <hji@dyntopia.com>
2
+ #
3
+ # SPDX-License-Identifier: BSD-2-Clause
4
+
5
+ from . import __project__, cli
6
+
7
+
8
+ def main() -> None:
9
+ prefix = __project__.upper()
10
+ cli.cli(auto_envvar_prefix=prefix) # pylint: disable=E1120,E1123
11
+
12
+
13
+ if __name__ == "__main__":
14
+ main()
@@ -0,0 +1,142 @@
1
+ # Copyright (c) 2019, Hans Jerry Illikainen <hji@dyntopia.com>
2
+ #
3
+ # SPDX-License-Identifier: BSD-2-Clause
4
+
5
+ from collections import namedtuple
6
+ from typing import Dict, Generator, List, Optional
7
+ from urllib.parse import urljoin
8
+
9
+ import requests
10
+
11
+ from . import cache, const, packages
12
+
13
+ Vulnerability = namedtuple(
14
+ "Vulnerability", [
15
+ "name",
16
+ "version",
17
+ "id",
18
+ "cve",
19
+ "cvss_score",
20
+ "title",
21
+ "description",
22
+ ]
23
+ )
24
+
25
+
26
+ class AuditError(Exception):
27
+ pass
28
+
29
+
30
+ def components(
31
+ pkgs: List[packages.Package],
32
+ token: Optional[str] = None,
33
+ proxies: Optional[Dict[str, str]] = None,
34
+ ignore_cache: bool = False,
35
+ ) -> List[Vulnerability]:
36
+ old = list(_from_cache(pkgs, ignore_cache))
37
+ new = list(_from_api([
38
+ p for p in pkgs
39
+ if not any(p.coordinate == o.coordinate for o, _ in old)
40
+ ], ignore_cache, token, proxies)) # yapf: disable
41
+ return (new + old)
42
+
43
+
44
+ def flatten_vuln_list(vulns):
45
+ return [
46
+ _transform(p, vuln)
47
+ for p, c in vulns # package, coordinate from ossindex
48
+ for vuln in c.get("vulnerabilities", [])
49
+ ]
50
+
51
+
52
+
53
+ def _from_api(
54
+ pkgs: List[packages.Package],
55
+ ignore_cache: bool,
56
+ token: Optional[str] = None,
57
+ proxies: Optional[Dict[str, str]] = None
58
+ ) -> Generator:
59
+ url = urljoin(const.API, const.COMPONENT_REPORT)
60
+ headers = {"Authorization": f"Bearer {token}"} if token else {}
61
+
62
+ for i in range(0, len(pkgs), const.MAX_PACKAGES):
63
+ coordinates = [p.coordinate for p in pkgs[i:i + const.MAX_PACKAGES]]
64
+ res = requests.post(url, headers=headers, json={"coordinates": coordinates},
65
+ proxies=proxies, timeout=const.REQ_TIMEOUT)
66
+
67
+ if res.status_code == 200:
68
+ for entry in res.json():
69
+ pkg = next((
70
+ p for p in pkgs
71
+ if p.coordinate == entry.get("coordinates")
72
+ ), packages.Package("unknown", "0"))
73
+ if not ignore_cache:
74
+ cache.save(entry)
75
+ yield (pkg, entry)
76
+ elif res.status_code == 401:
77
+ raise AuditError("invalid credentials")
78
+ elif res.status_code == 429:
79
+ raise AuditError("too many requests")
80
+ else:
81
+ raise AuditError("unknown status code {}".format(res.status_code))
82
+
83
+
84
+ def _from_cache(pkgs: List[packages.Package], ignore_cache: bool) -> Generator:
85
+ if not ignore_cache:
86
+ for pkg in pkgs:
87
+ entry = cache.get(pkg.coordinate)
88
+ if entry:
89
+ yield (pkg, entry)
90
+
91
+
92
+ def _transform(pkg: packages.Package, vuln: Dict) -> Vulnerability:
93
+ return Vulnerability(
94
+ name=pkg.name,
95
+ version=pkg.version,
96
+ id=vuln.get("id", ""),
97
+ cve=vuln.get("cve", ""),
98
+ cvss_score=vuln.get("cvssScore", ""),
99
+ title=vuln.get("title", ""),
100
+ description=vuln.get("description", ""),
101
+ )
102
+
103
+
104
+ def create_report(vulns: List):
105
+ """Create a list of dict objects as returned by the SonarType OSSIndex scanner. The list contains on
106
+ dict per python package found and queried against the database. The packages "vulnerabilities" field
107
+ is an list with all vulnerabilites for this package. The vulnerability list can be empty.
108
+
109
+ Parameters
110
+ ----------
111
+ vulns : List with a tuple of the package object and the SonarType response for this package
112
+
113
+ Returns
114
+ -------
115
+ list - a list of scan coordinates (packages) and a list with their vulnerabilities (if any)
116
+ """
117
+ out_list = []
118
+ for p, c in vulns:
119
+ del c["time"]
120
+ out_list.append(c)
121
+ return out_list
122
+
123
+ def create_vuln_list(vulns: List, columns: List):
124
+ """Create a list of dicts from the vulnerability tuples containing only the column names requests
125
+
126
+ Parameters
127
+ ----------
128
+ vulns : List list of all vulnerabilities found
129
+ columns : List list of all columns(fields) needed from the vulnerability for output
130
+
131
+ Returns
132
+ -------
133
+ list - a list of vulnerability dictionaries containing the columns given only
134
+ """
135
+ out_list = []
136
+ for v in vulns:
137
+ v_dict = v._asdict()
138
+ v_out = {}
139
+ for c in columns:
140
+ v_out[c] = v_dict[c] if c in v_dict else ""
141
+ out_list.append(v_out)
142
+ return out_list
@@ -0,0 +1,58 @@
1
+ # Copyright (c) 2019, Hans Jerry Illikainen <hji@dyntopia.com>
2
+ #
3
+ # SPDX-License-Identifier: BSD-2-Clause
4
+
5
+ import json
6
+ import time
7
+ from typing import Dict, Optional
8
+
9
+ from . import const
10
+
11
+
12
+ def get(coordinate: str) -> Optional[Dict]:
13
+ if const.CACHE.exists():
14
+ with const.CACHE.open() as f:
15
+ try:
16
+ entry = next(
17
+ (
18
+ e for e in json.load(f)
19
+ if e["coordinates"] == coordinate
20
+ ),
21
+ {},
22
+ )
23
+ if _is_valid(entry):
24
+ return entry
25
+ except json.JSONDecodeError:
26
+ pass
27
+ return None
28
+
29
+
30
+ def save(entry: Dict) -> None:
31
+ entries = [] # type: list
32
+
33
+ if const.CACHE.exists():
34
+ with const.CACHE.open() as f:
35
+ try:
36
+ entries = [
37
+ e for e in json.load(f) if _is_valid(e)
38
+ and e.get("coordinates") != entry.get("coordinates")
39
+ ]
40
+ except json.JSONDecodeError:
41
+ pass
42
+ else:
43
+ const.CACHE.parent.mkdir(parents=True, exist_ok=True)
44
+
45
+ with const.CACHE.open("w") as f:
46
+ entry["time"] = time.time()
47
+ json.dump(entries + [entry], f)
48
+
49
+
50
+ def reset() -> None:
51
+ if const.CACHE.exists():
52
+ const.CACHE.unlink()
53
+
54
+
55
+ def _is_valid(entry: Dict) -> bool:
56
+ then = entry.get("time", float("inf"))
57
+ now = time.time()
58
+ return not then > now + const.CACHE_TIME