@intentsolutionsio/penetration-tester 2.0.0 → 3.0.4

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 (112) hide show
  1. package/.claude-plugin/plugin.json +8 -3
  2. package/README.md +8 -0
  3. package/commands/pentest.md +5 -0
  4. package/package.json +8 -3
  5. package/skills/analyzing-tls-config/SKILL.md +221 -0
  6. package/skills/analyzing-tls-config/references/AUTHORIZATION.md +133 -0
  7. package/skills/analyzing-tls-config/references/PLAYBOOK.md +267 -0
  8. package/skills/analyzing-tls-config/references/THEORY.md +128 -0
  9. package/skills/analyzing-tls-config/scripts/analyze_tls.py +415 -0
  10. package/skills/auditing-cors-policy/SKILL.md +186 -0
  11. package/skills/auditing-cors-policy/references/PLAYBOOK.md +220 -0
  12. package/skills/auditing-cors-policy/references/THEORY.md +142 -0
  13. package/skills/auditing-cors-policy/scripts/audit_cors.py +350 -0
  14. package/skills/auditing-npm-dependencies/SKILL.md +254 -0
  15. package/skills/auditing-npm-dependencies/references/PLAYBOOK.md +175 -0
  16. package/skills/auditing-npm-dependencies/references/THEORY.md +122 -0
  17. package/skills/auditing-npm-dependencies/scripts/audit_npm.py +408 -0
  18. package/skills/auditing-python-dependencies/SKILL.md +251 -0
  19. package/skills/auditing-python-dependencies/references/PLAYBOOK.md +193 -0
  20. package/skills/auditing-python-dependencies/references/THEORY.md +122 -0
  21. package/skills/auditing-python-dependencies/scripts/audit_python.py +459 -0
  22. package/skills/checking-http-security-headers/SKILL.md +176 -0
  23. package/skills/checking-http-security-headers/references/PLAYBOOK.md +212 -0
  24. package/skills/checking-http-security-headers/references/THEORY.md +137 -0
  25. package/skills/checking-http-security-headers/scripts/check_headers.py +362 -0
  26. package/skills/checking-license-compliance/SKILL.md +225 -0
  27. package/skills/checking-license-compliance/references/PLAYBOOK.md +161 -0
  28. package/skills/checking-license-compliance/references/THEORY.md +152 -0
  29. package/skills/checking-license-compliance/scripts/check_licenses.py +461 -0
  30. package/skills/composing-vulnerability-report/SKILL.md +212 -0
  31. package/skills/composing-vulnerability-report/references/PLAYBOOK.md +180 -0
  32. package/skills/composing-vulnerability-report/references/THEORY.md +178 -0
  33. package/skills/composing-vulnerability-report/scripts/compose_report.py +396 -0
  34. package/skills/confirming-pentest-authorization/SKILL.md +247 -0
  35. package/skills/confirming-pentest-authorization/references/PLAYBOOK.md +189 -0
  36. package/skills/confirming-pentest-authorization/references/THEORY.md +167 -0
  37. package/skills/confirming-pentest-authorization/scripts/check_authorization.py +457 -0
  38. package/skills/defining-pentest-scope/SKILL.md +227 -0
  39. package/skills/defining-pentest-scope/references/PLAYBOOK.md +238 -0
  40. package/skills/defining-pentest-scope/references/THEORY.md +170 -0
  41. package/skills/defining-pentest-scope/scripts/define_scope.py +472 -0
  42. package/skills/detecting-command-injection-patterns/SKILL.md +144 -0
  43. package/skills/detecting-command-injection-patterns/references/PLAYBOOK.md +302 -0
  44. package/skills/detecting-command-injection-patterns/references/THEORY.md +206 -0
  45. package/skills/detecting-command-injection-patterns/scripts/scan_cmdi.py +290 -0
  46. package/skills/detecting-debug-endpoints/SKILL.md +207 -0
  47. package/skills/detecting-debug-endpoints/references/PLAYBOOK.md +402 -0
  48. package/skills/detecting-debug-endpoints/references/THEORY.md +218 -0
  49. package/skills/detecting-debug-endpoints/scripts/probe_debug.py +518 -0
  50. package/skills/detecting-directory-listing/SKILL.md +206 -0
  51. package/skills/detecting-directory-listing/references/PLAYBOOK.md +277 -0
  52. package/skills/detecting-directory-listing/references/THEORY.md +203 -0
  53. package/skills/detecting-directory-listing/scripts/probe_directory_listing.py +180 -0
  54. package/skills/detecting-eval-exec-usage/SKILL.md +128 -0
  55. package/skills/detecting-eval-exec-usage/references/PLAYBOOK.md +306 -0
  56. package/skills/detecting-eval-exec-usage/references/THEORY.md +159 -0
  57. package/skills/detecting-eval-exec-usage/scripts/scan_eval.py +223 -0
  58. package/skills/detecting-exposed-secrets-files/SKILL.md +179 -0
  59. package/skills/detecting-exposed-secrets-files/references/PLAYBOOK.md +274 -0
  60. package/skills/detecting-exposed-secrets-files/references/THEORY.md +174 -0
  61. package/skills/detecting-exposed-secrets-files/scripts/probe_secrets.py +207 -0
  62. package/skills/detecting-insecure-deserialization/SKILL.md +148 -0
  63. package/skills/detecting-insecure-deserialization/references/PLAYBOOK.md +333 -0
  64. package/skills/detecting-insecure-deserialization/references/THEORY.md +199 -0
  65. package/skills/detecting-insecure-deserialization/scripts/scan_deserialization.py +250 -0
  66. package/skills/detecting-sql-injection-patterns/SKILL.md +161 -0
  67. package/skills/detecting-sql-injection-patterns/references/PLAYBOOK.md +317 -0
  68. package/skills/detecting-sql-injection-patterns/references/THEORY.md +261 -0
  69. package/skills/detecting-sql-injection-patterns/scripts/scan_sqli.py +354 -0
  70. package/skills/detecting-ssl-cert-issues/SKILL.md +182 -0
  71. package/skills/detecting-ssl-cert-issues/references/PLAYBOOK.md +203 -0
  72. package/skills/detecting-ssl-cert-issues/references/THEORY.md +133 -0
  73. package/skills/detecting-ssl-cert-issues/scripts/check_cert_chain.py +481 -0
  74. package/skills/detecting-weak-cryptography/SKILL.md +147 -0
  75. package/skills/detecting-weak-cryptography/references/PLAYBOOK.md +466 -0
  76. package/skills/detecting-weak-cryptography/references/THEORY.md +194 -0
  77. package/skills/detecting-weak-cryptography/scripts/scan_weak_crypto.py +417 -0
  78. package/skills/fingerprinting-server-software/SKILL.md +191 -0
  79. package/skills/fingerprinting-server-software/references/PLAYBOOK.md +337 -0
  80. package/skills/fingerprinting-server-software/references/THEORY.md +183 -0
  81. package/skills/fingerprinting-server-software/scripts/fingerprint_server.py +347 -0
  82. package/skills/generating-executive-summary/SKILL.md +261 -0
  83. package/skills/generating-executive-summary/references/PLAYBOOK.md +201 -0
  84. package/skills/generating-executive-summary/references/THEORY.md +195 -0
  85. package/skills/generating-executive-summary/scripts/exec_summary.py +538 -0
  86. package/skills/mapping-findings-to-owasp-top10/SKILL.md +235 -0
  87. package/skills/mapping-findings-to-owasp-top10/references/PLAYBOOK.md +193 -0
  88. package/skills/mapping-findings-to-owasp-top10/references/THEORY.md +160 -0
  89. package/skills/mapping-findings-to-owasp-top10/scripts/map_owasp.py +540 -0
  90. package/skills/performing-penetration-testing/SKILL.md +282 -190
  91. package/skills/performing-penetration-testing/references/OWASP_TOP_10.md +22 -0
  92. package/skills/performing-penetration-testing/references/REMEDIATION_PLAYBOOK.md +46 -0
  93. package/skills/performing-penetration-testing/references/SECURITY_HEADERS.md +41 -0
  94. package/skills/performing-penetration-testing/scripts/code_security_scanner.py +144 -79
  95. package/skills/performing-penetration-testing/scripts/dependency_auditor.py +116 -93
  96. package/skills/performing-penetration-testing/scripts/security_scanner.py +574 -446
  97. package/skills/probing-dangerous-http-methods/SKILL.md +182 -0
  98. package/skills/probing-dangerous-http-methods/references/PLAYBOOK.md +234 -0
  99. package/skills/probing-dangerous-http-methods/references/THEORY.md +145 -0
  100. package/skills/probing-dangerous-http-methods/scripts/probe_methods.py +263 -0
  101. package/skills/recording-pentest-engagement/SKILL.md +253 -0
  102. package/skills/recording-pentest-engagement/references/PLAYBOOK.md +203 -0
  103. package/skills/recording-pentest-engagement/references/THEORY.md +195 -0
  104. package/skills/recording-pentest-engagement/scripts/record_engagement.py +461 -0
  105. package/skills/scanning-for-hardcoded-secrets/SKILL.md +215 -0
  106. package/skills/scanning-for-hardcoded-secrets/references/PLAYBOOK.md +325 -0
  107. package/skills/scanning-for-hardcoded-secrets/references/THEORY.md +175 -0
  108. package/skills/scanning-for-hardcoded-secrets/scripts/scan_secrets.py +395 -0
  109. package/skills/tracing-transitive-vulnerabilities/SKILL.md +235 -0
  110. package/skills/tracing-transitive-vulnerabilities/references/PLAYBOOK.md +233 -0
  111. package/skills/tracing-transitive-vulnerabilities/references/THEORY.md +138 -0
  112. package/skills/tracing-transitive-vulnerabilities/scripts/trace_vulns.py +484 -0
@@ -70,6 +70,7 @@ def log_error(message: str) -> None:
70
70
  # Project detection
71
71
  # ---------------------------------------------------------------------------
72
72
 
73
+
73
74
  def detect_project_type(directory: Path) -> list[str]:
74
75
  """Auto-detect project ecosystems by scanning for manifest files.
75
76
 
@@ -99,8 +100,7 @@ def detect_project_type(directory: Path) -> list[str]:
99
100
  for ecosystem, manifest in FUTURE_SCANNERS.items():
100
101
  if (directory / manifest).exists():
101
102
  log_warn(
102
- f"Detected {ecosystem} project ({manifest}), but {ecosystem} "
103
- f"scanning is not yet implemented. Skipping."
103
+ f"Detected {ecosystem} project ({manifest}), but {ecosystem} scanning is not yet implemented. Skipping."
104
104
  )
105
105
 
106
106
  return detected
@@ -110,6 +110,7 @@ def detect_project_type(directory: Path) -> list[str]:
110
110
  # npm audit
111
111
  # ---------------------------------------------------------------------------
112
112
 
113
+
113
114
  def run_npm_audit(directory: Path) -> list[dict[str, Any]]:
114
115
  """Run npm audit and parse vulnerability findings.
115
116
 
@@ -128,7 +129,9 @@ def run_npm_audit(directory: Path) -> list[dict[str, Any]]:
128
129
  # Check npm is available
129
130
  npm_check = subprocess.run(
130
131
  ["npm", "--version"],
131
- capture_output=True, text=True, timeout=SUBPROCESS_TIMEOUT,
132
+ capture_output=True,
133
+ text=True,
134
+ timeout=SUBPROCESS_TIMEOUT,
132
135
  )
133
136
  if npm_check.returncode != 0:
134
137
  log_warn("npm is not installed or not in PATH. Skipping npm audit.")
@@ -141,15 +144,13 @@ def run_npm_audit(directory: Path) -> list[dict[str, Any]]:
141
144
 
142
145
  # Warn if node_modules is missing
143
146
  if not (directory / "node_modules").is_dir():
144
- log_warn(
145
- f"No node_modules directory in {directory}. "
146
- f"Run 'npm install' first for accurate audit results."
147
- )
147
+ log_warn(f"No node_modules directory in {directory}. Run 'npm install' first for accurate audit results.")
148
148
 
149
149
  try:
150
150
  result = subprocess.run(
151
151
  ["npm", "audit", "--json"],
152
- capture_output=True, text=True,
152
+ capture_output=True,
153
+ text=True,
153
154
  cwd=str(directory),
154
155
  timeout=SUBPROCESS_TIMEOUT,
155
156
  )
@@ -198,43 +199,49 @@ def _parse_npm_audit_v2(data: dict[str, Any]) -> list[dict[str, Any]]:
198
199
  # Each vulnerability entry may reference multiple advisories via "via"
199
200
  via_entries = vuln_info.get("via", [])
200
201
  if not via_entries:
201
- findings.append({
202
- "scanner": "npm",
203
- "package": pkg_name,
204
- "severity": _normalize_severity(severity),
205
- "title": f"Vulnerability in {pkg_name}",
206
- "detail": f"Severity: {severity}. Check npm audit for details.",
207
- "cve": None,
208
- "installed_version": vuln_info.get("range"),
209
- "fixed_version": fixed_version,
210
- })
211
- continue
212
-
213
- for via in via_entries:
214
- if isinstance(via, str):
215
- # Indirect vulnerability reference (transitive dependency name)
216
- findings.append({
202
+ findings.append(
203
+ {
217
204
  "scanner": "npm",
218
205
  "package": pkg_name,
219
206
  "severity": _normalize_severity(severity),
220
- "title": f"Depends on vulnerable {via}",
221
- "detail": f"{pkg_name} is affected through dependency on {via}.",
207
+ "title": f"Vulnerability in {pkg_name}",
208
+ "detail": f"Severity: {severity}. Check npm audit for details.",
222
209
  "cve": None,
223
210
  "installed_version": vuln_info.get("range"),
224
211
  "fixed_version": fixed_version,
225
- })
212
+ }
213
+ )
214
+ continue
215
+
216
+ for via in via_entries:
217
+ if isinstance(via, str):
218
+ # Indirect vulnerability reference (transitive dependency name)
219
+ findings.append(
220
+ {
221
+ "scanner": "npm",
222
+ "package": pkg_name,
223
+ "severity": _normalize_severity(severity),
224
+ "title": f"Depends on vulnerable {via}",
225
+ "detail": f"{pkg_name} is affected through dependency on {via}.",
226
+ "cve": None,
227
+ "installed_version": vuln_info.get("range"),
228
+ "fixed_version": fixed_version,
229
+ }
230
+ )
226
231
  elif isinstance(via, dict):
227
232
  cve = _extract_cve(via.get("url", ""))
228
- findings.append({
229
- "scanner": "npm",
230
- "package": pkg_name,
231
- "severity": _normalize_severity(via.get("severity", severity)),
232
- "title": via.get("title", f"Vulnerability in {pkg_name}"),
233
- "detail": via.get("title", "No description available."),
234
- "cve": cve,
235
- "installed_version": via.get("range") or vuln_info.get("range"),
236
- "fixed_version": fixed_version,
237
- })
233
+ findings.append(
234
+ {
235
+ "scanner": "npm",
236
+ "package": pkg_name,
237
+ "severity": _normalize_severity(via.get("severity", severity)),
238
+ "title": via.get("title", f"Vulnerability in {pkg_name}"),
239
+ "detail": via.get("title", "No description available."),
240
+ "cve": cve,
241
+ "installed_version": via.get("range") or vuln_info.get("range"),
242
+ "fixed_version": fixed_version,
243
+ }
244
+ )
238
245
 
239
246
  return findings
240
247
 
@@ -250,16 +257,18 @@ def _parse_npm_audit_v1(data: dict[str, Any]) -> list[dict[str, Any]]:
250
257
  for _adv_id, advisory in advisories.items():
251
258
  cve_list = advisory.get("cves", [])
252
259
  cve = cve_list[0] if cve_list else None
253
- findings.append({
254
- "scanner": "npm",
255
- "package": advisory.get("module_name", "unknown"),
256
- "severity": _normalize_severity(advisory.get("severity", "moderate")),
257
- "title": advisory.get("title", "Unknown vulnerability"),
258
- "detail": advisory.get("overview", "No description available."),
259
- "cve": cve,
260
- "installed_version": advisory.get("findings", [{}])[0].get("version"),
261
- "fixed_version": advisory.get("patched_versions"),
262
- })
260
+ findings.append(
261
+ {
262
+ "scanner": "npm",
263
+ "package": advisory.get("module_name", "unknown"),
264
+ "severity": _normalize_severity(advisory.get("severity", "moderate")),
265
+ "title": advisory.get("title", "Unknown vulnerability"),
266
+ "detail": advisory.get("overview", "No description available."),
267
+ "cve": cve,
268
+ "installed_version": advisory.get("findings", [{}])[0].get("version"),
269
+ "fixed_version": advisory.get("patched_versions"),
270
+ }
271
+ )
263
272
 
264
273
  return findings
265
274
 
@@ -268,6 +277,7 @@ def _parse_npm_audit_v1(data: dict[str, Any]) -> list[dict[str, Any]]:
268
277
  # pip-audit
269
278
  # ---------------------------------------------------------------------------
270
279
 
280
+
271
281
  def run_pip_audit(directory: Path) -> list[dict[str, Any]]:
272
282
  """Run pip-audit and parse vulnerability findings.
273
283
 
@@ -291,7 +301,8 @@ def run_pip_audit(directory: Path) -> list[dict[str, Any]]:
291
301
  try:
292
302
  result = subprocess.run(
293
303
  pip_audit_cmd,
294
- capture_output=True, text=True,
304
+ capture_output=True,
305
+ text=True,
295
306
  cwd=str(directory),
296
307
  timeout=SUBPROCESS_TIMEOUT,
297
308
  )
@@ -305,14 +316,16 @@ def run_pip_audit(directory: Path) -> list[dict[str, Any]]:
305
316
  # Try installing pip-audit
306
317
  install_result = subprocess.run(
307
318
  [sys.executable, "-m", "pip", "install", "pip-audit", "--quiet"],
308
- capture_output=True, text=True,
319
+ capture_output=True,
320
+ text=True,
309
321
  timeout=SUBPROCESS_TIMEOUT,
310
322
  )
311
323
  if install_result.returncode == 0:
312
324
  try:
313
325
  result = subprocess.run(
314
326
  pip_audit_cmd,
315
- capture_output=True, text=True,
327
+ capture_output=True,
328
+ text=True,
316
329
  cwd=str(directory),
317
330
  timeout=SUBPROCESS_TIMEOUT,
318
331
  )
@@ -370,16 +383,18 @@ def _parse_pip_audit_output(raw_output: str) -> list[dict[str, Any]]:
370
383
  if isinstance(fix, list):
371
384
  fix = fix[0] if fix else None
372
385
 
373
- findings.append({
374
- "scanner": "pip-audit",
375
- "package": pkg_name,
376
- "severity": _severity_from_vuln_id(vuln_id),
377
- "title": f"{vuln_id}: {pkg_name}" if vuln_id else f"Vulnerability in {pkg_name}",
378
- "detail": description or "No description provided by pip-audit.",
379
- "cve": cve,
380
- "installed_version": installed,
381
- "fixed_version": fix,
382
- })
386
+ findings.append(
387
+ {
388
+ "scanner": "pip-audit",
389
+ "package": pkg_name,
390
+ "severity": _severity_from_vuln_id(vuln_id),
391
+ "title": f"{vuln_id}: {pkg_name}" if vuln_id else f"Vulnerability in {pkg_name}",
392
+ "detail": description or "No description provided by pip-audit.",
393
+ "cve": cve,
394
+ "installed_version": installed,
395
+ "fixed_version": fix,
396
+ }
397
+ )
383
398
 
384
399
  return findings
385
400
 
@@ -390,7 +405,8 @@ def _run_pip_outdated_fallback() -> list[dict[str, Any]]:
390
405
  try:
391
406
  result = subprocess.run(
392
407
  [sys.executable, "-m", "pip", "list", "--outdated", "--format=json"],
393
- capture_output=True, text=True,
408
+ capture_output=True,
409
+ text=True,
394
410
  timeout=SUBPROCESS_TIMEOUT,
395
411
  )
396
412
  except (FileNotFoundError, subprocess.TimeoutExpired):
@@ -406,20 +422,22 @@ def _run_pip_outdated_fallback() -> list[dict[str, Any]]:
406
422
  return findings
407
423
 
408
424
  for entry in data:
409
- findings.append({
410
- "scanner": "pip-audit",
411
- "package": entry.get("name", "unknown"),
412
- "severity": "info",
413
- "title": f"Outdated package: {entry.get('name', 'unknown')}",
414
- "detail": (
415
- f"Installed {entry.get('version', '?')}, "
416
- f"latest {entry.get('latest_version', '?')}. "
417
- f"Outdated packages may contain known vulnerabilities."
418
- ),
419
- "cve": None,
420
- "installed_version": entry.get("version"),
421
- "fixed_version": entry.get("latest_version"),
422
- })
425
+ findings.append(
426
+ {
427
+ "scanner": "pip-audit",
428
+ "package": entry.get("name", "unknown"),
429
+ "severity": "info",
430
+ "title": f"Outdated package: {entry.get('name', 'unknown')}",
431
+ "detail": (
432
+ f"Installed {entry.get('version', '?')}, "
433
+ f"latest {entry.get('latest_version', '?')}. "
434
+ f"Outdated packages may contain known vulnerabilities."
435
+ ),
436
+ "cve": None,
437
+ "installed_version": entry.get("version"),
438
+ "fixed_version": entry.get("latest_version"),
439
+ }
440
+ )
423
441
 
424
442
  return findings
425
443
 
@@ -428,6 +446,7 @@ def _run_pip_outdated_fallback() -> list[dict[str, Any]]:
428
446
  # pip check
429
447
  # ---------------------------------------------------------------------------
430
448
 
449
+
431
450
  def run_pip_check(directory: Path) -> list[dict[str, Any]]:
432
451
  """Run pip check to detect broken dependencies and version conflicts.
433
452
 
@@ -446,7 +465,8 @@ def run_pip_check(directory: Path) -> list[dict[str, Any]]:
446
465
  try:
447
466
  result = subprocess.run(
448
467
  [sys.executable, "-m", "pip", "check"],
449
- capture_output=True, text=True,
468
+ capture_output=True,
469
+ text=True,
450
470
  cwd=str(directory),
451
471
  timeout=SUBPROCESS_TIMEOUT,
452
472
  )
@@ -470,16 +490,18 @@ def run_pip_check(directory: Path) -> list[dict[str, Any]]:
470
490
  pkg_name = parts[0] if parts else "unknown"
471
491
  installed_version = parts[1] if len(parts) > 1 else None
472
492
 
473
- findings.append({
474
- "scanner": "pip-check",
475
- "package": pkg_name,
476
- "severity": "moderate",
477
- "title": f"Dependency conflict: {pkg_name}",
478
- "detail": line,
479
- "cve": None,
480
- "installed_version": installed_version,
481
- "fixed_version": None,
482
- })
493
+ findings.append(
494
+ {
495
+ "scanner": "pip-check",
496
+ "package": pkg_name,
497
+ "severity": "moderate",
498
+ "title": f"Dependency conflict: {pkg_name}",
499
+ "detail": line,
500
+ "cve": None,
501
+ "installed_version": installed_version,
502
+ "fixed_version": None,
503
+ }
504
+ )
483
505
 
484
506
  return findings
485
507
 
@@ -488,6 +510,7 @@ def run_pip_check(directory: Path) -> list[dict[str, Any]]:
488
510
  # Unification and deduplication
489
511
  # ---------------------------------------------------------------------------
490
512
 
513
+
491
514
  def unify_results(findings: list[dict[str, Any]]) -> list[dict[str, Any]]:
492
515
  """Normalize, deduplicate, and sort findings by severity.
493
516
 
@@ -528,6 +551,7 @@ def unify_results(findings: list[dict[str, Any]]) -> list[dict[str, Any]]:
528
551
  # Reporting
529
552
  # ---------------------------------------------------------------------------
530
553
 
554
+
531
555
  def generate_report(
532
556
  directory: Path,
533
557
  findings: list[dict[str, Any]],
@@ -618,6 +642,7 @@ def generate_report(
618
642
  # Helpers
619
643
  # ---------------------------------------------------------------------------
620
644
 
645
+
621
646
  def _normalize_severity(severity: str) -> str:
622
647
  """Normalize a severity string to one of the canonical levels."""
623
648
  s = severity.strip().lower()
@@ -637,6 +662,7 @@ def _normalize_severity(severity: str) -> str:
637
662
  def _extract_cve(text: str) -> Optional[str]:
638
663
  """Extract a CVE identifier from a string, if present."""
639
664
  import re
665
+
640
666
  match = re.search(r"CVE-\d{4}-\d{4,}", text)
641
667
  return match.group(0) if match else None
642
668
 
@@ -668,6 +694,7 @@ def _filter_by_severity(
668
694
  # CLI
669
695
  # ---------------------------------------------------------------------------
670
696
 
697
+
671
698
  def main() -> None:
672
699
  """Entry point for the dependency auditor CLI."""
673
700
  parser = argparse.ArgumentParser(
@@ -726,8 +753,7 @@ def main() -> None:
726
753
 
727
754
  if not ecosystems:
728
755
  log_warn(
729
- f"No supported project types detected in {directory}. "
730
- f"Supported: {', '.join(sorted(SUPPORTED_SCANNERS))}"
756
+ f"No supported project types detected in {directory}. Supported: {', '.join(sorted(SUPPORTED_SCANNERS))}"
731
757
  )
732
758
  sys.exit(0)
733
759
 
@@ -758,8 +784,7 @@ def main() -> None:
758
784
  filtered = _filter_by_severity(unified, args.min_severity)
759
785
 
760
786
  log(
761
- f"Unified: {len(unified)} total, {len(filtered)} at or above "
762
- f"{args.min_severity} severity.",
787
+ f"Unified: {len(unified)} total, {len(filtered)} at or above {args.min_severity} severity.",
763
788
  verbose,
764
789
  )
765
790
 
@@ -767,9 +792,7 @@ def main() -> None:
767
792
  generate_report(directory, filtered, args.output)
768
793
 
769
794
  # Exit code: 1 if any critical or high findings remain after filtering
770
- has_critical_or_high = any(
771
- f["severity"] in ("critical", "high") for f in filtered
772
- )
795
+ has_critical_or_high = any(f["severity"] in ("critical", "high") for f in filtered)
773
796
  sys.exit(1 if has_critical_or_high else 0)
774
797
 
775
798