Metadata-Version: 2.4
Name: shieldctl
Version: 0.1.2
Summary: CI-stage infrastructure security scanner — orchestrates checkov, tfsec, gitleaks, shellcheck, hadolint, actionlint, bandit, safety, and pip-audit into one command
Author-email: Kolawolu Odunola <kolawolu.o@gmail.com>
License: MIT
Project-URL: Homepage, https://github.com/kolawoluu/shieldctl
Project-URL: Repository, https://github.com/kolawoluu/shieldctl
Project-URL: Bug Tracker, https://github.com/kolawoluu/shieldctl/issues
Keywords: security,terraform,kubernetes,dockerfile,ci,static-analysis,infrastructure
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Security
Classifier: Topic :: System :: Systems Administration
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: typer[all]>=0.12
Requires-Dist: rich>=13
Requires-Dist: pyyaml>=6
Requires-Dist: bandit>=1.7
Requires-Dist: pip-audit>=2.7
Requires-Dist: checkov>=3
Requires-Dist: safety>=3
Requires-Dist: yamllint>=1.35
Requires-Dist: shellcheck-py>=0.10
Requires-Dist: hadolint-py>=2.14
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"
Requires-Dist: pytest-mock>=3; extra == "dev"
Dynamic: license-file

# 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](https://pypi.org/project/proofctl) pre-commit for fast, zero-dependency linting.

---

## Installation

```bash
pip install shieldctl
```

**Requires:** Python 3.11+

Install the scanner dependencies:

```bash
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:

```bash
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

```bash
# 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)

```yaml
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:

```yaml
      - 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:

```yaml
# 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:

```hcl
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:

```bash
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.

```bash
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:

```yaml
# 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:

```json
{
  "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.

---

## Architecture

shieldctl is a thin orchestrator that shells out to external tools, parses their output into a unified `Finding` model, deduplicates, filters, and reports.

```
shieldctl/
├── cli.py                     # typer commands: scan, install-tools
├── config.py                  # .shieldctl.yaml loader
├── dispatcher.py              # walks files, dispatches scanners
├── models.py                  # Finding dataclass + Severity enum
├── install.py                 # install-tools logic
├── scanners/
│   ├── base.py                # BaseScanner ABC + ScannerError
│   ├── terraform.py           # checkov + tfsec
│   ├── secrets.py             # gitleaks (repo-wide)
│   ├── bash.py                # shellcheck + custom SH-001–006
│   ├── yaml_scanner.py        # yamllint + checkov (content-based K8s/GHA detection)
│   ├── dockerfile.py          # hadolint
│   ├── python_scanner.py      # bandit + safety
│   ├── kubernetes.py          # checkov --framework kubernetes (content-based)
│   ├── gha.py                 # actionlint + checkov --framework github_actions
│   └── pip_audit.py           # pip-audit (repo-wide)
└── reporters/
    ├── terminal.py
    ├── json_reporter.py
    └── html_reporter.py
```

Each scanner implements the `BaseScanner` ABC:

```python
class BaseScanner(ABC):
    extensions: list[str] = []          # empty = repo-wide

    @abstractmethod
    def run(self, paths: list[Path]) -> list[Finding]: ...
```

Scanners with `extensions = []` (secrets, pip-audit) receive `[root]` and run once. Scanners with extensions receive only matching files. Content-based scanners (kubernetes, gha) filter their file list internally.

---

## Companion tool

[**proofctl**](https://github.com/kolawoluu/proofctl) — zero-dependency pre-commit linter for Python code quality, AI slop detection, and Terraform/Dockerfile/K8s/GHA policy checks. Runs in under a second with no external tools required.

| | proofctl | shieldctl |
|---|---|---|
| **When to run** | Pre-commit | CI / pre-deploy |
| **Speed** | < 1s | 10–60s |
| **External tools** | None | checkov, tfsec, gitleaks, etc. |
| **Python analysis** | AST (precise, zero deps) | bandit (broader, subprocess) |
| **Audience** | Every developer | Platform / security team |

---

## License

MIT
