Catalog npm
PACKAGE EXPLORERView in registry

@0x5278/blastradius

npm

Find what a terraform plan actually breaks: BFS the plan's own dependency graph past the resources being changed.

dependency-graphiacplanriskterraform
Versions collected2
Direct dependencies0From release 0.1.1
Last observed20 Oct, 22:37 UTC

README

Current package documentation

blastradius

CI npm License: MIT

terraform plan tells you what it's changing. It doesn't tell you what breaks as a result. blastradius reads the plan's own dependency graph and BFS-traces every resource downstream of a delete/replace/update - including resources Terraform marks no-op, which can still fail at runtime when something they depend on vanishes.

-  DELETE  aws_security_group.db_sg  (blast risk: 18)
     └─ aws_db_instance.main  [depth 1, risk 9]
       └─ aws_db_instance.replica  [depth 2, risk 9]

That depth-2 catch - the read replica nobody was thinking about - is the entire point. terraform plan is depth-0 by design; it only lists what changes, not what breaks as a consequence.

Install

npm install -g @0x5278/blastradius
# or just: npx @0x5278/blastradius --plan plan.json

Usage

terraform plan -out=tfplan
terraform show -json tfplan > plan.json

blastradius --plan plan.json                  # human-readable CLI report
blastradius --plan plan.json --format markdown # for PR comments
blastradius --plan plan.json --format json     # for scripting

# or pipe directly, no intermediate file
terraform show -json tfplan | blastradius

Exit code is non-zero when any change has a totalRisk >= 20 (default threshold, override with --threshold), so it gates CI out of the box.

In CI

- run: terraform plan -out=tfplan
- run: terraform show -json tfplan > plan.json
- run: npx @0x5278/blastradius --plan plan.json --format markdown >> $GITHUB_STEP_SUMMARY
- run: npx @0x5278/blastradius --plan plan.json   # exits 1 if totalRisk >= 20

PR reviewers see the risk table before approving, not after the incident.

As a library

const { run, formatReport } = require('@0x5278/blastradius');

const results = run(planJsonString, { minSeverity: 'update' });
console.log(formatReport(results, 'cli'));

results is an array, one entry per qualifying change:

{
  address: 'aws_security_group.db_sg',
  type: 'aws_security_group',
  severity: 'delete',
  totalRisk: 18,
  affected: [
    { address: 'aws_db_instance.main', type: 'aws_db_instance', depth: 1, risk: 9 },
    { address: 'aws_db_instance.replica', type: 'aws_db_instance', depth: 2, risk: 9 },
  ],
}

Options:

  • minSeverity: skip changes below this severity (create, update, replace, delete). Default: create.
  • includeNoOps: include changes whose only action is no-op (can still fail at runtime when a dependency is deleted). Default: false.

How it works

lib/parser.js    validate plan JSON, extract resource_changes + configuration
lib/graph.js     walk expressions[].references recursively -> reverse dependency graph
lib/analyzer.js  BFS from each change over the reverse graph, score by resource type
lib/report.js    cli / json / markdown formatters
bin/cli.js       stdin or --plan, exits non-zero above the risk floor
main.js          library entry point

The reference graph isn't reconstructed from HCL - Terraform's own plan JSON already contains it under configuration.root_module.resources[].expressions.*.references. blastradius walks data that is already there.

Module calls are recursed too: configuration.root_module.module_calls[name].module is walked with address flattening, so module.ec2.aws_security_group.sg and outputs (module.ec2.main_arn) resolve correctly across module boundaries.

Risk scoring is a static weight table per resource type, summed over the affected dependents. It is deliberately a guess; the long-term plan is to train these weights off a team's own incident history.

Try it

git clone https://github.com/JoeyKoch1/blastradius.git
cd blastradius
npm install
npm run example

Runs against examples/example-plan.json - a security group deletion that cascades through an RDS instance to a read replica two hops away - and examples/example-module-plan.json, the same scenario with the replica reached through a module boundary.

Known limitations / roadmap

  • for_each/count addresses are normalized (index suffixes are stripped when matching references), but the index itself is not derived from expressions.
  • Risk weights are hardcoded guesses, not learned from anything. The real long-term differentiator is training these off a team's own incident history instead of a static table.
  • No cross-state support. Graphs that span terraform_remote_state boundaries are not stitched together.

License

MIT - see LICENSE.

Downloads & popularity

Awaiting observations

No metric observations yet

Download counts and popularity will appear here as the collector receives them.

Versions 2

Collected releases
VersionPublishedDirect dependenciesOpen version
0.1.1Registry latest19 Aug 20260
0.1.018 Aug 20260

Package advisories

0
No matching advisories collected

This describes our collected data; it is not a security assessment.

Repository

PENDING
joeykoch1/blastradius

Shared repository data · GitHub

License detected in repository
Not collected

Metadata history

Meaningful changes
Current collected state Current

Observed since 20 Oct, 22:37 UTC

No superseded metadata states collected. Unchanged observations do not create new revisions.

Record identity

package:ec47813326be4fff683b06f880a4d5ac54f1bdf8da01c12657fde1d4231c2b77