owasp-depscan 6.0.0a2__py3-none-any.whl → 6.0.0b2__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.

Potentially problematic release.


This version of owasp-depscan might be problematic. Click here for more details.

depscan/cli.py CHANGED
@@ -54,6 +54,7 @@ from depscan.lib.config import (
54
54
  from depscan.lib.license import build_license_data, bulk_lookup
55
55
  from depscan.lib.logger import DEBUG, LOG, SPINNER, console, IS_CI
56
56
 
57
+ from reporting_lib.htmlgen import ReportGenerator
57
58
  if sys.platform == "win32" and os.environ.get("PYTHONIOENCODING") is None:
58
59
  sys.stdin.reconfigure(encoding="utf-8")
59
60
  sys.stdout.reconfigure(encoding="utf-8")
@@ -617,15 +618,11 @@ def run_depscan(args):
617
618
  html_report_file = depscan_options.get(
618
619
  "html_report_file", os.path.join(reports_dir, "depscan.html")
619
620
  )
620
- pdf_report_file = depscan_options.get(
621
- "pdf_report_file", os.path.join(reports_dir, "depscan.pdf")
622
- )
623
621
  txt_report_file = depscan_options.get(
624
622
  "txt_report_file", os.path.join(reports_dir, "depscan.txt")
625
623
  )
626
624
  run_config_file = os.path.join(reports_dir, "depscan.toml.sample")
627
625
  depscan_options["html_report_file"] = html_report_file
628
- depscan_options["pdf_report_file"] = pdf_report_file
629
626
  depscan_options["txt_report_file"] = txt_report_file
630
627
  # Create reports directory
631
628
  if reports_dir and not os.path.exists(reports_dir):
@@ -822,7 +819,7 @@ def run_depscan(args):
822
819
  pkg_max_risk_score=pkg_max_risk_score,
823
820
  risk_report_file=risk_report_file,
824
821
  )
825
- if not args.no_vuln_table and report_data:
822
+ if not args.no_vuln_table and report_data and rtable:
826
823
  console.print(rtable)
827
824
  except Exception as e:
828
825
  LOG.error(e)
@@ -975,7 +972,9 @@ def run_depscan(args):
975
972
  theme=(MONOKAI if os.getenv("USE_DARK_THEME") else DEFAULT_TERMINAL_THEME),
976
973
  )
977
974
  console.save_text(txt_report_file, clear=False)
978
- utils.export_pdf(html_report_file, pdf_report_file)
975
+ # Prettify the rich html report
976
+ html_report_generator = ReportGenerator(input_rich_html_path=html_report_file, report_output_path=html_report_file, raw_content=False)
977
+ html_report_generator.parse_and_generate_report()
979
978
  # This logic needs refactoring
980
979
  # render report into template if wished
981
980
  if args.report_template and os.path.isfile(args.report_template):
depscan/lib/bom.py CHANGED
@@ -6,7 +6,6 @@ from collections import defaultdict
6
6
  from datetime import datetime, timezone
7
7
  from urllib.parse import unquote_plus
8
8
 
9
- from blint.cyclonedx.spec import CycloneDX
10
9
  from custom_json_diff.lib.utils import json_load, json_dump
11
10
  from defusedxml.ElementTree import parse
12
11
  from xbom_lib.blint import BlintGenerator
@@ -15,7 +14,6 @@ from xbom_lib.cdxgen import (
15
14
  CdxgenImageBasedGenerator,
16
15
  CdxgenServerGenerator,
17
16
  )
18
-
19
17
  from depscan.lib.logger import LOG, SPINNER, console
20
18
  from depscan.lib.utils import cleanup_license_string
21
19
  from typing import Dict, Optional
@@ -327,13 +325,6 @@ def create_blint_bom(
327
325
  LOG.info(
328
326
  "The blint invocation was unsuccessful. Try generating the BOM separately."
329
327
  )
330
- # Empty SBOM is fine if there are no binaries in the project.
331
- elif bom_result.bom_obj and isinstance(bom_result.bom_obj, CycloneDX):
332
- if (
333
- not bom_result.bom_obj.components
334
- and not bom_result.bom_obj.dependencies
335
- ):
336
- LOG.debug("Empty SBOM received from blint.")
337
328
  return bom_result.success and os.path.exists(bom_file)
338
329
 
339
330
 
@@ -418,7 +409,12 @@ def create_lifecycle_boms(cdxgen_lib, src_dir, options):
418
409
  )
419
410
  status.update("Preparing blint for post-build BOM generation.")
420
411
  # post-build BOM with blint
421
- coptions = {**options, "deep": False, "use_blintdb": False, "lifecycles": ["post-build"]}
412
+ coptions = {
413
+ **options,
414
+ "deep": False,
415
+ "use_blintdb": False,
416
+ "lifecycles": ["post-build"],
417
+ }
422
418
  # What if the build directory is different to the source
423
419
  build_dir = os.getenv("DEPSCAN_BUILD_DIR") or options.get("build_dir") or src_dir
424
420
  res = create_blint_bom(postbuild_bom_file, build_dir, options=coptions)
depscan/lib/explainer.py CHANGED
@@ -1,3 +1,4 @@
1
+ import os
1
2
  import re
2
3
  import glob
3
4
  from collections import defaultdict
@@ -46,9 +47,14 @@ def explain(project_type, src_dir, bom_dir, vdr_file, vdr_result, explanation_mo
46
47
  rsection = Markdown("""## Service Endpoints
47
48
 
48
49
  The following endpoints and code hotspots were identified by depscan. Verify that proper authentication and authorization mechanisms are in place to secure them.""")
49
- console.print(rsection)
50
+ any_endpoints_shown = False
50
51
  for ospec in openapi_spec_files:
51
- pattern_methods = print_endpoints(ospec)
52
+ pattern_methods = print_endpoints(
53
+ ospec, rsection if not any_endpoints_shown else None
54
+ )
55
+ if not any_endpoints_shown and pattern_methods:
56
+ any_endpoints_shown = True
57
+
52
58
  # Return early for endpoints only explanations
53
59
  if explanation_mode in ("Endpoints",):
54
60
  return
@@ -58,6 +64,12 @@ The following endpoints and code hotspots were identified by depscan. Verify tha
58
64
  else "Reachable Flows"
59
65
  )
60
66
  for sf in slices_files:
67
+ if len(slices_files) > 1:
68
+ fn = os.path.basename(sf)
69
+ section_label = f"# Explanations for {sf}"
70
+ if "-" in fn:
71
+ section_label = f"# Explanations for {fn.split('-')[0].upper()}"
72
+ console.print(Markdown(section_label))
61
73
  if (reachables_data := json_load(sf, log=LOG)) and reachables_data.get(
62
74
  "reachables"
63
75
  ):
@@ -102,7 +114,7 @@ def _track_usage_targets(usage_targets, usages_object):
102
114
  usage_targets.add(f"{file}#{l}")
103
115
 
104
116
 
105
- def print_endpoints(ospec):
117
+ def print_endpoints(ospec, header_section=None):
106
118
  if not ospec:
107
119
  return
108
120
  paths = json_load(ospec).get("paths") or {}
@@ -144,6 +156,9 @@ def print_endpoints(ospec):
144
156
  sorted_areas.sort()
145
157
  rtable.add_row(k, ("\n".join(v)).upper(), "\n".join(sorted_areas))
146
158
  if pattern_methods:
159
+ # Print the header section
160
+ if header_section:
161
+ console.print(header_section)
147
162
  console.print()
148
163
  console.print(rtable)
149
164
  return pattern_methods
@@ -171,28 +186,26 @@ def explain_reachables(
171
186
  reachable_explanations = 0
172
187
  checked_flows = 0
173
188
  has_crypto_flows = False
189
+ explained_ids = {}
174
190
  purls_reachable_explanations = defaultdict(int)
175
191
  source_reachable_explanations = defaultdict(int)
176
192
  sink_reachable_explanations = defaultdict(int)
177
193
  has_explanation = False
178
194
  header_shown = False
195
+ has_cpp_flow = False
179
196
  for areach in reachables.get("reachables", []):
197
+ cpp_flow = is_cpp_flow(areach.get("flows"))
198
+ if not has_cpp_flow and cpp_flow:
199
+ has_cpp_flow = True
180
200
  if (
181
201
  not areach.get("flows")
182
202
  or len(areach.get("flows")) < 2
183
- or (not areach.get("purls") and not is_cpp_flow(areach.get("flows")))
203
+ or (not areach.get("purls") and not cpp_flow)
184
204
  ):
185
205
  continue
186
- # Focus only on the prioritized list if available
187
- # if project_type in ("java",) and pkg_group_rows:
188
- # is_prioritized = False
189
- # for apurl in areach.get("purls"):
190
- # if pkg_group_rows.get(apurl):
191
- # is_prioritized = True
192
- # if not is_prioritized:
193
- # continue
194
206
  (
195
207
  flow_tree,
208
+ added_ids,
196
209
  comment,
197
210
  source_sink_desc,
198
211
  source_code_str,
@@ -207,7 +220,13 @@ def explain_reachables(
207
220
  project_type,
208
221
  vdr_result,
209
222
  )
210
- if not source_sink_desc or not flow_tree:
223
+ # The goal is to reduce duplicate explanations by checking if a given flow is similar to one we have explained
224
+ # before. We do this by checking the node ids, source-sink explanations, purl tags and so on.
225
+ added_ids_str = "-".join(added_ids)
226
+ # Have we seen this sequence before?
227
+ if explained_ids.get(added_ids_str) or len(added_ids) < 4:
228
+ continue
229
+ if not source_sink_desc or not flow_tree or len(flow_tree.children) < 4:
211
230
  continue
212
231
  # In non-reachables mode, we are not interested in reachable flows.
213
232
  if (
@@ -258,6 +277,7 @@ def explain_reachables(
258
277
  header_shown = True
259
278
  console.print()
260
279
  console.print(rtable)
280
+ explained_ids[added_ids_str] = True
261
281
  reachable_explanations += 1
262
282
  if purls_str:
263
283
  purls_reachable_explanations[purls_str] += 1
@@ -282,9 +302,15 @@ def explain_reachables(
282
302
  - Generate a Cryptographic BOM with cdxgen and monitor it in Dependency-Track.
283
303
  """
284
304
  elif checked_flows:
285
- tips += """
305
+ if not has_cpp_flow:
306
+ tips += """
286
307
  - Review the validation and sanitization methods used in the application.
287
308
  - To enhance the security posture, implement a common validation middleware.
309
+ """
310
+ else:
311
+ tips += """
312
+ - Continuously fuzz the parser and validation functions with diverse payloads.
313
+ - Generate post-build SBOMs with OWASP blint by building this project for various architecture combinations. Re-run depscan with the `--bom-dir` argument to enhance the analysis.
288
314
  """
289
315
  elif purls_reachable_explanations:
290
316
  tips += """
@@ -348,7 +374,7 @@ def flow_to_source_sink(idx, flow, purls, project_type, vdr_result):
348
374
  source_sink_desc = f"Invocation: {method_full_name}"
349
375
  elif flow.get("label") == "RETURN" and flow.get("code"):
350
376
  source_sink_desc = flow.get("code").split("\n")[0]
351
- elif project_type not in ("java") and flow.get("label") == "IDENTIFIER":
377
+ elif project_type not in ("java",) and flow.get("label") == "IDENTIFIER":
352
378
  source_sink_desc = flow.get("code").split("\n")[0]
353
379
  if source_sink_desc.endswith("("):
354
380
  source_sink_desc = f":diamond_suit: {source_sink_desc})"
@@ -411,19 +437,21 @@ def filter_tags(tags):
411
437
 
412
438
 
413
439
  def is_filterable_code(project_type, code):
414
- match project_type:
415
- case "js" | "ts" | "javascript" | "typescript" | "bom":
416
- for c in (
417
- "console.log",
418
- "thoughtLog(",
419
- "_tmp_",
420
- "LOG.debug(",
421
- "options.get(",
422
- "RET",
423
- "this.",
424
- ):
425
- if code and code.startswith(c):
426
- return True
440
+ if len(code) < 3:
441
+ return True
442
+ for c in (
443
+ "console.log",
444
+ "thoughtLog(",
445
+ "_tmp_",
446
+ "LOG.debug(",
447
+ "options.get(",
448
+ "RET",
449
+ "this.",
450
+ "NULL",
451
+ "!",
452
+ ):
453
+ if code and code.startswith(c):
454
+ return True
427
455
  return False
428
456
 
429
457
 
@@ -436,21 +464,33 @@ def flow_to_str(explanation_mode, flow, project_type):
436
464
  and flow.get("lineNumber")
437
465
  and not flow.get("parentFileName").startswith("unknown")
438
466
  ):
439
- file_loc = f"{flow.get('parentFileName').replace('src/main/java/', '').replace('src/main/scala/', '')}#{flow.get('lineNumber')} "
467
+ # strip common prefixes
468
+ name = flow.get('parentFileName', '')
469
+ for p in ('src/main/java/', 'src/main/scala/'):
470
+ name = name.removeprefix(p)
471
+ file_loc = f"{name}#{flow.get('lineNumber')} "
440
472
  node_desc = flow.get("code").split("\n")[0]
473
+ if (len(node_desc) < 3 or node_desc.endswith("{")) and len(flow.get("code")) > 3:
474
+ node_desc = " ".join(flow.get("code", "").split())
475
+ if "(" in node_desc:
476
+ node_desc = node_desc.split("(")[0] + "() ..."
441
477
  if node_desc.endswith("("):
442
478
  node_desc = f":diamond_suit: {node_desc})"
479
+ elif node_desc.startswith("return "):
480
+ node_desc = f":arrow_backward: [italic]{node_desc}[/italic]"
443
481
  tags = filter_tags(flow.get("tags"))
444
- if flow.get("label") == "METHOD_PARAMETER_IN":
482
+ if flow.get("label") in ("METHOD_PARAMETER_IN",):
445
483
  param_name = flow.get("name")
446
484
  if param_name == "this":
447
485
  param_name = ""
448
486
  node_desc = f"{flow.get('parentMethodName')}([red]{param_name}[/red]) :right_arrow_curving_left:"
449
487
  if tags:
450
488
  node_desc = f"{node_desc}\n[bold]Tags:[/bold] [italic]{tags}[/italic]\n"
451
- elif flow.get("label") == "IDENTIFIER":
489
+ elif flow.get("label") in ("IDENTIFIER", "CALL"):
452
490
  if node_desc.startswith("<"):
453
491
  node_desc = flow.get("name")
492
+ if flow.get("isExternal"):
493
+ node_desc = f"{node_desc} :right_arrow_curving_up:"
454
494
  if tags:
455
495
  node_desc = f"{node_desc}\n[bold]Tags:[/bold] [italic]{tags}[/italic]\n"
456
496
  if tags and not is_filterable_code(project_type, node_desc):
@@ -487,6 +527,7 @@ def explain_flows(explanation_mode, flows, purls, project_type, vdr_result):
487
527
  if purls:
488
528
  purls_str = "\n".join(purls)
489
529
  comments.append(f"[info]Reachable Packages:[/info]\n{purls_str}")
530
+ added_ids = []
490
531
  added_flows = []
491
532
  added_node_desc = []
492
533
  has_check_tag = False
@@ -518,12 +559,13 @@ def explain_flows(explanation_mode, flows, purls, project_type, vdr_result):
518
559
  file_loc, flow_str, node_desc, has_check_tag_flow = flow_to_str(
519
560
  explanation_mode, aflow, project_type
520
561
  )
521
- if last_file_loc and last_file_loc == file_loc:
562
+ if not flow_str or (last_file_loc and last_file_loc == file_loc):
522
563
  continue
523
564
  last_file_loc = file_loc
524
565
  if flow_str in added_flows or node_desc in added_node_desc:
525
566
  continue
526
567
  added_flows.append(flow_str)
568
+ added_ids.append(str(aflow.get("id", "")))
527
569
  added_node_desc.append(node_desc)
528
570
  if not tree:
529
571
  tree = Tree(flow_str)
@@ -538,6 +580,7 @@ def explain_flows(explanation_mode, flows, purls, project_type, vdr_result):
538
580
  )
539
581
  return (
540
582
  tree,
583
+ added_ids,
541
584
  "\n".join(comments),
542
585
  source_sink_desc,
543
586
  source_code_str,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: owasp-depscan
3
- Version: 6.0.0a2
3
+ Version: 6.0.0b2
4
4
  Summary: Fully open-source security audit for project dependencies based on known vulnerabilities and advisories.
5
5
  Author-email: Team AppThreat <cloud@appthreat.com>
6
6
  License-Expression: MIT
@@ -20,7 +20,7 @@ Classifier: Topic :: Utilities
20
20
  Requires-Python: >=3.10
21
21
  Description-Content-Type: text/markdown
22
22
  License-File: LICENSE
23
- Requires-Dist: appthreat-vulnerability-db[oras]
23
+ Requires-Dist: appthreat-vulnerability-db[oras]>=6.4.3
24
24
  Requires-Dist: custom-json-diff>=2.1.6
25
25
  Requires-Dist: defusedxml>=0.7.1
26
26
  Requires-Dist: PyYAML>=6.0.2
@@ -28,9 +28,10 @@ Requires-Dist: rich>=14.0.0
28
28
  Requires-Dist: Jinja2>=3.1.6
29
29
  Requires-Dist: packageurl-python>=0.16.0
30
30
  Requires-Dist: cvss>=3.4
31
- Requires-Dist: tomli>=2.2.1
31
+ Requires-Dist: tomli>=2.2.1; python_full_version <= "3.11"
32
32
  Requires-Dist: ds-xbom-lib
33
33
  Requires-Dist: ds-analysis-lib
34
+ Requires-Dist: ds-reporting-lib
34
35
  Provides-Extra: dev
35
36
  Requires-Dist: black>=25.1.0; extra == "dev"
36
37
  Requires-Dist: flake8>=7.1.2; extra == "dev"
@@ -147,8 +148,6 @@ Always stay a step ahead with advanced vulnerability and exploit prediction.
147
148
  - Chainguard
148
149
  - Wolfi OS
149
150
 
150
- Application vulnerabilities would be reported for all Linux distros and Windows. To download the full vulnerability database suitable for scanning OS, invoke dep-scan with `` for the first time. dep-scan would also download the appropriate database based on project type automatically.
151
-
152
151
  ## Quick Start
153
152
 
154
153
  dep-scan is ideal for use during continuous integration (CI) and as a local development tool.
@@ -1,11 +1,11 @@
1
1
  depscan/__init__.py,sha256=u_HyD63vlgVi48bUU6bI8O1fdXJOLPaNwCrMJdCnzJE,165
2
- depscan/cli.py,sha256=ojbK1tX3dPPNdWmPV8uTtY4oto4vgewl6gNlurw9ZGA,40874
2
+ depscan/cli.py,sha256=KULqCzhgQJ6OBXP4gUo7QIXJi1ZvOudzacw-6PRHx5I,40934
3
3
  depscan/cli_options.py,sha256=zKje-zoM0OjCVW0pC6cRWb_8D6T-R1PTSjfGBjmSzZ8,9468
4
4
  depscan/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  depscan/lib/audit.py,sha256=i6sE-vN0Fk5gc-npHIhrrior4772_tTlPVAlznyuogo,1582
6
- depscan/lib/bom.py,sha256=9Y_Xo-q2U9Q8ZTb47XoYX_E4GTfryAFhXUTKHb48X2U,21176
6
+ depscan/lib/bom.py,sha256=OUFESoyPmYz0Igm9zoLn8ap6dLUJdQUb1VFN8i5lEgE,20823
7
7
  depscan/lib/config.py,sha256=v3Rv4nyPMjvk-EbpWSSYcloQa4v2N0bTzXKWF5nDJvg,9572
8
- depscan/lib/explainer.py,sha256=7YOTMqlkdJj4tuJ8Wbtpw9wQDM8W8K5kiPFaxgQ9eVI,20230
8
+ depscan/lib/explainer.py,sha256=yyGE7FwXmzcTniYewHBSkxtfgR6wilG5KXn6WgyyiWY,22018
9
9
  depscan/lib/github.py,sha256=h6e_12xLwspXJbt_7lW6vuHaqgJQgyFSRCLrfUndCH4,1697
10
10
  depscan/lib/license.py,sha256=ChwqAXPrMcDQJqSgDag7Th8VwoRCq8oMvwPt64iL4gw,2404
11
11
  depscan/lib/logger.py,sha256=gU5epbOHlhvuFhMqRTgn71AJ4KPB5Gf2iAmgTx3qI-4,2837
@@ -17,7 +17,7 @@ depscan/lib/package_query/metadata.py,sha256=Nnh6ctLIXPRTMoHOjZC7uhmFM3ogGljg22d
17
17
  depscan/lib/package_query/npm_pkg.py,sha256=eXdTeq1ffxLN3fWfMh3QcGG1u0VYLGR4-0BmVoD5BPk,15443
18
18
  depscan/lib/package_query/pkg_query.py,sha256=ODnRegpD3gv5FIKy0ogXMEITcdfVVV-eoIaWf7UhrQU,7038
19
19
  depscan/lib/package_query/pypi_pkg.py,sha256=scn6UhMWqA0ajS-u5UVMGV7Vx-6PEY75lN6uET9yU5c,4808
20
- owasp_depscan-6.0.0a2.dist-info/licenses/LICENSE,sha256=oQnCbnZtJ_NLDdOLc-rVY1D1N0RNWLHPpYXcc77xzSo,1073
20
+ owasp_depscan-6.0.0b2.dist-info/licenses/LICENSE,sha256=oQnCbnZtJ_NLDdOLc-rVY1D1N0RNWLHPpYXcc77xzSo,1073
21
21
  vendor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
22
  vendor/choosealicense.com/_data/fields.yml,sha256=ydNsITXFUuADzGPM-jcUcJnN0r_qSGgH51oV27nX3Qs,819
23
23
  vendor/choosealicense.com/_data/meta.yml,sha256=rSNmnx0LE6VA9wnR29Y_P9s-TnADQqbdw2enE4i1mWM,1792
@@ -69,9 +69,9 @@ vendor/choosealicense.com/_licenses/upl-1.0.txt,sha256=yJ3mfZkFmzSHesz6uOF9S0fX6
69
69
  vendor/choosealicense.com/_licenses/vim.txt,sha256=d5GQjXB328L8EBkhKgxcjk344D3K7UfcJmP1barrhHI,6119
70
70
  vendor/choosealicense.com/_licenses/wtfpl.txt,sha256=BxXeubkvQm32MDmlZsBcbzJzBpR5kWgw0JxSR9d7f3k,948
71
71
  vendor/choosealicense.com/_licenses/zlib.txt,sha256=e6dfCeLhxD3NCnIkY4cVIagRaWdRvencjNhHZ1APvpc,1678
72
- vendor/spdx/json/licenses.json,sha256=66zBMswN5ufUL8M9TIMV0PQH9aZK_X5mtHm3OkwKmVU,314245
73
- owasp_depscan-6.0.0a2.dist-info/METADATA,sha256=a2l-2ebikqGNzqTMcQy_5K4B0nk1Ng9R2qAHneouuEw,17650
74
- owasp_depscan-6.0.0a2.dist-info/WHEEL,sha256=GHB6lJx2juba1wDgXDNlMTyM13ckjBMKf-OnwgKOCtA,91
75
- owasp_depscan-6.0.0a2.dist-info/entry_points.txt,sha256=FxQKHFWZTfKU2eBxHPFRxwhSNexntYygYhquykS8zxA,69
76
- owasp_depscan-6.0.0a2.dist-info/top_level.txt,sha256=qbHOZvNU2dXANv946hMdP2vOi0ESQB5t2ZY5ktKtXvQ,15
77
- owasp_depscan-6.0.0a2.dist-info/RECORD,,
72
+ vendor/spdx/json/licenses.json,sha256=6hJD6hFqYZy_oIQ20DchwuLlkZH8FaXFR00vflKb-lk,315992
73
+ owasp_depscan-6.0.0b2.dist-info/METADATA,sha256=7-Wt5PdEpf--5HYTiRrwpQf03FAZltpvj4C4U8XpdAQ,17433
74
+ owasp_depscan-6.0.0b2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
75
+ owasp_depscan-6.0.0b2.dist-info/entry_points.txt,sha256=QvBVhjzm1Vx1CQkACbQWeNykZInIXUFUi6scoOYA7XY,45
76
+ owasp_depscan-6.0.0b2.dist-info/top_level.txt,sha256=qbHOZvNU2dXANv946hMdP2vOi0ESQB5t2ZY5ktKtXvQ,15
77
+ owasp_depscan-6.0.0b2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.3.0)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,3 +1,2 @@
1
1
  [console_scripts]
2
2
  depscan = depscan.cli:main
3
- scan = depscan.cli:main