Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ AzureFox is built for that workflow.
- Highlight pivot paths through workloads, managed identities, deployment systems, and secret-bearing configuration
- Expose escalation opportunities and likely next steps instead of leaving you to sort raw Azure data

## Install

```bash
pipx install azurefox
```

## Operator Workflow

Start with the identity you have, then work outward toward movement and consequence:
Expand Down Expand Up @@ -80,12 +86,6 @@ AzureFox reduces noise by ranking consequence, not just returning Azure objects.
- Assess whether a service principal or application relationship creates a pivot or escalation path
- Work outward from subscription or tenant visibility to identify cross-resource and cross-tenant movement

## Install

```bash
pipx install azurefox
```

## Run It

Start with the current Azure identity and the strongest visible control paths:
Expand Down
14 changes: 14 additions & 0 deletions tests/test_collectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
collect_rbac,
collect_resource_trusts,
collect_role_trusts,
collect_snapshots_disks,
collect_storage,
collect_tokens_credentials,
collect_vms,
Expand Down Expand Up @@ -3673,6 +3674,19 @@ def test_collect_vms(fixture_provider, options) -> None:
assert len(output.findings) == 1


def test_collect_snapshots_disks(fixture_provider, options) -> None:
output = collect_snapshots_disks(fixture_provider, options)
assert len(output.snapshot_disk_assets) == 4
assert output.issues == []
assert output.snapshot_disk_assets[0].name == "data-detached-legacy"
assert output.snapshot_disk_assets[0].attachment_state == "detached"
assert output.snapshot_disk_assets[0].public_network_access == "Enabled"
assert output.snapshot_disk_assets[1].asset_kind == "snapshot"
assert output.snapshot_disk_assets[1].source_resource_name == "data-detached-legacy"
assert output.snapshot_disk_assets[2].name == "vm-web-01-os-snap"
assert output.snapshot_disk_assets[3].name == "vm-web-01-os"


def test_collect_vms_keeps_public_ip_lookup_failures_explicit() -> None:
provider = object.__new__(AzureProvider)
provider.clients = SimpleNamespace(
Expand Down
21 changes: 21 additions & 0 deletions tests/test_terminal_ux.py
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,27 @@ def test_snapshots_disks_takeaway_counts_disk_access_as_broad_export_signal() ->
assert "1 show broader sharing or export posture" in " ".join(rendered.split())


def test_vms_table_mode_surfaces_public_ip_and_identity_cues(tmp_path: Path) -> None:
result = runner.invoke(
app,
["--outdir", str(tmp_path), "vms"],
env=_fixture_env(),
)

assert result.exit_code == 0
assert (
"Summarizing reachable compute assets and identity-bearing workloads." in result.stdout
)
assert "vm-web-01" in result.stdout
assert "52.160.10.20" in result.stdout
assert "10.0.1.4" in result.stdout
assert "Public workload with attached identity" in result.stdout
normalized_output = " ".join(result.stdout.split())
assert (
"Takeaway: 1 compute assets visible; 1 have public IP exposure."
) in normalized_output


def test_storage_takeaway_keeps_partial_read_posture_explicit() -> None:
payload = {
"metadata": {"command": "storage"},
Expand Down
Loading