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.
- {attackmap-0.4.5/src/attackmap.egg-info → attackmap-0.4.6}/PKG-INFO +1 -1
- {attackmap-0.4.5 → attackmap-0.4.6}/pyproject.toml +1 -1
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/__init__.py +1 -1
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/llm_review.py +38 -3
- {attackmap-0.4.5 → attackmap-0.4.6/src/attackmap.egg-info}/PKG-INFO +1 -1
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_llm_review.py +29 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/LICENSE +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/README.md +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/setup.cfg +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/analyzer.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/analyzer_contracts.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/analyzers.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/anomalies.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/asset_model.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/attack_taxonomy.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/authz.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/cli.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/config_scanner.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/context_pack.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/control_model.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/crypto.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/cve.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/defensive_review.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/detection_opportunities.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/diagrams.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/diff.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/exploitability.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/graph.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/insights.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/merge.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/models.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/progress.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/recon_models.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/recon_to_analysis.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/report.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/review_eval.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/review_json.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/review_prompts.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/sarif.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/sbom.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/scanner.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/sdk/__init__.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/sdk/contracts.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/sdk/models.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/security_overlay.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/srcpaths.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/suggest.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/taint.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/threat_model.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/topology.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/weaknesses.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap/webhardening.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap.egg-info/SOURCES.txt +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap.egg-info/dependency_links.txt +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap.egg-info/entry_points.txt +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap.egg-info/requires.txt +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/src/attackmap.egg-info/top_level.txt +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_analyzer.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_analyzer_metadata_schema.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_analyzers.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_anomalies.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_artifact_schemas.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_authz.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_config_scanner.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_context_pack.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_crypto.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_cve.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_defensive_review.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_diagrams.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_diff.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_exploitability.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_graph.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_hunt.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_injection_sinks.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_merge.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_modules_cli.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_pr_comment.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_progress.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_provenance.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_recon_to_analysis.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_remediate.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_report.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_review_eval.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_review_json.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_review_prompts.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_sarif.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_sbom.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_scanner.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_security_overlay.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_shared_contract_imports.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_signal_v2.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_srcpaths.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_suggest.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_taint.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_threat_intel.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_threat_model.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_topology.py +0 -0
- {attackmap-0.4.5 → attackmap-0.4.6}/tests/test_weaknesses.py +0 -0
- {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.
|
|
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.
|
|
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.
|
|
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
|
|
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
|
|
566
|
+
if client is not None:
|
|
543
567
|
return "api"
|
|
544
|
-
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|