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
Metadata
Release files for shieldctl 0.1.6
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| shieldctl-0.1.6.tar.gz | 43.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| shieldctl-0.1.6-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 78.3 kB
Release files / shieldctl-0.1.6.tar.gz
| Download URL | shieldctl-0.1.6.tar.gz |
|---|---|
| Size | 43.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9d96daf230a55632a8fba347cb8d391f3467d20e70ba6a1c8cd6b2fff591a8b3
|
|
BLAKE2b-256 checksum How to use checksums |
734fae6188424c2e658c4878bccec5ecb90ff0b89444fc0f5c1de6526e63bc9f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 18, 2026.
Transparency logRelease files / shieldctl-0.1.6-py3-none-any.whl
| Download URL | shieldctl-0.1.6-py3-none-any.whl |
|---|---|
| Size | 34.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c0811349f28234508e7841915eb88ec27623a9f5fd02f8e91ad2ff94e21cc882
|
|
BLAKE2b-256 checksum How to use checksums |
1f9b24939c2acea62db6953f8e5a6ae6229e317ee49f6cfe0a5940ca146dd134
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 18, 2026.
Transparency log