attackmap 0.4.5__tar.gz → 0.4.6__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.

Potentially problematic release.


This version of attackmap might be problematic. Click here for more details.

Files changed (99) hide show
  1. {attackmap-0.4.5/src/attackmap.egg-info → attackmap-0.4.6}/PKG-INFO +1 -1
  2. {attackmap-0.4.5 → attackmap-0.4.6}/pyproject.toml +1 -1
  3. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/__init__.py +1 -1
  4. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/llm_review.py +38 -3
  5. {attackmap-0.4.5 → attackmap-0.4.6/src/attackmap.egg-info}/PKG-INFO +1 -1
  6. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_llm_review.py +29 -0
  7. {attackmap-0.4.5 → attackmap-0.4.6}/LICENSE +0 -0
  8. {attackmap-0.4.5 → attackmap-0.4.6}/README.md +0 -0
  9. {attackmap-0.4.5 → attackmap-0.4.6}/setup.cfg +0 -0
  10. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/analyzer.py +0 -0
  11. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/analyzer_contracts.py +0 -0
  12. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/analyzers.py +0 -0
  13. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/anomalies.py +0 -0
  14. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/asset_model.py +0 -0
  15. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/attack_taxonomy.py +0 -0
  16. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/authz.py +0 -0
  17. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/cli.py +0 -0
  18. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/config_scanner.py +0 -0
  19. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/context_pack.py +0 -0
  20. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/control_model.py +0 -0
  21. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/crypto.py +0 -0
  22. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/cve.py +0 -0
  23. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/defensive_review.py +0 -0
  24. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/detection_opportunities.py +0 -0
  25. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/diagrams.py +0 -0
  26. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/diff.py +0 -0
  27. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/exploitability.py +0 -0
  28. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/graph.py +0 -0
  29. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/insights.py +0 -0
  30. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/merge.py +0 -0
  31. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/models.py +0 -0
  32. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/progress.py +0 -0
  33. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/recon_models.py +0 -0
  34. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/recon_to_analysis.py +0 -0
  35. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/report.py +0 -0
  36. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/review_eval.py +0 -0
  37. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/review_json.py +0 -0
  38. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/review_prompts.py +0 -0
  39. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/sarif.py +0 -0
  40. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/sbom.py +0 -0
  41. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/scanner.py +0 -0
  42. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/sdk/__init__.py +0 -0
  43. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/sdk/contracts.py +0 -0
  44. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/sdk/models.py +0 -0
  45. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/security_overlay.py +0 -0
  46. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/srcpaths.py +0 -0
  47. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/suggest.py +0 -0
  48. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/taint.py +0 -0
  49. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/threat_model.py +0 -0
  50. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/topology.py +0 -0
  51. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/weaknesses.py +0 -0
  52. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/webhardening.py +0 -0
  53. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap.egg-info/SOURCES.txt +0 -0
  54. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap.egg-info/dependency_links.txt +0 -0
  55. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap.egg-info/entry_points.txt +0 -0
  56. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap.egg-info/requires.txt +0 -0
  57. {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap.egg-info/top_level.txt +0 -0
  58. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_analyzer.py +0 -0
  59. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_analyzer_metadata_schema.py +0 -0
  60. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_analyzers.py +0 -0
  61. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_anomalies.py +0 -0
  62. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_artifact_schemas.py +0 -0
  63. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_authz.py +0 -0
  64. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_config_scanner.py +0 -0
  65. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_context_pack.py +0 -0
  66. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_crypto.py +0 -0
  67. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_cve.py +0 -0
  68. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_defensive_review.py +0 -0
  69. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_diagrams.py +0 -0
  70. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_diff.py +0 -0
  71. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_exploitability.py +0 -0
  72. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_graph.py +0 -0
  73. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_hunt.py +0 -0
  74. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_injection_sinks.py +0 -0
  75. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_merge.py +0 -0
  76. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_modules_cli.py +0 -0
  77. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_pr_comment.py +0 -0
  78. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_progress.py +0 -0
  79. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_provenance.py +0 -0
  80. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_recon_to_analysis.py +0 -0
  81. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_remediate.py +0 -0
  82. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_report.py +0 -0
  83. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_review_eval.py +0 -0
  84. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_review_json.py +0 -0
  85. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_review_prompts.py +0 -0
  86. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_sarif.py +0 -0
  87. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_sbom.py +0 -0
  88. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_scanner.py +0 -0
  89. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_security_overlay.py +0 -0
  90. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_shared_contract_imports.py +0 -0
  91. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_signal_v2.py +0 -0
  92. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_srcpaths.py +0 -0
  93. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_suggest.py +0 -0
  94. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_taint.py +0 -0
  95. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_threat_intel.py +0 -0
  96. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_threat_model.py +0 -0
  97. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_topology.py +0 -0
  98. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_weaknesses.py +0 -0
  99. {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_webhardening.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: attackmap
3
- Version: 0.4.5
3
+ Version: 0.4.6
4
4
  Summary: AI-assisted defensive security analyzer for codebases — scans a repository, models assets and controls, finds cross-cutting weaknesses, and generates an evidence-grounded review with MITRE ATT&CK mappings and detection-engineering hints.
5
5
  Author: AttackMap Contributors
6
6
  Author-email: Matthew Davis <matthewd@matthewd.xyz>
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "attackmap"
7
- version = "0.4.5"
7
+ version = "0.4.6"
8
8
  description = "AI-assisted defensive security analyzer for codebases — scans a repository, models assets and controls, finds cross-cutting weaknesses, and generates an evidence-grounded review with MITRE ATT&CK mappings and detection-engineering hints."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -1,2 +1,2 @@
1
1
  __all__ = ["__version__"]
2
- __version__ = "0.4.5"
2
+ __version__ = "0.4.6"
@@ -197,6 +197,14 @@ def _claude_cli_available() -> bool:
197
197
  return shutil.which("claude") is not None
198
198
 
199
199
 
200
+ def _anthropic_sdk_available() -> bool:
201
+ try:
202
+ import anthropic # noqa: F401
203
+ except ImportError:
204
+ return False
205
+ return True
206
+
207
+
200
208
  def _run_via_claude_cli(
201
209
  rendered_system: str,
202
210
  rendered_user: str,
@@ -412,6 +420,14 @@ def _codex_cli_available() -> bool:
412
420
  return shutil.which("codex") is not None
413
421
 
414
422
 
423
+ def _openai_sdk_available() -> bool:
424
+ try:
425
+ import openai # noqa: F401
426
+ except ImportError:
427
+ return False
428
+ return True
429
+
430
+
415
431
  def _run_via_codex_cli(
416
432
  rendered_system: str,
417
433
  rendered_user: str,
@@ -514,10 +530,18 @@ def _resolve_openai_backend(
514
530
  )
515
531
  return "cli"
516
532
  # auto
517
- if client is not None or api_key or os.environ.get("OPENAI_API_KEY"):
533
+ if client is not None:
534
+ return "api"
535
+ has_key = bool(api_key or os.environ.get("OPENAI_API_KEY"))
536
+ # Prefer the API backend only when the openai SDK is importable; otherwise
537
+ # fall back to the `codex` CLI (needs no SDK) so a key set against an
538
+ # SDK-less install still works via subscription auth.
539
+ if has_key and _openai_sdk_available():
518
540
  return "api"
519
541
  if _codex_cli_available():
520
542
  return "cli"
543
+ if has_key:
544
+ return "api" # no SDK and no CLI — surface the actionable SDK-missing error
521
545
  raise LlmReviewError(
522
546
  "No OpenAI backend available. Set OPENAI_API_KEY, or install the `codex` "
523
547
  "CLI and run `codex login`."
@@ -539,12 +563,23 @@ def _resolve_backend(
539
563
  )
540
564
  return "cli"
541
565
  # auto
542
- if client is not None or api_key:
566
+ if client is not None:
543
567
  return "api"
544
- if os.environ.get("ANTHROPIC_API_KEY") or os.environ.get("ANTHROPIC_AUTH_TOKEN"):
568
+ has_key = bool(
569
+ api_key
570
+ or os.environ.get("ANTHROPIC_API_KEY")
571
+ or os.environ.get("ANTHROPIC_AUTH_TOKEN")
572
+ )
573
+ # Prefer the API backend only when the SDK is actually importable. Otherwise
574
+ # fall back to the `claude` CLI (needs no SDK), so a key set against an
575
+ # SDK-less install (e.g. Homebrew, which doesn't vendor `attackmap[llm]`)
576
+ # still works via subscription auth instead of erroring.
577
+ if has_key and _anthropic_sdk_available():
545
578
  return "api"
546
579
  if _claude_cli_available():
547
580
  return "cli"
581
+ if has_key:
582
+ return "api" # no SDK and no CLI — surface the actionable SDK-missing error
548
583
  raise LlmReviewError(
549
584
  "No LLM backend available. Set ANTHROPIC_API_KEY, set ANTHROPIC_AUTH_TOKEN, "
550
585
  "or install the `claude` CLI and run `claude login`."
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: attackmap
3
- Version: 0.4.5
3
+ Version: 0.4.6
4
4
  Summary: AI-assisted defensive security analyzer for codebases — scans a repository, models assets and controls, finds cross-cutting weaknesses, and generates an evidence-grounded review with MITRE ATT&CK mappings and detection-engineering hints.
5
5
  Author: AttackMap Contributors
6
6
  Author-email: Matthew Davis <matthewd@matthewd.xyz>
@@ -424,6 +424,35 @@ def test_auto_backend_falls_back_to_cli_when_no_creds(monkeypatch) -> None:
424
424
  assert result.backend == "cli"
425
425
 
426
426
 
427
+ def test_auto_backend_falls_back_to_cli_when_key_set_but_sdk_missing(monkeypatch) -> None:
428
+ # Homebrew installs attackmap without the anthropic SDK. A key set in that
429
+ # environment must fall back to the `claude` CLI, not error.
430
+ monkeypatch.setenv("ANTHROPIC_API_KEY", "sk-ant-test")
431
+ monkeypatch.setattr("attackmap.llm_review._anthropic_sdk_available", lambda: False)
432
+ monkeypatch.setattr("attackmap.llm_review._claude_cli_available", lambda: True)
433
+
434
+ payload = {"type": "result", "is_error": False, "result": "hi"}
435
+ runner, _ = _make_cli_runner(json.dumps(payload))
436
+
437
+ result = generate_llm_review(
438
+ _trivial_scan(), _surfaces(), _findings(), [], backend="auto", cli_runner=runner
439
+ )
440
+ assert result.backend == "cli"
441
+
442
+
443
+ def test_openai_auto_falls_back_to_codex_when_key_set_but_sdk_missing(monkeypatch) -> None:
444
+ monkeypatch.setenv("OPENAI_API_KEY", "sk-openai-test")
445
+ monkeypatch.setattr("attackmap.llm_review._openai_sdk_available", lambda: False)
446
+ monkeypatch.setattr("attackmap.llm_review._codex_cli_available", lambda: True)
447
+ runner, _ = _make_cli_runner("hi")
448
+
449
+ result = generate_llm_review(
450
+ _trivial_scan(), _surfaces(), _findings(), [],
451
+ provider="openai", backend="auto", codex_runner=runner,
452
+ )
453
+ assert result.backend == "cli"
454
+
455
+
427
456
  def test_auto_backend_raises_when_no_creds_and_no_cli(monkeypatch) -> None:
428
457
  monkeypatch.delenv("ANTHROPIC_API_KEY", raising=False)
429
458
  monkeypatch.delenv("ANTHROPIC_AUTH_TOKEN", raising=False)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes