owasp-depscan 4.2.7__py3-none-any.whl → 4.3.0__py3-none-any.whl
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.
- depscan/cli.py +89 -24
- depscan/lib/bom.py +5 -4
- depscan/lib/csaf.py +1680 -0
- {owasp_depscan-4.2.7.dist-info → owasp_depscan-4.3.0.dist-info}/METADATA +30 -8
- {owasp_depscan-4.2.7.dist-info → owasp_depscan-4.3.0.dist-info}/RECORD +9 -8
- {owasp_depscan-4.2.7.dist-info → owasp_depscan-4.3.0.dist-info}/LICENSE +0 -0
- {owasp_depscan-4.2.7.dist-info → owasp_depscan-4.3.0.dist-info}/WHEEL +0 -0
- {owasp_depscan-4.2.7.dist-info → owasp_depscan-4.3.0.dist-info}/entry_points.txt +0 -0
- {owasp_depscan-4.2.7.dist-info → owasp_depscan-4.3.0.dist-info}/top_level.txt +0 -0
depscan/cli.py
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import argparse
|
|
5
5
|
import json
|
|
6
6
|
import os
|
|
7
|
+
import sys
|
|
7
8
|
import tempfile
|
|
8
9
|
|
|
9
10
|
from quart import Quart, request
|
|
@@ -11,7 +12,6 @@ from rich.panel import Panel
|
|
|
11
12
|
from rich.terminal_theme import MONOKAI
|
|
12
13
|
from vdb.lib import config
|
|
13
14
|
from vdb.lib import db as db_lib
|
|
14
|
-
from vdb.lib.aqua import AquaSource
|
|
15
15
|
from vdb.lib.config import data_dir
|
|
16
16
|
from vdb.lib.gha import GitHubSource
|
|
17
17
|
from vdb.lib.nvd import NvdSource
|
|
@@ -20,6 +20,7 @@ from vdb.lib.utils import parse_purl
|
|
|
20
20
|
|
|
21
21
|
import oras.client
|
|
22
22
|
|
|
23
|
+
from depscan.lib.csaf import export_csaf, write_toml
|
|
23
24
|
from depscan.lib import privado, utils
|
|
24
25
|
from depscan.lib.analysis import (
|
|
25
26
|
PrepareVexOptions,
|
|
@@ -31,8 +32,18 @@ from depscan.lib.analysis import (
|
|
|
31
32
|
summary_stats,
|
|
32
33
|
)
|
|
33
34
|
from depscan.lib.audit import audit, risk_audit, risk_audit_map, type_audit_map
|
|
34
|
-
from depscan.lib.bom import
|
|
35
|
-
|
|
35
|
+
from depscan.lib.bom import (
|
|
36
|
+
create_bom,
|
|
37
|
+
get_pkg_by_type,
|
|
38
|
+
get_pkg_list,
|
|
39
|
+
submit_bom,
|
|
40
|
+
)
|
|
41
|
+
from depscan.lib.config import (
|
|
42
|
+
UNIVERSAL_SCAN_TYPE,
|
|
43
|
+
license_data_dir,
|
|
44
|
+
spdx_license_list,
|
|
45
|
+
vdb_database_url,
|
|
46
|
+
)
|
|
36
47
|
from depscan.lib.license import build_license_data, bulk_lookup
|
|
37
48
|
from depscan.lib.logger import LOG, console
|
|
38
49
|
from depscan.lib.utils import get_version
|
|
@@ -77,7 +88,15 @@ def build_args():
|
|
|
77
88
|
action="store_true",
|
|
78
89
|
default=False,
|
|
79
90
|
dest="cache",
|
|
80
|
-
help="Cache vulnerability information in platform specific "
|
|
91
|
+
help="Cache vulnerability information in platform specific "
|
|
92
|
+
"user_data_dir",
|
|
93
|
+
)
|
|
94
|
+
parser.add_argument(
|
|
95
|
+
"--csaf",
|
|
96
|
+
action="store_true",
|
|
97
|
+
default=False,
|
|
98
|
+
dest="csaf",
|
|
99
|
+
help="Generate a CSAF",
|
|
81
100
|
)
|
|
82
101
|
parser.add_argument(
|
|
83
102
|
"--sync",
|
|
@@ -92,12 +111,15 @@ def build_args():
|
|
|
92
111
|
action="store_true",
|
|
93
112
|
default=True,
|
|
94
113
|
dest="suggest",
|
|
95
|
-
help="DEPRECATED: Suggest is the default mode for determining fix "
|
|
114
|
+
help="DEPRECATED: Suggest is the default mode for determining fix "
|
|
115
|
+
"version.",
|
|
96
116
|
)
|
|
97
117
|
parser.add_argument(
|
|
98
118
|
"--risk-audit",
|
|
99
119
|
action="store_true",
|
|
100
|
-
default=True
|
|
120
|
+
default=True
|
|
121
|
+
if os.getenv("ENABLE_OSS_RISK", "") in ["true", "1"]
|
|
122
|
+
else False,
|
|
101
123
|
dest="risk_audit",
|
|
102
124
|
help="Perform package risk audit (slow operation). Npm only.",
|
|
103
125
|
)
|
|
@@ -137,7 +159,9 @@ def build_args():
|
|
|
137
159
|
)
|
|
138
160
|
parser.add_argument(
|
|
139
161
|
"--reports-dir",
|
|
140
|
-
default=os.getenv(
|
|
162
|
+
default=os.getenv(
|
|
163
|
+
"DEPSCAN_REPORTS_DIR", os.path.join(os.getcwd(), "reports")
|
|
164
|
+
),
|
|
141
165
|
dest="reports_dir",
|
|
142
166
|
help="Reports directory",
|
|
143
167
|
)
|
|
@@ -264,7 +288,9 @@ def scan(db, project_type, pkg_list, suggest_mode):
|
|
|
264
288
|
LOG.debug("Empty package search attempted!")
|
|
265
289
|
else:
|
|
266
290
|
LOG.debug("Scanning %d oss dependencies for issues", len(pkg_list))
|
|
267
|
-
results, pkg_aliases, purl_aliases = utils.search_pkgs(
|
|
291
|
+
results, pkg_aliases, purl_aliases = utils.search_pkgs(
|
|
292
|
+
db, project_type, pkg_list
|
|
293
|
+
)
|
|
268
294
|
# pkg_aliases is a dict that can be used to find the original vendor and
|
|
269
295
|
# package name This way we consistently use the same names used by the
|
|
270
296
|
# caller irrespective of how the result was obtained
|
|
@@ -321,7 +347,9 @@ def scan(db, project_type, pkg_list, suggest_mode):
|
|
|
321
347
|
"Re-checking our suggestion to ensure there are no further "
|
|
322
348
|
"vulnerabilities"
|
|
323
349
|
)
|
|
324
|
-
override_results, _, _ = utils.search_pkgs(
|
|
350
|
+
override_results, _, _ = utils.search_pkgs(
|
|
351
|
+
db, project_type, sug_pkg_list
|
|
352
|
+
)
|
|
325
353
|
if override_results:
|
|
326
354
|
new_sug_dict = suggest_version(override_results)
|
|
327
355
|
LOG.debug("Received override results: %s", new_sug_dict)
|
|
@@ -427,7 +455,7 @@ def summarise(
|
|
|
427
455
|
bom_data["services"] = []
|
|
428
456
|
bom_data["services"].insert(0, pservice)
|
|
429
457
|
with open(vex_file, mode="w", encoding="utf-8") as vexfp:
|
|
430
|
-
json.dump(bom_data, vexfp)
|
|
458
|
+
json.dump(bom_data, vexfp, indent=4)
|
|
431
459
|
LOG.info("VEX file %s generated successfully", vex_file)
|
|
432
460
|
except Exception:
|
|
433
461
|
LOG.warning("Unable to generate VEX file for this scan")
|
|
@@ -453,8 +481,8 @@ async def cache():
|
|
|
453
481
|
db = db_lib.get()
|
|
454
482
|
if not db_lib.index_count(db["index_file"]):
|
|
455
483
|
oras_client = oras.client.OrasClient()
|
|
456
|
-
paths_list = oras_client.pull(target
|
|
457
|
-
LOG.debug(f
|
|
484
|
+
paths_list = oras_client.pull(target=vdb_database_url, outdir=data_dir)
|
|
485
|
+
LOG.debug(f"VDB data is stored at: {paths_list}")
|
|
458
486
|
return {
|
|
459
487
|
"error": "false",
|
|
460
488
|
"message": "vulnerability database cached successfully",
|
|
@@ -551,7 +579,8 @@ async def run_scan():
|
|
|
551
579
|
else:
|
|
552
580
|
return {
|
|
553
581
|
"error": "true",
|
|
554
|
-
"message": "Unable to generate SBoM. Check your input path or "
|
|
582
|
+
"message": "Unable to generate SBoM. Check your input path or "
|
|
583
|
+
"url.",
|
|
555
584
|
}, 500
|
|
556
585
|
|
|
557
586
|
|
|
@@ -562,7 +591,9 @@ def run_server(args):
|
|
|
562
591
|
:param args: Command line arguments passed to the function.
|
|
563
592
|
"""
|
|
564
593
|
print(at_logo)
|
|
565
|
-
console.print(
|
|
594
|
+
console.print(
|
|
595
|
+
f"Depscan server running on {args.server_host}:{args.server_port}"
|
|
596
|
+
)
|
|
566
597
|
app.config["CDXGEN_SERVER_URL"] = args.cdxgen_server
|
|
567
598
|
app.run(
|
|
568
599
|
host=args.server_host,
|
|
@@ -583,9 +614,24 @@ def main():
|
|
|
583
614
|
if not args.no_banner:
|
|
584
615
|
print(at_logo)
|
|
585
616
|
src_dir = args.src_dir_image
|
|
586
|
-
if not src_dir:
|
|
617
|
+
if not src_dir or src_dir == ".":
|
|
587
618
|
src_dir = os.getcwd()
|
|
588
619
|
reports_dir = args.reports_dir
|
|
620
|
+
if args.csaf:
|
|
621
|
+
toml_file_path = os.path.join(src_dir, "csaf.toml")
|
|
622
|
+
if not os.path.exists(toml_file_path):
|
|
623
|
+
LOG.info("CSAF toml not found, creating template in %s", src_dir)
|
|
624
|
+
write_toml(toml_file_path)
|
|
625
|
+
LOG.info(
|
|
626
|
+
"Please fill out the toml with your details and rerun depscan."
|
|
627
|
+
)
|
|
628
|
+
LOG.info("Check out our CSAF documentation for an explanation of "
|
|
629
|
+
"this feature. https://github.com/owasp-dep-scan/dep-scan"
|
|
630
|
+
"/blob/master/contrib/CSAF_README.md")
|
|
631
|
+
LOG.info("If you're just checking out how our generator works, "
|
|
632
|
+
"feel free to skip filling out the toml and just rerun "
|
|
633
|
+
"depscan.")
|
|
634
|
+
sys.exit(0)
|
|
589
635
|
# Detect the project types and perform the right type of scan
|
|
590
636
|
if args.project_type:
|
|
591
637
|
project_types_list = args.project_type.split(",")
|
|
@@ -623,7 +669,9 @@ def main():
|
|
|
623
669
|
for project_type in project_types_list:
|
|
624
670
|
results = []
|
|
625
671
|
report_file = areport_file.replace(".json", f"-{project_type}.json")
|
|
626
|
-
risk_report_file = areport_file.replace(
|
|
672
|
+
risk_report_file = areport_file.replace(
|
|
673
|
+
".json", f"-risk.{project_type}.json"
|
|
674
|
+
)
|
|
627
675
|
LOG.info("=" * 80)
|
|
628
676
|
if args.bom and os.path.exists(args.bom):
|
|
629
677
|
bom_file = args.bom
|
|
@@ -660,7 +708,9 @@ def main():
|
|
|
660
708
|
license_report_file = os.path.join(
|
|
661
709
|
reports_dir, "license-" + project_type + ".json"
|
|
662
710
|
)
|
|
663
|
-
analyse_licenses(
|
|
711
|
+
analyse_licenses(
|
|
712
|
+
project_type, licenses_results, license_report_file
|
|
713
|
+
)
|
|
664
714
|
if project_type in risk_audit_map:
|
|
665
715
|
if args.risk_audit:
|
|
666
716
|
console.print(
|
|
@@ -708,14 +758,16 @@ def main():
|
|
|
708
758
|
try:
|
|
709
759
|
audit_results = audit(project_type, pkg_list)
|
|
710
760
|
if audit_results:
|
|
711
|
-
LOG.debug(
|
|
761
|
+
LOG.debug(
|
|
762
|
+
"Remote audit yielded %d results", len(audit_results)
|
|
763
|
+
)
|
|
712
764
|
results = results + audit_results
|
|
713
765
|
except Exception as e:
|
|
714
766
|
LOG.error("Remote audit was not successful")
|
|
715
767
|
LOG.error(e)
|
|
716
768
|
results = []
|
|
717
|
-
# In case of docker, bom, or universal type, check if there are any
|
|
718
|
-
# audited remotely
|
|
769
|
+
# In case of docker, bom, or universal type, check if there are any
|
|
770
|
+
# npm packages that can be audited remotely
|
|
719
771
|
if project_type in ("podman", "docker", "oci", "bom", "universal"):
|
|
720
772
|
npm_pkg_list = get_pkg_by_type(pkg_list, "npm")
|
|
721
773
|
if npm_pkg_list:
|
|
@@ -734,15 +786,23 @@ def main():
|
|
|
734
786
|
if not db_lib.index_count(db["index_file"]):
|
|
735
787
|
run_cacher = True
|
|
736
788
|
else:
|
|
737
|
-
LOG.debug(
|
|
789
|
+
LOG.debug(
|
|
790
|
+
"Vulnerability database loaded from %s", config.vdb_bin_file
|
|
791
|
+
)
|
|
738
792
|
|
|
739
793
|
sources_list = [OSVSource(), NvdSource()]
|
|
740
794
|
if os.environ.get("GITHUB_TOKEN"):
|
|
741
795
|
sources_list.insert(0, GitHubSource())
|
|
742
796
|
if run_cacher:
|
|
797
|
+
LOG.debug(
|
|
798
|
+
"About to download vdb from %s. This might take a while ...",
|
|
799
|
+
vdb_database_url,
|
|
800
|
+
)
|
|
743
801
|
oras_client = oras.client.OrasClient()
|
|
744
|
-
paths_list = oras_client.pull(
|
|
745
|
-
|
|
802
|
+
paths_list = oras_client.pull(
|
|
803
|
+
target=vdb_database_url, outdir=data_dir
|
|
804
|
+
)
|
|
805
|
+
LOG.debug("VDB data is stored at: %s", paths_list)
|
|
746
806
|
run_cacher = False
|
|
747
807
|
elif args.sync:
|
|
748
808
|
for s in sources_list:
|
|
@@ -758,7 +818,12 @@ def main():
|
|
|
758
818
|
db, project_type, pkg_list, args.suggest
|
|
759
819
|
)
|
|
760
820
|
if vdb_results:
|
|
761
|
-
results
|
|
821
|
+
results += vdb_results
|
|
822
|
+
if args.csaf:
|
|
823
|
+
new_res = []
|
|
824
|
+
for r in results:
|
|
825
|
+
new_res.append(r.to_dict())
|
|
826
|
+
export_csaf(new_res, src_dir, reports_dir)
|
|
762
827
|
# Summarise and print results
|
|
763
828
|
summarise(
|
|
764
829
|
project_type,
|
depscan/lib/bom.py
CHANGED
|
@@ -38,7 +38,7 @@ def exec_tool(args, cwd=None, stdout=subprocess.PIPE):
|
|
|
38
38
|
stderr=subprocess.STDOUT,
|
|
39
39
|
cwd=cwd,
|
|
40
40
|
env=os.environ.copy(),
|
|
41
|
-
shell=False,
|
|
41
|
+
shell=True if sys.platform == "win32" else False,
|
|
42
42
|
encoding="utf-8",
|
|
43
43
|
)
|
|
44
44
|
LOG.debug(cp.stdout)
|
|
@@ -245,8 +245,8 @@ def resource_path(relative_path):
|
|
|
245
245
|
return os.path.join(base_path, relative_path)
|
|
246
246
|
|
|
247
247
|
|
|
248
|
-
def exec_cdxgen(
|
|
249
|
-
if
|
|
248
|
+
def exec_cdxgen(use_bin=True):
|
|
249
|
+
if use_bin:
|
|
250
250
|
cdxgen_cmd = os.environ.get("CDXGEN_CMD", "cdxgen")
|
|
251
251
|
if not shutil.which(cdxgen_cmd):
|
|
252
252
|
local_bin = resource_path(
|
|
@@ -269,7 +269,8 @@ def exec_cdxgen(bin=True):
|
|
|
269
269
|
return cdxgen_cmd
|
|
270
270
|
except Exception:
|
|
271
271
|
return None
|
|
272
|
-
|
|
272
|
+
else:
|
|
273
|
+
return cdxgen_cmd
|
|
273
274
|
else:
|
|
274
275
|
# cdxgen_cmd = (
|
|
275
276
|
# os.environ.get("CDXGEN_CMD", "cdxgen")
|