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)
.envfiles, 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,subprocessmisuse, 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.* }}inrun:steps - Invalid action references and syntax errors
- Shell script correctness inside
run:blocks
checkov checks:
pull_request_targetwith 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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file shieldctl-0.1.5.tar.gz.
File metadata
- Download URL: shieldctl-0.1.5.tar.gz
- Upload date:
- Size: 43.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2378432032429a74e55aa65b72ad5236c653e0afe9351dd38d2c569f2cf71821
|
|
| MD5 |
858fa5070b0cbe6c79cf277e39f82f6d
|
|
| BLAKE2b-256 |
1015780c56f8ed0d22dab7722c1f65f59bc1456b50d196b687cb8a73303165b8
|
Provenance
The following attestation bundles were made for shieldctl-0.1.5.tar.gz:
Publisher:
publish.yml on kolawoluu/shieldctl
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
shieldctl-0.1.5.tar.gz -
Subject digest:
2378432032429a74e55aa65b72ad5236c653e0afe9351dd38d2c569f2cf71821 - Sigstore transparency entry: 1484658090
- Sigstore integration time:
-
Permalink:
kolawoluu/shieldctl@a501f823f5f218b8fd122b47d6a19316595bfb9c -
Branch / Tag:
refs/tags/v0.1.5 - Owner: https://github.com/kolawoluu
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@a501f823f5f218b8fd122b47d6a19316595bfb9c -
Trigger Event:
push
-
Statement type:
File details
Details for the file shieldctl-0.1.5-py3-none-any.whl.
File metadata
- Download URL: shieldctl-0.1.5-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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
31a42b6f380ccfe1b1f477eded44ae4228070205ea1b2c10ce8355cc14aaa2cc
|
|
| MD5 |
6efe867c22e2f0b8575680e72b20237a
|
|
| BLAKE2b-256 |
777730a5dc9130e02ec064b64d19eaaa3968e80dd2904a4f2e01075285c68623
|
Provenance
The following attestation bundles were made for shieldctl-0.1.5-py3-none-any.whl:
Publisher:
publish.yml on kolawoluu/shieldctl
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
shieldctl-0.1.5-py3-none-any.whl -
Subject digest:
31a42b6f380ccfe1b1f477eded44ae4228070205ea1b2c10ce8355cc14aaa2cc - Sigstore transparency entry: 1484658119
- Sigstore integration time:
-
Permalink:
kolawoluu/shieldctl@a501f823f5f218b8fd122b47d6a19316595bfb9c -
Branch / Tag:
refs/tags/v0.1.5 - Owner: https://github.com/kolawoluu
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@a501f823f5f218b8fd122b47d6a19316595bfb9c -
Trigger Event:
push
-
Statement type: