DEV Community

FabrizioPerezPeralta
FabrizioPerezPeralta

Posted on

Applying SAST to Infrastructure as Code: Unifying Cloud and Container Security with Trivy

Author: Fabrizio Salvador Elias Perez Peralta

Abstract
As Infrastructure as Code (IaC) evolves, the boundary between cloud provisioning and container configuration blurs. This article explores the application of Static Application Security Testing (SAST) using Trivy, an open-source vulnerability scanner listed in the OWASP tools catalog. While my colleague Gianfranco demonstrated the power of Checkov for AWS (referenced in GianfrancoArocutipa/checkov-iac-demo), this piece expands the scope by scanning both a Terraform deployment and a Docker container configuration in a single pass. The demonstration uncovers critical misconfigurations—including root-level container execution and unencrypted cloud storage—and details how to remediate them. Finally, it showcases how Trivy's SARIF output can be integrated into GitHub Actions, emphasizing the importance of developer experience and automated security gates in modern DevSecOps workflows.

The Evolution of IaC and the Need for SAST
In the modern DevSecOps landscape, a vulnerable system is rarely just about bad PHP or Python code. Often, the breach happens at the infrastructure level. You can write the most secure application in the world, but if the Docker container runs as root and the Terraform-managed S3 bucket holding the backups is public, you are compromised.

SAST tools for IaC analyze configuration files (like .tf, Dockerfile, or kubernetes.yaml) against hundreds of security policies before any infrastructure is actually created. This allows developers to catch misconfigurations right in their IDE or Continuous Integration (CI) pipeline.

Why Trivy?
While Checkov is an outstanding policy-as-code tool, Trivy (by Aqua Security) takes a "comprehensive scanner" approach. It is widely loved in the Linux and Docker communities because a single binary can scan container images, file systems, Git repositories, and IaC configurations (Terraform, CloudFormation, Kubernetes, Dockerfiles) seamlessly. It is fast, stateless, and requires no prerequisites.

The Victim: A Containerized App Managed by Terraform
For this demonstration, we have a small project containing two critical IaC files: a Dockerfile for our application and a main.tf to deploy cloud resources.

1. The Vulnerable Dockerfile:

# Dockerfile
FROM ubuntu:20.04
RUN apt-get update && apt-get install -y curl
COPY myapp /app/
# VULNERABILITY: No USER specified, runs as root by default
CMD ["/app/myapp"]
Enter fullscreen mode Exit fullscreen mode

2. The Vulnerable Terraform (main.tf):

# main.tf
provider "aws" {
  region = "us-east-1"
}

resource "aws_s3_bucket" "app_data" {
  bucket = "company-sensitive-app-data"
  # VULNERABILITY: No encryption at rest configured
}

resource "aws_security_group" "web" {
  # VULNERABILITY: SSH exposed to the world
  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 1: Install and Run Trivy
Trivy is incredibly easy to use on Linux. You can install it via an apt package, homebrew, or simply pull the binary. Once installed, we instruct Trivy to scan our entire directory for IaC misconfigurations:

trivy config ./infrastructure
Step 2: Analyzing the Scan Results
In a matter of seconds, Trivy parses both the Dockerfile and the Terraform code. The output instantly highlights the critical flaws:

Plaintext
infrastructure/Dockerfile (dockerfile)
======================================
Tests: 23 (SUCCESSES: 22, FAILURES: 1, EXCEPTIONS: 0)
Failures: 1 (HIGH: 1, CRITICAL: 0)

+---------------------------+------------+-----------------------------------------+
|           TYPE            |  SEVERITY  |                 MESSAGE                 |
+---------------------------+------------+-----------------------------------------+
| DS002                     | HIGH       | Specify at least 1 USER command in      |
|                           |            | Dockerfile with non-root user as        |
|                           |            | arguments                               |
+---------------------------+------------+-----------------------------------------+

infrastructure/main.tf (terraform)
==================================
Tests: 45 (SUCCESSES: 43, FAILURES: 2, EXCEPTIONS: 0)
Failures: 2 (HIGH: 1, CRITICAL: 1)

+---------------------------+------------+-----------------------------------------+
|           TYPE            |  SEVERITY  |                 MESSAGE                 |
+---------------------------+------------+-----------------------------------------+
| AVD-AWS-0088              | HIGH       | S3 Bucket does not have encryption      |
|                           |            | enabled                                 |
+---------------------------+------------+-----------------------------------------+
| AVD-AWS-0107              | CRITICAL   | Security group rule allows ingress      |
|                           |            | from public internet to port 22         |
+---------------------------+------------+-----------------------------------------+
Enter fullscreen mode Exit fullscreen mode

Trivy not only caught the Terraform misconfigurations (unencrypted bucket and open SSH port) but also flagged that our container is violating the principle of least privilege by running as root.

Step 3: Hardening the Code
We remediate the issues by updating our IaC files with secure defaults.

Fixed Dockerfile:

FROM ubuntu:20.04
RUN useradd -m appuser
COPY myapp /app/
USER appuser # Fixed: Running as a non-root user
CMD ["/app/myapp"]
Fixed Terraform:

Terraform
resource "aws_s3_bucket" "app_data" {
  bucket = "company-sensitive-app-data"
}

resource "aws_s3_bucket_server_side_encryption_configuration" "app_data_encryption" {
  bucket = aws_s3_bucket.app_data.id
  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm = "AES256" # Fixed: Encryption at rest enforced
    }
  }
}

resource "aws_security_group" "web" {
  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["10.0.0.0/16"] # Fixed: Restricted to internal VPN
  }
}
Running trivy config ./infrastructure again yields a clean report with FAILURES: 0.
Enter fullscreen mode Exit fullscreen mode

Step 4: Automating the Developer Experience (CI/CD)
As a developer who works on building custom vulnerability analyzers (like Anzencore) and IDE extensions, I know that CLI tools are only half the battle. If a security tool isn't integrated into the developer workflow, it won't be used.

By integrating Trivy into GitHub Actions, we can block pull requests that introduce vulnerable infrastructure. Trivy supports outputting to the SARIF (Static Analysis Results Interchange Format) standard, which integrates natively with GitHub Code Scanning.

name: IaC SAST - Trivy Scan
on:
  pull_request:
    branches: [ main ]

jobs:
  trivy-scan:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Run Trivy vulnerability scanner in IaC mode
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: 'config'
          hide-progress: false
          format: 'sarif'
          output: 'trivy-results.sarif'
          exit-code: '1'

      - name: Upload Trivy scan results to GitHub Security tab
        uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: 'trivy-results.sarif'
Enter fullscreen mode Exit fullscreen mode

Conclusion
By treating our containers and cloud deployments as code, SAST tools like Trivy allow us to push security to the very beginning of the software development lifecycle. Whether we are writing Terraform to spin up AWS resources or crafting Dockerfiles, scanning these text files for known architectural vulnerabilities prevents costly breaches before they happen. Integrating these tools through automated CI/CD gates ensures that security becomes an enabler of quality, rather than a blocker of speed.

Demo repository (code + CI workflow): https://github.com/GianfrancoArocutipa/checkov-iac-demo(#)

Top comments (1)

Collapse
 
gian_francoarocutipaaro profile image
GIAN FRANCO AROCUTIPA AROCUTIPA

Excellent article! Abstract: this piece applies Trivy, Aqua Security's open-source comprehensive scanner from the OWASP catalog, to a project mixing two IaC artifact types — a Dockerfile and a Terraform configuration — scanned in a single pass with trivy config. The scan surfaced a CRITICAL exposed SSH port, a HIGH unencrypted S3 bucket, and, notably, a HIGH finding no Terraform-only scanner would see: a container running as root (DS002), violating least privilege. After remediation the report comes back clean, and the article closes by wiring the scan into GitHub Actions with SARIF output feeding GitHub Code Scanning, framing security gates as part of developer experience rather than an obstacle.
An important observation: the fact that one binary with zero prerequisites covers containers, Terraform, Kubernetes and even Git repos makes Trivy an unusually low-friction entry point for teams adopting DevSecOps — the hardest part of shifting left is usually adoption, not detection.