
A list of IAM bindings does not tell you how bad a compromise would be, because roles chain. A service account that can create keys for another service account, which holds Owner, is Owner. gcp-iam-blast-radius answers one question about a project: if this identity is compromised, what can it become? It reads project IAM read-only, follows service-account impersonation chains, scores every principal's blast radius from 0 to 100, flags privilege-escalation paths and proposes least-privilege fixes. Every fix is re-simulated against the policy before it is shown, so a suggestion that would widen access or lock you out of IAM is rejected automatically.
How it computes "can become"
The engine derives direct capabilities per principal, then adds "can become" edges between service accounts: token creator, key creation, actAs combined with code execution, and IAM admin. A principal's effective score is what it can reach through those hops. Ten deterministic rules own severity and the overall posture. In the acme-prod sample, user:bob has a direct score of 20 but an effective score of 100, through bob to the compute service account (Editor) to the terraform service account (Owner).
The trust split
Code decides severity, posture and which fixes are valid. Models only explain and propose, and both are validated. An explanation must mention only identities and roles that exist in the snapshot and contain no links. A proposed patch must reference a real binding, must not add capabilities, and must lower total blast radius when simulated. The cross-provider eval (18 runs) saw 0 invariant violations: posture and findings were identical to rules-only, and every fix was re-verified. Models did propose bad patches: gemini-2.5-flash and gpt-5-nano each had 12 accepted and 4 rejected by the simulator, the mock 14 and 2.
Reading the sample report
The acme-prod sample is critical: 13 principals, 17 findings, 14 verified fixes. The blast map shows critical identities as large red nodes with arrows for "can become" edges. Findings include a group that can escalate from score 86 to 100 by creating a key for a service account, a CI deployer that can mint tokens for the compute service account, roles/storage.objectViewer granted to allUsers, primitive Editor and Owner roles on service accounts and a user, a user-managed key 210 days old, an external gmail identity holding storage.admin, and a disabled service account that still holds roles. Further down, each fix shows its accepted effect on the total, for example "remove serviceAccountUser from bob: total blast radius 803 to 718", with the exact gcloud projects remove-iam-policy-binding command. Where a narrower role is needed it is added before the old one is removed.
Model-proposed fixes appear in the same list with their label, such as replacing roles/editor on a service account with roles/compute.instanceAdmin.v1, accepted because simulation shows the total drops from 803 to 708. The orchestration timeline at the bottom shows the engine in 1 ms, four analyst and four planner model calls in parallel, then the verifier and judge.
A real project

The second screenshot is a live analysis of one of my own projects on Cloud Run: 39 principals, 2 findings, 2 verified fixes, for $0.001. Live mode only analyses projects on an allow-list (IAMBLAST_LIVE_PROJECTS), the service account holds iam.securityReviewer, and results live in memory only.
| Check | Result |
|---|---|
| Offline tests | 58 pass: engine hops, cycles, disabled SAs, every rule, patch verification, hostile-model cases |
| Deployed | Unauthenticated, openai provider and non-allow-listed project all 403 |
Limits
The role catalog is a curated 75-role approximation, not IAM's permission lists. Unlisted roles are reported as IAM009 and not scored, so radii can be understated; confirm important fixes with Policy Analyzer. Scope is project and service-account IAM; resource-level policies, folder and org inheritance, deny policies, conditions and group membership are not expanded. Fixes are verified against a model of access, not actual usage, so a "safe" removal can break a workload: check audit logs first. It was verified on three synthetic snapshots and one small real project, and large estates are untested.
Try it yourself
git clone https://github.com/soodrajesh/gcp-iam-blast-radius
cd gcp-iam-blast-radius
.venv/bin/iamblast analyze iamblast/samples/acme_prod.json --llm none # exit 2 = critical
.venv/bin/iamblast collect my-project --internal-domain example.com > snapshot.json
📢 Have questions or feedback? Drop a comment below or connect with me on Twitter/X@spysood!