attackmap 0.4.25__tar.gz → 0.4.26__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.25/src/attackmap.egg-info → attackmap-0.4.26}/PKG-INFO +27 -9
- {attackmap-0.4.25 → attackmap-0.4.26}/README.md +26 -8
- {attackmap-0.4.25 → attackmap-0.4.26}/pyproject.toml +1 -1
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/lockfiles.py +21 -4
- {attackmap-0.4.25 → attackmap-0.4.26/src/attackmap.egg-info}/PKG-INFO +27 -9
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_lockfiles.py +54 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/LICENSE +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/setup.cfg +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/__init__.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/analyzer.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/analyzer_contracts.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/analyzers.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/anomalies.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/asset_model.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/attack_taxonomy.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/authz.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/cli.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/config_scanner.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/context_pack.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/contracts.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/control_model.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/crossrepo.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/crypto.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/cve.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/defensive_review.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/detection_opportunities.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/diagrams.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/diff.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/exploitability.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/fleet.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/graph.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/hunt_harness.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/insights.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/llm_review.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/merge.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/models.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/progress.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/recon_models.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/recon_to_analysis.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/report.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/review_eval.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/review_json.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/review_prompts.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/route_auth_fusion.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/sarif.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/sbom.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/scanner.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/sdk/__init__.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/sdk/contracts.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/sdk/models.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/security_overlay.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/srcpaths.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/suggest.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/suppress.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/taint.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/threat_model.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/topology.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/triage.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/weaknesses.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/webhardening.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap/workflow_scanner.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap.egg-info/SOURCES.txt +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap.egg-info/dependency_links.txt +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap.egg-info/entry_points.txt +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap.egg-info/requires.txt +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/src/attackmap.egg-info/top_level.txt +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_analyzer.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_analyzer_metadata_schema.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_analyzers.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_anomalies.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_artifact_schemas.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_authz.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_config_scanner.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_context_pack.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_contracts.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_crossrepo.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_crypto.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_cve.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_defensive_review.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_diagrams.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_diff.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_exploitability.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_fleet.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_graph.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_hunt.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_hunt_harness.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_injection_sinks.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_llm_review.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_merge.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_modules_cli.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_pr_comment.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_progress.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_provenance.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_recall.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_recon_to_analysis.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_remediate.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_report.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_review_eval.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_review_json.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_review_prompts.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_route_auth_fusion.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_sarif.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_sbom.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_scanner.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_security_overlay.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_shared_contract_imports.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_signal_v2.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_srcpaths.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_suggest.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_suppress.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_taint.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_taint_sanitizer.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_threat_intel.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_threat_model.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_topology.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_triage.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_weaknesses.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_webhardening.py +0 -0
- {attackmap-0.4.25 → attackmap-0.4.26}/tests/test_workflow_scanner.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.26
|
|
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>
|
|
@@ -790,10 +790,12 @@ scaffolding, testing, and publishing instructions is in
|
|
|
790
790
|
|
|
791
791
|
```bash
|
|
792
792
|
attackmap analyze <path> # run a review on a repository
|
|
793
|
+
attackmap analyze repoA repoB gateway # multi-repo fleet scan (per-repo reports + fleet summary)
|
|
793
794
|
attackmap analyze <path> --output dir # write outputs to `dir/`
|
|
794
795
|
attackmap analyze <path> --format json # json | markdown | all (default)
|
|
795
796
|
attackmap analyze <path> --module python --module rust # only these analyzers
|
|
796
797
|
attackmap analyze <path> --cve # cross-reference SBOM against OSV.dev
|
|
798
|
+
attackmap analyze <path> --recall # widen taint discovery (deeper hops, capability-reach); extra reach marked SPECULATIVE
|
|
797
799
|
attackmap analyze <path> --llm # add LLM narrative (auto-resolve auth)
|
|
798
800
|
attackmap analyze <path> --llm --llm-backend cli # force Claude CLI
|
|
799
801
|
attackmap analyze <path> --llm --llm-provider openai # use OpenAI/Codex (OPENAI_API_KEY or `codex` CLI)
|
|
@@ -803,6 +805,8 @@ attackmap analyze <path> --hunt --verify # adjudicate each lead vs. source (conf
|
|
|
803
805
|
attackmap analyze <path> --hunt --verify --verify-votes 3 # majority vote of N independent skeptics (default 3; 1 = single pass)
|
|
804
806
|
attackmap analyze <path> --hunt --verify --hunt-lenses 4 # N lens-specialised generation passes, deduped, then verified
|
|
805
807
|
attackmap analyze <path> --hunt --verify --hunt-rounds 4 # loop-until-dry generation (completeness critic seeds each round)
|
|
808
|
+
attackmap analyze <path> --hunt --verify --hunt-budget 500000 # cap total hunt output tokens across rounds
|
|
809
|
+
attackmap analyze <path> --recall --hunt --verify # widen the net, then adjudicate the speculative reach
|
|
806
810
|
attackmap analyze <path> --remediate # LLM review-first fix suggestions (remediation.md)
|
|
807
811
|
attackmap analyze <path> --triage # cluster/dedupe/rank existing findings (triage.md; deterministic fallback)
|
|
808
812
|
attackmap analyze <path> --pr-comment pr.md # Markdown PR summary comment for CI
|
|
@@ -849,7 +853,7 @@ introduces a new HIGH finding.
|
|
|
849
853
|
|
|
850
854
|
## Project status
|
|
851
855
|
|
|
852
|
-
AttackMap is **beta** (v0.4.
|
|
856
|
+
AttackMap is **beta** (v0.4.25) — published and validated on real codebases, but
|
|
853
857
|
pre-1.0 and heuristic.
|
|
854
858
|
|
|
855
859
|
**Solid today:**
|
|
@@ -867,13 +871,27 @@ pre-1.0 and heuristic.
|
|
|
867
871
|
- BOLA/IDOR authorization detection (with custom-middleware / guard-arg awareness).
|
|
868
872
|
- Insecure-crypto / weak-randomness (Python/JS/Go/PHP) and web-hardening
|
|
869
873
|
(CORS/CSRF/cookies/CSP/debug) detection.
|
|
870
|
-
- Anomaly / outlier detection
|
|
871
|
-
|
|
872
|
-
and
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
874
|
+
- Anomaly / outlier detection — including **signature-free invariant mining**
|
|
875
|
+
(flag the handler that reaches a dangerous sink without the guard its peers
|
|
876
|
+
apply) — and **exploitability fusion**, a deterministic, explainable 0–100
|
|
877
|
+
"exploitable now" score that ranks route→sink combinations and folds in
|
|
878
|
+
known-vulnerable dependencies on the path.
|
|
879
|
+
- **`--hunt`** (LLM exploit-chain hypotheses) with a **verify jury** —
|
|
880
|
+
**`--verify`** adjudicates each lead CONFIRMED/REFUTED/NEEDS-REVIEW against the
|
|
881
|
+
actual source; **`--verify-votes`** runs N independent skeptics, **`--hunt-lenses`**
|
|
882
|
+
fans generation across failure-mode specialists, **`--hunt-rounds`** loops
|
|
883
|
+
until dry with a completeness critic, **`--hunt-budget`** caps token spend —
|
|
884
|
+
plus **`--remediate`** (review-first fixes) and **`--triage`**
|
|
885
|
+
(cluster/dedupe/rank existing findings, deterministic fallback).
|
|
886
|
+
- **`--recall`** — verifier-gated aggressive discovery: widens the taint walk
|
|
887
|
+
(deeper hops, capability-reach enumeration of every reach to exec/fs/net/
|
|
888
|
+
deserialize/template/SQL) and marks the extra reach SPECULATIVE, kept out of
|
|
889
|
+
`--fail-on-new-high` and left for `--hunt --verify` to adjudicate.
|
|
890
|
+
- **Cross-repo / fleet analysis** — `attackmap analyze repoA repoB …` scans a
|
|
891
|
+
fleet, links one repo's outbound HTTP calls to another's routes, and surfaces
|
|
892
|
+
bugs that live in the seams: cross-boundary (confused-deputy) flows,
|
|
893
|
+
trust-assumption gaps, and the sibling service that omits a control its peers
|
|
894
|
+
enforce. All cross-repo findings are speculative until verified.
|
|
877
895
|
- SBOM inventory (5 ecosystems) + OSV.dev CVE cross-reference (`--cve`).
|
|
878
896
|
- Output: Markdown + JSON + **SARIF 2.1.0** + **Mermaid / Graphviz** diagrams;
|
|
879
897
|
**diff/baseline** PR gating; a reusable **GitHub Action + PR bot**; optional
|
|
@@ -729,10 +729,12 @@ scaffolding, testing, and publishing instructions is in
|
|
|
729
729
|
|
|
730
730
|
```bash
|
|
731
731
|
attackmap analyze <path> # run a review on a repository
|
|
732
|
+
attackmap analyze repoA repoB gateway # multi-repo fleet scan (per-repo reports + fleet summary)
|
|
732
733
|
attackmap analyze <path> --output dir # write outputs to `dir/`
|
|
733
734
|
attackmap analyze <path> --format json # json | markdown | all (default)
|
|
734
735
|
attackmap analyze <path> --module python --module rust # only these analyzers
|
|
735
736
|
attackmap analyze <path> --cve # cross-reference SBOM against OSV.dev
|
|
737
|
+
attackmap analyze <path> --recall # widen taint discovery (deeper hops, capability-reach); extra reach marked SPECULATIVE
|
|
736
738
|
attackmap analyze <path> --llm # add LLM narrative (auto-resolve auth)
|
|
737
739
|
attackmap analyze <path> --llm --llm-backend cli # force Claude CLI
|
|
738
740
|
attackmap analyze <path> --llm --llm-provider openai # use OpenAI/Codex (OPENAI_API_KEY or `codex` CLI)
|
|
@@ -742,6 +744,8 @@ attackmap analyze <path> --hunt --verify # adjudicate each lead vs. source (conf
|
|
|
742
744
|
attackmap analyze <path> --hunt --verify --verify-votes 3 # majority vote of N independent skeptics (default 3; 1 = single pass)
|
|
743
745
|
attackmap analyze <path> --hunt --verify --hunt-lenses 4 # N lens-specialised generation passes, deduped, then verified
|
|
744
746
|
attackmap analyze <path> --hunt --verify --hunt-rounds 4 # loop-until-dry generation (completeness critic seeds each round)
|
|
747
|
+
attackmap analyze <path> --hunt --verify --hunt-budget 500000 # cap total hunt output tokens across rounds
|
|
748
|
+
attackmap analyze <path> --recall --hunt --verify # widen the net, then adjudicate the speculative reach
|
|
745
749
|
attackmap analyze <path> --remediate # LLM review-first fix suggestions (remediation.md)
|
|
746
750
|
attackmap analyze <path> --triage # cluster/dedupe/rank existing findings (triage.md; deterministic fallback)
|
|
747
751
|
attackmap analyze <path> --pr-comment pr.md # Markdown PR summary comment for CI
|
|
@@ -788,7 +792,7 @@ introduces a new HIGH finding.
|
|
|
788
792
|
|
|
789
793
|
## Project status
|
|
790
794
|
|
|
791
|
-
AttackMap is **beta** (v0.4.
|
|
795
|
+
AttackMap is **beta** (v0.4.25) — published and validated on real codebases, but
|
|
792
796
|
pre-1.0 and heuristic.
|
|
793
797
|
|
|
794
798
|
**Solid today:**
|
|
@@ -806,13 +810,27 @@ pre-1.0 and heuristic.
|
|
|
806
810
|
- BOLA/IDOR authorization detection (with custom-middleware / guard-arg awareness).
|
|
807
811
|
- Insecure-crypto / weak-randomness (Python/JS/Go/PHP) and web-hardening
|
|
808
812
|
(CORS/CSRF/cookies/CSP/debug) detection.
|
|
809
|
-
- Anomaly / outlier detection
|
|
810
|
-
|
|
811
|
-
and
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
813
|
+
- Anomaly / outlier detection — including **signature-free invariant mining**
|
|
814
|
+
(flag the handler that reaches a dangerous sink without the guard its peers
|
|
815
|
+
apply) — and **exploitability fusion**, a deterministic, explainable 0–100
|
|
816
|
+
"exploitable now" score that ranks route→sink combinations and folds in
|
|
817
|
+
known-vulnerable dependencies on the path.
|
|
818
|
+
- **`--hunt`** (LLM exploit-chain hypotheses) with a **verify jury** —
|
|
819
|
+
**`--verify`** adjudicates each lead CONFIRMED/REFUTED/NEEDS-REVIEW against the
|
|
820
|
+
actual source; **`--verify-votes`** runs N independent skeptics, **`--hunt-lenses`**
|
|
821
|
+
fans generation across failure-mode specialists, **`--hunt-rounds`** loops
|
|
822
|
+
until dry with a completeness critic, **`--hunt-budget`** caps token spend —
|
|
823
|
+
plus **`--remediate`** (review-first fixes) and **`--triage`**
|
|
824
|
+
(cluster/dedupe/rank existing findings, deterministic fallback).
|
|
825
|
+
- **`--recall`** — verifier-gated aggressive discovery: widens the taint walk
|
|
826
|
+
(deeper hops, capability-reach enumeration of every reach to exec/fs/net/
|
|
827
|
+
deserialize/template/SQL) and marks the extra reach SPECULATIVE, kept out of
|
|
828
|
+
`--fail-on-new-high` and left for `--hunt --verify` to adjudicate.
|
|
829
|
+
- **Cross-repo / fleet analysis** — `attackmap analyze repoA repoB …` scans a
|
|
830
|
+
fleet, links one repo's outbound HTTP calls to another's routes, and surfaces
|
|
831
|
+
bugs that live in the seams: cross-boundary (confused-deputy) flows,
|
|
832
|
+
trust-assumption gaps, and the sibling service that omits a control its peers
|
|
833
|
+
enforce. All cross-repo findings are speculative until verified.
|
|
816
834
|
- SBOM inventory (5 ecosystems) + OSV.dev CVE cross-reference (`--cve`).
|
|
817
835
|
- Output: Markdown + JSON + **SARIF 2.1.0** + **Mermaid / Graphviz** diagrams;
|
|
818
836
|
**diff/baseline** PR gating; a reusable **GitHub Action + PR bot**; optional
|
|
@@ -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.26"
|
|
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"
|
|
@@ -263,11 +263,29 @@ def _parse_pnpm_lock(path: Path) -> _Graph | None:
|
|
|
263
263
|
import yaml
|
|
264
264
|
except ImportError: # pragma: no cover - PyYAML is a declared dependency
|
|
265
265
|
return None
|
|
266
|
-
|
|
267
|
-
|
|
266
|
+
# pnpm-lock is normally a single YAML document, but some files concatenate
|
|
267
|
+
# several with `---` separators (e.g. a merge artifact, or a tool that
|
|
268
|
+
# appends per-workspace lockfiles). Parse every document and merge them, and
|
|
269
|
+
# never let a malformed-YAML error abort the whole scan — a lockfile we can't
|
|
270
|
+
# read just yields no dependency hints.
|
|
271
|
+
try:
|
|
272
|
+
documents = [
|
|
273
|
+
doc
|
|
274
|
+
for doc in yaml.safe_load_all(path.read_text(encoding="utf-8"))
|
|
275
|
+
if isinstance(doc, dict)
|
|
276
|
+
]
|
|
277
|
+
except yaml.YAMLError:
|
|
278
|
+
return None
|
|
279
|
+
if not documents:
|
|
268
280
|
return None
|
|
269
281
|
graph = _Graph(ecosystem="npm")
|
|
282
|
+
for data in documents:
|
|
283
|
+
_absorb_pnpm_document(data, graph)
|
|
284
|
+
return graph
|
|
270
285
|
|
|
286
|
+
|
|
287
|
+
def _absorb_pnpm_document(data: dict, graph: _Graph) -> None:
|
|
288
|
+
"""Fold one parsed pnpm-lock YAML document into ``graph`` (roots + packages)."""
|
|
271
289
|
# Roots: importers.'.'.{dependencies,devDependencies} (v6+) or top-level.
|
|
272
290
|
importers = data.get("importers")
|
|
273
291
|
root_sections = []
|
|
@@ -285,7 +303,7 @@ def _parse_pnpm_lock(path: Path) -> _Graph | None:
|
|
|
285
303
|
|
|
286
304
|
packages = data.get("packages")
|
|
287
305
|
if not isinstance(packages, dict):
|
|
288
|
-
return
|
|
306
|
+
return
|
|
289
307
|
for raw_key, meta in packages.items():
|
|
290
308
|
m = _PNPM_PKG_KEY_RE.match(str(raw_key))
|
|
291
309
|
if not m:
|
|
@@ -302,7 +320,6 @@ def _parse_pnpm_lock(path: Path) -> _Graph | None:
|
|
|
302
320
|
children.update(deps.keys())
|
|
303
321
|
if children:
|
|
304
322
|
graph.edges.setdefault(name, set()).update(children)
|
|
305
|
-
return graph
|
|
306
323
|
|
|
307
324
|
|
|
308
325
|
# --- pypi: poetry.lock / uv.lock; cargo: Cargo.lock (all TOML [[package]]) --
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: attackmap
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.26
|
|
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>
|
|
@@ -790,10 +790,12 @@ scaffolding, testing, and publishing instructions is in
|
|
|
790
790
|
|
|
791
791
|
```bash
|
|
792
792
|
attackmap analyze <path> # run a review on a repository
|
|
793
|
+
attackmap analyze repoA repoB gateway # multi-repo fleet scan (per-repo reports + fleet summary)
|
|
793
794
|
attackmap analyze <path> --output dir # write outputs to `dir/`
|
|
794
795
|
attackmap analyze <path> --format json # json | markdown | all (default)
|
|
795
796
|
attackmap analyze <path> --module python --module rust # only these analyzers
|
|
796
797
|
attackmap analyze <path> --cve # cross-reference SBOM against OSV.dev
|
|
798
|
+
attackmap analyze <path> --recall # widen taint discovery (deeper hops, capability-reach); extra reach marked SPECULATIVE
|
|
797
799
|
attackmap analyze <path> --llm # add LLM narrative (auto-resolve auth)
|
|
798
800
|
attackmap analyze <path> --llm --llm-backend cli # force Claude CLI
|
|
799
801
|
attackmap analyze <path> --llm --llm-provider openai # use OpenAI/Codex (OPENAI_API_KEY or `codex` CLI)
|
|
@@ -803,6 +805,8 @@ attackmap analyze <path> --hunt --verify # adjudicate each lead vs. source (conf
|
|
|
803
805
|
attackmap analyze <path> --hunt --verify --verify-votes 3 # majority vote of N independent skeptics (default 3; 1 = single pass)
|
|
804
806
|
attackmap analyze <path> --hunt --verify --hunt-lenses 4 # N lens-specialised generation passes, deduped, then verified
|
|
805
807
|
attackmap analyze <path> --hunt --verify --hunt-rounds 4 # loop-until-dry generation (completeness critic seeds each round)
|
|
808
|
+
attackmap analyze <path> --hunt --verify --hunt-budget 500000 # cap total hunt output tokens across rounds
|
|
809
|
+
attackmap analyze <path> --recall --hunt --verify # widen the net, then adjudicate the speculative reach
|
|
806
810
|
attackmap analyze <path> --remediate # LLM review-first fix suggestions (remediation.md)
|
|
807
811
|
attackmap analyze <path> --triage # cluster/dedupe/rank existing findings (triage.md; deterministic fallback)
|
|
808
812
|
attackmap analyze <path> --pr-comment pr.md # Markdown PR summary comment for CI
|
|
@@ -849,7 +853,7 @@ introduces a new HIGH finding.
|
|
|
849
853
|
|
|
850
854
|
## Project status
|
|
851
855
|
|
|
852
|
-
AttackMap is **beta** (v0.4.
|
|
856
|
+
AttackMap is **beta** (v0.4.25) — published and validated on real codebases, but
|
|
853
857
|
pre-1.0 and heuristic.
|
|
854
858
|
|
|
855
859
|
**Solid today:**
|
|
@@ -867,13 +871,27 @@ pre-1.0 and heuristic.
|
|
|
867
871
|
- BOLA/IDOR authorization detection (with custom-middleware / guard-arg awareness).
|
|
868
872
|
- Insecure-crypto / weak-randomness (Python/JS/Go/PHP) and web-hardening
|
|
869
873
|
(CORS/CSRF/cookies/CSP/debug) detection.
|
|
870
|
-
- Anomaly / outlier detection
|
|
871
|
-
|
|
872
|
-
and
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
874
|
+
- Anomaly / outlier detection — including **signature-free invariant mining**
|
|
875
|
+
(flag the handler that reaches a dangerous sink without the guard its peers
|
|
876
|
+
apply) — and **exploitability fusion**, a deterministic, explainable 0–100
|
|
877
|
+
"exploitable now" score that ranks route→sink combinations and folds in
|
|
878
|
+
known-vulnerable dependencies on the path.
|
|
879
|
+
- **`--hunt`** (LLM exploit-chain hypotheses) with a **verify jury** —
|
|
880
|
+
**`--verify`** adjudicates each lead CONFIRMED/REFUTED/NEEDS-REVIEW against the
|
|
881
|
+
actual source; **`--verify-votes`** runs N independent skeptics, **`--hunt-lenses`**
|
|
882
|
+
fans generation across failure-mode specialists, **`--hunt-rounds`** loops
|
|
883
|
+
until dry with a completeness critic, **`--hunt-budget`** caps token spend —
|
|
884
|
+
plus **`--remediate`** (review-first fixes) and **`--triage`**
|
|
885
|
+
(cluster/dedupe/rank existing findings, deterministic fallback).
|
|
886
|
+
- **`--recall`** — verifier-gated aggressive discovery: widens the taint walk
|
|
887
|
+
(deeper hops, capability-reach enumeration of every reach to exec/fs/net/
|
|
888
|
+
deserialize/template/SQL) and marks the extra reach SPECULATIVE, kept out of
|
|
889
|
+
`--fail-on-new-high` and left for `--hunt --verify` to adjudicate.
|
|
890
|
+
- **Cross-repo / fleet analysis** — `attackmap analyze repoA repoB …` scans a
|
|
891
|
+
fleet, links one repo's outbound HTTP calls to another's routes, and surfaces
|
|
892
|
+
bugs that live in the seams: cross-boundary (confused-deputy) flows,
|
|
893
|
+
trust-assumption gaps, and the sibling service that omits a control its peers
|
|
894
|
+
enforce. All cross-repo findings are speculative until verified.
|
|
877
895
|
- SBOM inventory (5 ecosystems) + OSV.dev CVE cross-reference (`--cve`).
|
|
878
896
|
- Output: Markdown + JSON + **SARIF 2.1.0** + **Mermaid / Graphviz** diagrams;
|
|
879
897
|
**diff/baseline** PR gating; a reusable **GitHub Action + PR bot**; optional
|
|
@@ -211,6 +211,60 @@ packages:
|
|
|
211
211
|
assert by_name["minimist"].version == "1.2.5"
|
|
212
212
|
|
|
213
213
|
|
|
214
|
+
def test_pnpm_lock_multi_document_merges_all(tmp_path: Path) -> None:
|
|
215
|
+
# Some pnpm-lock.yaml files concatenate several YAML documents with `---`
|
|
216
|
+
# separators (seen in the wild, e.g. bluesky-social/atproto). A single-doc
|
|
217
|
+
# load raises yaml.ComposerError and previously aborted the whole scan
|
|
218
|
+
# (regression). We must parse every document and merge them.
|
|
219
|
+
(tmp_path / "pnpm-lock.yaml").write_text(
|
|
220
|
+
"""---
|
|
221
|
+
lockfileVersion: '9.0'
|
|
222
|
+
importers:
|
|
223
|
+
.:
|
|
224
|
+
dependencies:
|
|
225
|
+
lodash:
|
|
226
|
+
specifier: ^4.17.0
|
|
227
|
+
version: 4.17.21
|
|
228
|
+
packages:
|
|
229
|
+
lodash@4.17.21:
|
|
230
|
+
resolution: {integrity: sha512-abc}
|
|
231
|
+
---
|
|
232
|
+
lockfileVersion: '9.0'
|
|
233
|
+
importers:
|
|
234
|
+
.:
|
|
235
|
+
dependencies:
|
|
236
|
+
express:
|
|
237
|
+
specifier: ^4.18.0
|
|
238
|
+
version: 4.18.2
|
|
239
|
+
packages:
|
|
240
|
+
express@4.18.2:
|
|
241
|
+
resolution: {integrity: sha512-def}
|
|
242
|
+
""",
|
|
243
|
+
encoding="utf-8",
|
|
244
|
+
)
|
|
245
|
+
hints, superseded = parse_lockfiles(tmp_path)
|
|
246
|
+
assert ("npm", ".") in superseded
|
|
247
|
+
by_name = _by_name(hints)
|
|
248
|
+
# Both documents contribute their packages and roots.
|
|
249
|
+
assert by_name["lodash"].version == "4.17.21"
|
|
250
|
+
assert by_name["express"].version == "4.18.2"
|
|
251
|
+
assert by_name["lodash"].direct is True
|
|
252
|
+
assert by_name["express"].direct is True
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def test_pnpm_lock_malformed_yaml_yields_no_hints_without_crashing(tmp_path: Path) -> None:
|
|
256
|
+
# A lockfile we can't parse must never abort the scan — it just yields
|
|
257
|
+
# no dependency hints.
|
|
258
|
+
(tmp_path / "pnpm-lock.yaml").write_text(
|
|
259
|
+
"lockfileVersion: '9.0'\npackages:\n - : : broken\n bad indent\n",
|
|
260
|
+
encoding="utf-8",
|
|
261
|
+
)
|
|
262
|
+
# The key property: the call returns (a list) instead of raising a
|
|
263
|
+
# yaml.YAMLError that would abort the scan.
|
|
264
|
+
hints, superseded = parse_lockfiles(tmp_path)
|
|
265
|
+
assert isinstance(hints, list)
|
|
266
|
+
|
|
267
|
+
|
|
214
268
|
def test_uv_local_project_excluded_and_deps_direct(tmp_path: Path) -> None:
|
|
215
269
|
# The project package (editable source) is not an SBOM entry; its deps
|
|
216
270
|
# are the direct dependencies.
|
|
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
|
|
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
|