AID-H-003.005: Infrastructure as Code (IaC) Security Scanning for AI Systems

Version 1.20261015 · Actionable sub-technique

Covers the 'pre-deployment' phase of automatically scanning Infrastructure as Code (IaC) files (e.g., Terraform, CloudFormation, Kubernetes YAML) in the CI/CD pipeline. This 'shift-left' security practice aims to detect and block security misconfigurations, such as insecure network paths that could undermine model supply chain security, before infrastructure is provisioned.

Pillars: infra

Lifecycle: validation

Scope boundary

Owns security scanning and admission findings for Infrastructure-as-Code that provisions AI compute, storage, network, identity, pipelines, and serving environments before deployment.

AID-M-005.002: AID-H-003.005 scans a concrete IaC change for vulnerabilities and insecure resource definitions; AID-M-005.002 defines the signed policy-as-code baseline, posture criteria, and release gate used to judge configuration. The scanner evaluates the artifact, while the Model control owns the authoritative configuration-policy contract.

Implementation guidance

AID-H-003.005-G001

Integrate IaC security scanners into the CI/CD pipeline to act as a merge blocker.
Production implementation: scan source and rendered plan as separate populations
name: IaC security gate
on: [pull_request]
permissions:
  contents: read
jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
      - name: Install policy-selected Terraform
        uses: hashicorp/setup-terraform@dfe3c3f87815947d99a8997f908cb6525fc44e9e # v4.0.1
        with:
          terraform_version: "${{ vars.VERIFIED_TERRAFORM_VERSION }}"
          terraform_wrapper: false
      - name: Render locked Terraform plan without deployment credentials
        run: |
          set -euo pipefail
          mkdir -p reports
          terraform -chdir=infrastructure init -lockfile=readonly -input=false
          terraform -chdir=infrastructure plan -input=false -refresh=false -out=plan.tfplan
          terraform -chdir=infrastructure show -json plan.tfplan > infrastructure/plan.json
          test -s infrastructure/plan.json
      - name: Scan IaC source population
        id: source_scan
        continue-on-error: true
        uses: bridgecrewio/checkov-action@7b972723c44fb3d256283fac96fae5d7c1894bb7
        with:
          directory: infrastructure
          soft_fail: false
          output_format: cli,json
          output_file_path: console,reports/checkov-source.json
      - name: Scan rendered Terraform plan
        id: plan_scan
        continue-on-error: true
        uses: bridgecrewio/checkov-action@7b972723c44fb3d256283fac96fae5d7c1894bb7
        with:
          file: infrastructure/plan.json
          framework: terraform_plan
          repo_root_for_plan_enrichment: infrastructure
          soft_fail: false
          output_format: cli,json
          output_file_path: console,reports/checkov-plan.json
      - name: Bind reports to exact inputs
        id: bind_evidence
        if: always()
        run: |
          set -euo pipefail
          test -s reports/checkov-source.json
          test -s reports/checkov-plan.json
          test -s infrastructure/.terraform.lock.hcl
          test -s infrastructure/plan.json
          sha256sum             infrastructure/.terraform.lock.hcl             infrastructure/plan.json             reports/checkov-source.json             reports/checkov-plan.json             > reports/iac-evidence.sha256
      - name: Retain complete scan evidence
        if: always()
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: "iac-security-evidence-${{ github.run_id }}-${{ github.run_attempt }}"
          path: reports/
          if-no-files-found: error
      - name: Enforce both scan outcomes
        if: always()
        env:
          SOURCE_SCAN_OUTCOME: "${{ steps.source_scan.outcome }}"
          PLAN_SCAN_OUTCOME: "${{ steps.plan_scan.outcome }}"
          EVIDENCE_OUTCOME: "${{ steps.bind_evidence.outcome }}"
        run: |
          set -euo pipefail
          test "$SOURCE_SCAN_OUTCOME" = success
          test "$PLAN_SCAN_OUTCOME" = success
          test "$EVIDENCE_OUTCOME" = success
          test -s reports/iac-evidence.sha256

Action and verification: Make this exact workflow check required by repository rules and repeat the source and plan scans before apply. Checkov's file input overrides directory, so separate steps are required to cover both populations. Keep the plan-rendering job free of production deployment credentials, pin Terraform through a protected policy variable, and run untrusted provider or module code only on an isolated ephemeral runner under the organization's network policy.

Retain the checked-out tree/commit, provider lock, module and policy digests, exact action commits, Terraform version, plan digest, both complete Checkov JSON reports, exceptions, runner image, workflow/run attempt, and the uploaded artifact digest/retention record. Exercise a clean change, a source-only finding, a generated-plan-only finding, render error, scanner error, stale exception, missing report, changed plan after scanning, and an IaC file outside the declared source population. Any missing population or non-success outcome blocks merge/apply; one report never substitutes for the other.

Before you begin

Apply AID-H-003.005-G001 only when infrastructure-as-code, rendered manifests, policies, or configuration deploy or change AI workloads and their data, model, network, or identity boundaries.

Keep the action safely bounded

Block merge or apply on uncovered files, scanner error, secret exposure, policy violation, stale exception, source-to-plan mismatch, or incomplete evidence retention.

AID-H-003.005-G002

Scan for hardcoded secrets within IaC and configuration files.
Production implementation: scan current tree and full reachable history
name: IaC secret gate
on: [pull_request]
permissions:
  contents: read
jobs:
  secrets:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          fetch-depth: 0
      - name: Scan repository history
        uses: trufflesecurity/trufflehog@17456f8c7d042d8c82c9a8ca9e937231f9f42e26 # v3.95.2
        with:
          path: ./
          extra_args: --only-verified --fail

Action: Also scan generated plan JSON, rendered manifests, configuration bundles, and build artifacts before upload because they may contain resolved secrets absent from Git history. A verified secret blocks immediately; unverified high-entropy findings follow the signed review policy. Retain commit range, complete scanned-path manifest, excluded-path review, scanner/rule/database versions, raw findings, and post-remediation credential revocation evidence.

Before you begin

Apply AID-H-003.005-G002 only when infrastructure-as-code, rendered manifests, policies, or configuration deploy or change AI workloads and their data, model, network, or identity boundaries.

Keep the action safely bounded

block merge or apply on uncovered files, scanner error, secret exposure, policy violation, stale exception, or source-to-plan mismatch.

AID-H-003.005-G003

Develop and enforce custom policies to prevent insecure network paths for AI workloads.
Before you begin

Apply AID-H-003.005-G003 only when infrastructure-as-code, rendered manifests, policies, or configuration deploy or change AI workloads and their data, model, network, or identity boundaries.

Production implementation: default-deny and port-bounded FQDN egress

This concrete policy applies only to an AI runtime whose signed egress manifest authorizes the OpenAI API and Hugging Face Hub. For another workload, the deployment renderer must replace the complete destination set from that workload's signature-verified manifest; sample, wildcard, or caller-supplied destinations are rejected.

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: ai-workload-egress
  namespace: ai-services
spec:
  endpointSelector:
    matchLabels:
      app.kubernetes.io/part-of: ai-runtime
  egress:
    - toEndpoints:
        - matchLabels:
            k8s:io.kubernetes.pod.namespace: kube-system
            k8s:k8s-app: kube-dns
      toPorts:
        - ports:
            - port: "53"
              protocol: UDP
            - port: "53"
              protocol: TCP
          rules:
            dns:
              - matchName: api.openai.com
              - matchName: huggingface.co
    - toFQDNs:
        - matchName: api.openai.com
        - matchName: huggingface.co
      toPorts:
        - ports:
            - port: "443"
              protocol: TCP

Verification: A policy-as-code gate reconciles every rendered toFQDNs and DNS entry against the signed workload manifest and rejects missing or extra FQDNs, wildcards, unrestricted ports, IP-block escape routes, absent endpoint selectors, and unapproved DNS resolvers. After apply, read back the Cilium policy revision and endpoint enforcement status, then test both approved TLS destinations plus denied arbitrary DNS names, direct IPs, alternate ports, and IPv6 from every selected pod. Bind the signed result to the manifest digest, cluster, namespace, workload labels, policy bytes, Cilium version, DNS observations, and complete pod population.

Open-source tools

Commercial / hosted tools

Threat mappings

MITRE ATLAS

MAESTRO

OWASP LLM Top 10 2026

OWASP ML Top 10 2023

OWASP Top 10 for Agentic Applications 2026

NIST Adversarial Machine Learning 2025

Cisco Integrated AI Security and Safety Framework

Google Secure AI Framework 2.0 - Risks

Databricks AI Security Framework 3.0

AID-H-003.005: Infrastructure as Code (IaC) Security Scanning for AI Systems | AIDEFEND

Loading interactive view…

A Practical Knowledge Base for AI Security Defenses

Version