b2KIT

Terraform Security Scanner

Scan Terraform HCL files for security misconfigurations: open security groups, public S3 buckets, and missing encryption.

Tested tool guide Tested browser tools Checked August 16, 2026

What Terraform Security Scanner does, with a checked example

Paste a Terraform .tf file into the box and this tool parses the HCL and reports resources that match known insecure patterns: security group ingress from 0.0.0.0/0 or ::/0, S3 buckets with public ACLs or disabled public-access blocks, and storage without encryption at rest. Findings name the resource address, the offending line, and a concrete fix, and the file never leaves the browser. What surprises people: the scan reads the full text you pasted, but it evaluates only literal values. Values that arrive through variables, module calls, or data sources are skipped rather than resolved, so a range written as var.admin_cidr passes even when it resolves to the world.

Worked example

A concrete input and expected output from the current implementation.

Input

resource "aws_s3_bucket" "logs" {
  bucket = "app-logs"
  acl    = "public-read"
}

resource "aws_security_group" "ssh" {
  name = "ssh-from-everywhere"

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

resource "aws_db_instance" "main" {
  engine         = "postgres"
  instance_class = "db.t3.micro"
}

Expected output

3 findings

[CRITICAL] aws_security_group.ssh (line 13)
  Port 22 (SSH) is open to 0.0.0.0/0. Restrict cidr_blocks to a known range.

[HIGH] aws_s3_bucket.logs (line 3)
  ACL "public-read" grants anonymous read access. Set acl = "private" or remove the argument.

[HIGH] aws_db_instance.main (line 17)
  Encryption at rest is not enabled. Add storage_encrypted = true.

All three findings follow from the file: the ingress block opens SSH to every IPv4 address, the ACL grants anonymous reads, and aws_db_instance without storage_encrypted is created unencrypted by provider default. Nothing else in the file matches a rule, so nothing else is reported.

How the result is produced

1

Parsing and rule matching

The text is parsed as HCL before any rule runs, so malformed syntax returns a parse error naming the line instead of a findings list. Each rule targets one resource type and checks arguments for literal values: an ingress block with cidr_blocks = ["0.0.0.0/0"], an ACL equal to public-read, a missing storage_encrypted. Non-literal expressions are skipped, never guessed.

2

What a finding contains

A finding carries the resource address (type.name), the line of the offending argument, a severity, and a suggested fix. Legacy and current spellings of the same problem are both caught: a public-read ACL on aws_s3_bucket and a public access block with block_public_acls = false each produce a finding. When nothing matches, the result is an empty list, not an error.

Good uses

  • Reviewing a file just before terraform apply, when you have edited a security group or bucket and want the open-holes check in seconds rather than a full plan review.
  • Auditing .tf files you inherited but did not write, hunting for world-open security groups or publicly readable buckets that predate your team's policies.
  • Vetting a module from the Terraform registry or a colleague's branch before adopting it, to catch these three misconfiguration families before they enter your codebase.

Limits and checks

  • Blind to expressions: only literal values are checked. A cidr_blocks value from a variable, module output, or data source is skipped, so a range that resolves to 0.0.0.0/0 can sail through. Trace non-literal network and encryption arguments by hand before applying.
  • Narrow scope: the rule set is the three headline families only. IAM policies, KMS key policies, logging, and most other Terraform security concerns are out of scope, so a clean report is not a general security review of the file.
  • File as written, not the account: the report reflects exactly the text pasted. Encryption or restrictions applied elsewhere, such as account-level defaults, separate resource blocks in other files, or changes made after apply, are invisible to it, so the result can disagree with production in either direction.

Common questions

A clean result means my infrastructure is secure, right?

No. The scan covers a fixed set of patterns in the one file you pasted, using only literal values. Anything decided by variables, modules, data sources, or changes made after apply is outside what it can see. Treat a clean result as one useful check passed, and pair it with plan review or an account-level audit before relying on it.

Why is my S3 bucket flagged when encryption is already configured in another resource?

Because the scan reads only the file you pasted. Encryption configured in a separate aws_s3_bucket_server_side_encryption_configuration block, by an account default, or after apply is invisible to it. The fix it suggests is not to change your setup, but to make encryption explicit in the resource being scanned.

References and verification

The example and behavioral notes were checked against the browser implementation. Standards and primary references below define the relevant format, formula, or platform behavior.

Related Tools