Skip to content

Commit f63338b

Browse files
committed
feat: initial commit of AI-powered Terraform security guardrail
0 parents  commit f63338b

5 files changed

Lines changed: 236 additions & 0 deletions

File tree

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
name: DevSecOps AI Plan Guardrail
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
branches: [ main ]
8+
9+
jobs:
10+
ia-security-scan:
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- name: Checkout Code
15+
uses: actions/checkout@v4
16+
17+
- name: Set up Python
18+
uses: actions/setup-python@v5
19+
with:
20+
python-version: '3.11'
21+
22+
- name: Install Dependencies
23+
run: |
24+
pip install google-genai pydantic
25+
26+
- name: Setup Terraform
27+
uses: hashicorp/setup-terraform@v3
28+
with:
29+
terraform_wrapper: false
30+
31+
- name: Terraform Plan Generation
32+
run: |
33+
terraform init
34+
terraform plan -out=tfplan.binary
35+
terraform show -json tfplan.binary > plan.json
36+
37+
- name: Run Gemini AI Security Audit
38+
env:
39+
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
40+
run: |
41+
python scan_plan.py

.gitignore

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Ignore local python virtual environments
2+
venv/
3+
.venv/
4+
__pycache__/
5+
*.pyc
6+
7+
# Ignore local Terraform state and binaries
8+
.terraform/
9+
*.tfstate
10+
*.tfstate.backup
11+
tfplan.binary
12+
plan.json
13+
14+
# Ignore local environment variables / API keys
15+
.env

.terraform.lock.hcl

Lines changed: 25 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

main.tf

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
terraform {
2+
required_providers {
3+
aws = {
4+
source = "hashicorp/aws"
5+
version = "~> 5.0"
6+
}
7+
}
8+
}
9+
10+
provider "aws" {
11+
region = "us-east-1"
12+
# No active cloud keys required for offline planning
13+
skip_credentials_validation = true
14+
skip_requesting_account_id = true
15+
skip_metadata_api_check = true
16+
}
17+
18+
# 🚨 Security Flaw #1: Publicly accessible S3 Bucket
19+
resource "aws_s3_bucket" "public_data" {
20+
bucket = "company-confidential-data-2026"
21+
}
22+
23+
resource "aws_s3_bucket_public_access_block" "bad_practice" {
24+
bucket = aws_s3_bucket.public_data.id
25+
26+
block_public_acls = false
27+
block_public_policy = false
28+
ignore_public_acls = false
29+
restrict_public_buckets = false
30+
}
31+
32+
# 🚨 Security Flaw #2: SSH Open to the whole internet
33+
resource "aws_security_group" "allow_ssh_global" {
34+
name = "allow_ssh"
35+
description = "Insecure security group"
36+
37+
ingress {
38+
description = "SSH from anywhere"
39+
from_port = 22
40+
to_port = 22
41+
protocol = "tcp"
42+
cidr_blocks = ["0.0.0.0/0"] # Open to the world!
43+
}
44+
}

scan_plan.py

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import json
2+
import os
3+
import sys
4+
from typing import List
5+
6+
from google import genai
7+
from google.genai import types
8+
from pydantic import BaseModel
9+
10+
11+
# 1. Define the exact JSON structure we want Gemini to return
12+
class SecurityFinding(BaseModel):
13+
resource_name: str
14+
severity: str # CRITICAL, WARNING, or INFO
15+
vulnerability: str
16+
remediation_steps: str
17+
18+
19+
class SecurityReport(BaseModel):
20+
status: str # MUST be "PASS" or "FAIL"
21+
summary: str
22+
findings: List[SecurityFinding]
23+
24+
25+
def audit_terraform_plan():
26+
# Verify API key
27+
if not os.environ.get("GEMINI_API_KEY"):
28+
print("❌ Error: GEMINI_API_KEY environment variable is not set.")
29+
sys.exit(1)
30+
31+
# Load the real plan.json generated by the user
32+
plan_path = "plan.json"
33+
if not os.path.exists(plan_path):
34+
print(
35+
f"❌ Error: {plan_path} not found. Run 'terraform show -json tfplan.binary > plan.json' first."
36+
)
37+
sys.exit(1)
38+
39+
print(f"📖 Reading {plan_path}...")
40+
with open(plan_path, "r") as file:
41+
plan_data = json.load(file)
42+
43+
# Clean the plan slightly to save token space (focusing on resource changes)
44+
resource_changes = plan_data.get("resource_changes", [])
45+
46+
print(f"Securing {len(resource_changes)} planned resource modifications...")
47+
48+
# 2. Design the prompt
49+
prompt = f"""
50+
You are an automated DevSecOps Cloud Security Auditor. Your job is to analyze the following
51+
Terraform execution plan JSON data and evaluate it against enterprise security best practices.
52+
53+
Look specifically for:
54+
- Publicly accessible storage (S3 buckets, Azure Blobs, etc.)
55+
- Overly permissive networking rules (e.g., Ingress from 0.0.0.0/0 on sensitive ports like 22, 3389, 80, 443 unless justified)
56+
- Unencrypted resources or databases
57+
- Cleartext credentials/secrets
58+
59+
If you find ANY CRITICAL security violations, you MUST set the status to "FAIL".
60+
If the infrastructure is safe or contains only minor issues, set the status to "PASS".
61+
62+
RAW TERRAFORM PLAN RESOURCE CHANGES:
63+
{json.dumps(resource_changes, indent=2)}
64+
"""
65+
66+
print("🤖 Analyzing plan with Gemini Security Architect...")
67+
client = genai.Client()
68+
69+
# 3. Request structured content matching our Pydantic model
70+
response = client.models.generate_content(
71+
model="gemini-2.5-flash",
72+
contents=prompt,
73+
config=types.GenerateContentConfig(
74+
response_mime_type="application/json",
75+
response_schema=SecurityReport,
76+
temperature=0.1, # Low temperature ensures strict, analytical deterministic results
77+
),
78+
)
79+
80+
# 4. Parse the structured output
81+
report = SecurityReport.model_validate_json(response.text)
82+
83+
# 5. Print a beautiful console report
84+
print("\n" + "=" * 50)
85+
print(f"🛡️ DEVSECOPS SECURITY VERDICT: {report.status}")
86+
print("=" * 50)
87+
print(f"Summary: {report.summary}\n")
88+
89+
if report.findings:
90+
print(f"⚠️ Found {len(report.findings)} Security Vulnerabilities:")
91+
for idx, finding in enumerate(report.findings, 1):
92+
print(f"\n [{idx}] Resource: {finding.resource_name}")
93+
print(f" Severity: {finding.severity}")
94+
print(f" Vulnerability: {finding.vulnerability}")
95+
print(f" Fix: {finding.remediation_steps}")
96+
else:
97+
print("✅ No critical security violations detected.")
98+
99+
print("=" * 50)
100+
101+
# 6. ENFORCE THE GATEKEEPER RULE (Crucial for CI/CD pipelines!)
102+
if report.status == "FAIL":
103+
print("❌ Deployment Blocked: Critical security flaws must be resolved.")
104+
sys.exit(1) # Forces automated tools like GitHub Actions to halt immediately
105+
else:
106+
print("🚀 Deployment Approved: Infrastructure compliant.")
107+
sys.exit(0)
108+
109+
110+
if __name__ == "__main__":
111+
audit_terraform_plan()

0 commit comments

Comments
 (0)