Skip to main content

CI-stage infrastructure security scanner — orchestrates checkov, tfsec, gitleaks, shellcheck, hadolint, actionlint, bandit, safety, and pip-audit into one command

Project description

shieldctl

CI-stage infrastructure security scanner. Wraps best-of-breed tools — checkov, tfsec, gitleaks, shellcheck, hadolint, actionlint, bandit, safety, pip-audit — into a single command with unified findings, configurable severity thresholds, and CI-ready exit codes.

shieldctl scan ./infrastructure
→ checkov       infrastructure/ (38 files)
→ tfsec         infrastructure/ (38 files)
→ gitleaks      . (repo-wide)
→ shellcheck    scripts/ (4 files)
→ hadolint      Dockerfile
→ actionlint    .github/workflows/ci.yaml
→ pip-audit     . (repo-wide)

CRITICAL  .github/workflows/ci.yaml:14   CKV2_GHA_1   Top-level permissions set to write-all
HIGH      infrastructure/gke.tf:22       AVD-GCP-0038  GKE node auto-upgrade disabled
HIGH      infrastructure/iam.tf:8        AWS-IAM-109  IAM wildcard action on policy
MEDIUM    scripts/deploy.sh:31           SH-001       curl piped directly to bash — supply chain risk
LOW       Dockerfile:4                   DL3008       Pin versions in apt-get install
──────────────────────────────────────────────────────────────────────────────────
5 findings  (1 CRITICAL, 2 HIGH, 1 MEDIUM, 1 LOW)  exit 3

Why shieldctl

Each security tool in the ecosystem is excellent at its domain but requires different invocations, different output formats, and different CI setup. shieldctl is the single entry point:

  • One command covers Terraform, secrets, shell scripts, YAML, Dockerfiles, Kubernetes, GitHub Actions, and Python dependencies
  • Consistent exit codes so CI gates are trivial to configure
  • Unified finding model — file, line, rule, severity, message, remediation, OWASP reference
  • Content-based detection — finds Kubernetes manifests and GHA workflows regardless of their directory path
  • Built-in deduplication — same finding from two tools appears once

Use shieldctl in CI. Use proofctl pre-commit for fast, zero-dependency linting.


Installation

pip install shieldctl

Requires: Python 3.11+

Install the scanner dependencies:

shieldctl install-tools

Scanner dependencies

Scanner Tools Installed via
Terraform checkov, tfsec pip install checkov, brew install tfsec
Secrets gitleaks brew install gitleaks
Shell shellcheck brew install shellcheck
YAML yamllint, checkov pip install yamllint checkov
Dockerfile hadolint brew install hadolint
Python bandit, safety pip install bandit safety
Kubernetes checkov pip install checkov
GitHub Actions actionlint, checkov brew install actionlint, pip install checkov
Python deps pip-audit pip install pip-audit

Install all at once:

shieldctl install-tools

shieldctl gracefully skips any scanner whose tool is not installed — it never hard-fails for a missing binary unless you pass --strict.


Quick start

# Scan everything from the repo root
shieldctl scan .

# Scan a specific directory
shieldctl scan ./infrastructure

# Fail the build on HIGH or above
shieldctl scan . --fail-on HIGH

# Only run the Terraform scanner
shieldctl scan . --scanner terraform

# Scan only git-changed files (fast CI mode)
shieldctl scan . --changed-only

# Output JSON for downstream processing
shieldctl scan . --format json --output findings.json

# Generate an HTML dashboard
shieldctl scan . --format html --output report.html

# Fail immediately if any required scanner tool is missing
shieldctl scan . --strict

CI integration (GitHub Actions)

name: shieldctl

on:
  push:
    branches: [main]
  pull_request:

jobs:
  security-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0   # gitleaks needs full history

      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: Install shieldctl and scanner tools
        run: |
          pip install shieldctl checkov yamllint bandit safety pip-audit
          brew install tfsec gitleaks shellcheck hadolint actionlint

      - name: Run shieldctl
        run: shieldctl scan . --fail-on HIGH

      - name: Generate HTML report
        if: failure()
        run: shieldctl scan . --format html --output shieldctl-report.html || true

      - uses: actions/upload-artifact@v4
        if: failure()
        with:
          name: shieldctl-report
          path: shieldctl-report.html
          retention-days: 14

For PR-only scans to keep CI fast:

      - name: Run shieldctl (changed files only)
        run: shieldctl scan . --changed-only --fail-on HIGH

Scanners

Terraform

Wraps checkov and tfsec. Scans .tf, .tfvars, and terragrunt.hcl files.

  • checkov: 1000+ policies across AWS, GCP, Azure, Kubernetes
  • tfsec: static analysis focused on cloud misconfigurations
  • Findings deduplicated by (file, line, rule)

Secrets

Wraps gitleaks. Runs repo-wide (not per-file) to detect secrets in:

  • Source code and config files
  • Git history (including previously committed and deleted secrets)
  • .env files, CI configs, Terraform variable files

All secret findings are CRITICAL severity.

Shell

Wraps shellcheck plus 6 custom rules (shieldctl-bash):

Rule Severity Description
SH-001 HIGH curl/wget piped directly to a shell interpreter
SH-002 HIGH Hardcoded credential in shell script (PASSWORD=, TOKEN=, etc.)
SH-003 MEDIUM eval with a dynamic variable operand (command injection risk)
SH-004 LOW Script missing set -euo pipefail
SH-005 MEDIUM rm -rf with a variable path
SH-006 MEDIUM chmod 777 (world-writable permissions)

YAML

Wraps yamllint for structural validation of all .yml/.yaml files.

Also runs checkov on files that are detected as Kubernetes manifests or GitHub Actions workflows — detection is content-based (apiVersion:/kind: for K8s, on:/jobs: for GHA), so it works regardless of directory structure.

Dockerfile

Wraps hadolint. Scans Dockerfile, Dockerfile.*, and *.dockerfile files.

Rule coverage includes: unpinned base images, root user, missing --no-install-recommends, secrets in ARG/ENV, inefficient layer caching, and supply chain risks.

Python

Wraps bandit for SAST and safety for known CVE scanning of installed packages.

  • bandit: detects injection, hardcoded passwords, weak crypto, use of pickle, subprocess misuse, and more
  • safety: checks installed packages against the PyUp vulnerability database

Kubernetes

Wraps checkov with --framework kubernetes. Files are detected by content (apiVersion: + kind: at root level), so the scanner works even if manifests live outside a k8s/ directory.

Covers Kubernetes Pod Security Standards, RBAC misconfigurations, missing resource limits, and insecure pod specs.

GitHub Actions

Wraps actionlint and checkov (--framework github_actions). Files are detected by content (on: + jobs: at root level).

actionlint checks:

  • Expression injection via ${{ github.event.* }} in run: steps
  • Invalid action references and syntax errors
  • Shell script correctness inside run: blocks

checkov checks:

  • pull_request_target with PR head checkout (pwn request pattern)
  • Missing top-level permissions: block
  • Unpinned third-party action versions

Python dependencies (pip-audit)

Wraps pip-audit. Runs repo-wide, auditing the active Python environment against the PyPI Advisory Database (OSV). Reports CVE IDs, affected versions, and available fix versions.

All pip-audit findings are HIGH severity.


Configuration

Place .shieldctl.yaml in the scan root:

# Minimum severity that causes a non-zero exit (INFO | LOW | MEDIUM | HIGH | CRITICAL)
fail_on: HIGH

# Glob patterns to exclude from scanning
exclude_paths:
  - "**/.terraform/**"
  - "**/.terragrunt-cache/**"
  - "**/node_modules/**"
  - "**/.git/**"

# Suppress specific rules globally
suppress:
  - rule: CKV_GCP_29
    reason: "Allow-all-egress on bastion is intentional  tracked in JIRA-1234"
  - rule: CKV2_GHA_1
    reason: "write-all permissions required for release workflow"

The suppress list accepts any rule ID from any scanner (checkov rule IDs, tfsec IDs, SH-001, etc.).


Suppressing a single finding

For checkov/tfsec Terraform rules, use the native inline skip:

resource "google_storage_bucket" "assets" {
  # checkov:skip=CKV_GCP_5:Public read for static assets is intentional
  name = "my-public-assets"
}

For shell custom rules, add a comment on the flagged line:

eval "$generated_cmd"  # shieldctl: ignore[SH-003]

Commands

shieldctl scan

shieldctl scan [PATH] [OPTIONS]

Options:
  --fail-on        INFO | LOW | MEDIUM | HIGH | CRITICAL
                   Overrides .shieldctl.yaml fail_on setting
  --format / -f    terminal | json | html    (default: terminal)
  --output / -o    Write report to file
  --changed-only   Only scan git-changed files
  --scanner        Run a single scanner: terraform | secrets | bash |
                   yaml | dockerfile | python | kubernetes | gha | pip-audit
  --strict         Exit immediately if any required scanner tool is not installed

shieldctl install-tools

Install all scanner dependencies. Detects the OS and uses the appropriate package manager.

shieldctl install-tools          # install everything
shieldctl install-tools --dry-run  # show what would be installed

Exit codes

Code Meaning
0 No findings
1 Findings present, all below MEDIUM
2 MEDIUM findings present
3 HIGH or CRITICAL findings present (or --fail-on threshold exceeded)
4 Scanner error (tool crashed or --strict tool not found)

These codes are designed for CI gate configuration:

# Fail the build on HIGH+
- run: shieldctl scan . --fail-on HIGH
# Fail on any finding
- run: shieldctl scan . --fail-on INFO

Output formats

Terminal (default) — colour-coded rich table grouped by severity.

CRITICAL  .github/workflows/ci.yaml:14  CKV2_GHA_1  Top-level permissions set to write-all
HIGH      infra/gke.tf:22               AVD-GCP-0038  GKE node auto-upgrade disabled

JSON — structured output for downstream tooling:

{
  "summary": {
    "total": 5,
    "CRITICAL": 1,
    "HIGH": 2,
    "MEDIUM": 1,
    "LOW": 1
  },
  "findings": [
    {
      "file": ".github/workflows/ci.yaml",
      "line": 14,
      "rule": "CKV2_GHA_1",
      "severity": "CRITICAL",
      "message": "Top-level permissions are set to write-all",
      "remediation": "Set permissions to read-only at top level and grant write only where needed",
      "scanner": "checkov-gha",
      "owasp": "A05:2021 – Security Misconfiguration"
    }
  ]
}

HTML — self-contained single-file dashboard with severity summary cards, scanner breakdown, and a filterable findings table.


License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

shieldctl-0.1.4.tar.gz (43.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

shieldctl-0.1.4-py3-none-any.whl (34.7 kB view details)

Uploaded Python 3

File details

Details for the file shieldctl-0.1.4.tar.gz.

File metadata

  • Download URL: shieldctl-0.1.4.tar.gz
  • Upload date:
  • Size: 43.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for shieldctl-0.1.4.tar.gz
Algorithm Hash digest
SHA256 d0ef01614e38e5c00995968ce68c4c6f0aab05ba8a79068bec78d69ea212c10b
MD5 cefce8ca94ec44dbf8bafa4b538484ce
BLAKE2b-256 3e3d2f432041b31b5a1d51f58fbabaa5504950eafee7d63bfbafd9beca57a081

See more details on using hashes here.

Provenance

The following attestation bundles were made for shieldctl-0.1.4.tar.gz:

Publisher: publish.yml on kolawoluu/shieldctl

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file shieldctl-0.1.4-py3-none-any.whl.

File metadata

  • Download URL: shieldctl-0.1.4-py3-none-any.whl
  • Upload date:
  • Size: 34.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for shieldctl-0.1.4-py3-none-any.whl
Algorithm Hash digest
SHA256 67eb6596c415ea76e0a8377078f2c5effdea91bb441a965004a03d5a0a418df7
MD5 7786c013bfa742fa0f2aa41459a66755
BLAKE2b-256 e321f390d084125bf5c0da0582fbed6549b89cd7330919054a5b888957ead2b6

See more details on using hashes here.

Provenance

The following attestation bundles were made for shieldctl-0.1.4-py3-none-any.whl:

Publisher: publish.yml on kolawoluu/shieldctl

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page