shieldctl 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- shieldctl-0.1.0/LICENSE +21 -0
- shieldctl-0.1.0/PKG-INFO +500 -0
- shieldctl-0.1.0/README.md +468 -0
- shieldctl-0.1.0/pyproject.toml +51 -0
- shieldctl-0.1.0/setup.cfg +4 -0
- shieldctl-0.1.0/shieldctl/__init__.py +0 -0
- shieldctl-0.1.0/shieldctl/cli.py +154 -0
- shieldctl-0.1.0/shieldctl/config.py +59 -0
- shieldctl-0.1.0/shieldctl/dispatcher.py +170 -0
- shieldctl-0.1.0/shieldctl/install.py +224 -0
- shieldctl-0.1.0/shieldctl/models.py +38 -0
- shieldctl-0.1.0/shieldctl/reporters/__init__.py +0 -0
- shieldctl-0.1.0/shieldctl/reporters/html_reporter.py +164 -0
- shieldctl-0.1.0/shieldctl/reporters/json_reporter.py +22 -0
- shieldctl-0.1.0/shieldctl/reporters/terminal.py +108 -0
- shieldctl-0.1.0/shieldctl/scanners/__init__.py +0 -0
- shieldctl-0.1.0/shieldctl/scanners/base.py +42 -0
- shieldctl-0.1.0/shieldctl/scanners/bash.py +159 -0
- shieldctl-0.1.0/shieldctl/scanners/dockerfile.py +61 -0
- shieldctl-0.1.0/shieldctl/scanners/gha.py +111 -0
- shieldctl-0.1.0/shieldctl/scanners/kubernetes.py +76 -0
- shieldctl-0.1.0/shieldctl/scanners/pip_audit.py +65 -0
- shieldctl-0.1.0/shieldctl/scanners/python_scanner.py +100 -0
- shieldctl-0.1.0/shieldctl/scanners/secrets.py +49 -0
- shieldctl-0.1.0/shieldctl/scanners/terraform.py +240 -0
- shieldctl-0.1.0/shieldctl/scanners/yaml_scanner.py +126 -0
- shieldctl-0.1.0/shieldctl.egg-info/PKG-INFO +500 -0
- shieldctl-0.1.0/shieldctl.egg-info/SOURCES.txt +45 -0
- shieldctl-0.1.0/shieldctl.egg-info/dependency_links.txt +1 -0
- shieldctl-0.1.0/shieldctl.egg-info/entry_points.txt +2 -0
- shieldctl-0.1.0/shieldctl.egg-info/requires.txt +7 -0
- shieldctl-0.1.0/shieldctl.egg-info/top_level.txt +1 -0
- shieldctl-0.1.0/tests/test_config.py +103 -0
- shieldctl-0.1.0/tests/test_dispatcher.py +301 -0
- shieldctl-0.1.0/tests/test_models.py +65 -0
- shieldctl-0.1.0/tests/test_reporter_html.py +135 -0
- shieldctl-0.1.0/tests/test_reporter_json.py +106 -0
- shieldctl-0.1.0/tests/test_scanner_bash.py +118 -0
- shieldctl-0.1.0/tests/test_scanner_bash_custom.py +194 -0
- shieldctl-0.1.0/tests/test_scanner_dockerfile.py +164 -0
- shieldctl-0.1.0/tests/test_scanner_gha.py +181 -0
- shieldctl-0.1.0/tests/test_scanner_kubernetes.py +159 -0
- shieldctl-0.1.0/tests/test_scanner_pip_audit.py +156 -0
- shieldctl-0.1.0/tests/test_scanner_python.py +232 -0
- shieldctl-0.1.0/tests/test_scanner_secrets.py +103 -0
- shieldctl-0.1.0/tests/test_scanner_terraform.py +206 -0
- shieldctl-0.1.0/tests/test_scanner_yaml.py +136 -0
shieldctl-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Tameed Engineering
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
shieldctl-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: shieldctl
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: CI-stage infrastructure security scanner — orchestrates checkov, tfsec, gitleaks, shellcheck, hadolint, actionlint, bandit, safety, and pip-audit into one command
|
|
5
|
+
Author-email: Tameed Engineering <eng@tameed.io>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/kolawoluu/shieldctl
|
|
8
|
+
Project-URL: Repository, https://github.com/kolawoluu/shieldctl
|
|
9
|
+
Project-URL: Bug Tracker, https://github.com/kolawoluu/shieldctl/issues
|
|
10
|
+
Keywords: security,terraform,kubernetes,dockerfile,ci,static-analysis,infrastructure
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: System Administrators
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Security
|
|
21
|
+
Classifier: Topic :: System :: Systems Administration
|
|
22
|
+
Requires-Python: >=3.11
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Requires-Dist: typer[all]>=0.12
|
|
26
|
+
Requires-Dist: rich>=13
|
|
27
|
+
Requires-Dist: pyyaml>=6
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
30
|
+
Requires-Dist: pytest-mock>=3; extra == "dev"
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
|
|
33
|
+
# shieldctl
|
|
34
|
+
|
|
35
|
+
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.
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
shieldctl scan ./infrastructure
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
→ checkov infrastructure/ (38 files)
|
|
43
|
+
→ tfsec infrastructure/ (38 files)
|
|
44
|
+
→ gitleaks . (repo-wide)
|
|
45
|
+
→ shellcheck scripts/ (4 files)
|
|
46
|
+
→ hadolint Dockerfile
|
|
47
|
+
→ actionlint .github/workflows/ci.yaml
|
|
48
|
+
→ pip-audit . (repo-wide)
|
|
49
|
+
|
|
50
|
+
CRITICAL .github/workflows/ci.yaml:14 CKV2_GHA_1 Top-level permissions set to write-all
|
|
51
|
+
HIGH infrastructure/gke.tf:22 AVD-GCP-0038 GKE node auto-upgrade disabled
|
|
52
|
+
HIGH infrastructure/iam.tf:8 AWS-IAM-109 IAM wildcard action on policy
|
|
53
|
+
MEDIUM scripts/deploy.sh:31 SH-001 curl piped directly to bash — supply chain risk
|
|
54
|
+
LOW Dockerfile:4 DL3008 Pin versions in apt-get install
|
|
55
|
+
──────────────────────────────────────────────────────────────────────────────────
|
|
56
|
+
5 findings (1 CRITICAL, 2 HIGH, 1 MEDIUM, 1 LOW) exit 3
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Why shieldctl
|
|
62
|
+
|
|
63
|
+
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:
|
|
64
|
+
|
|
65
|
+
- **One command** covers Terraform, secrets, shell scripts, YAML, Dockerfiles, Kubernetes, GitHub Actions, and Python dependencies
|
|
66
|
+
- **Consistent exit codes** so CI gates are trivial to configure
|
|
67
|
+
- **Unified finding model** — file, line, rule, severity, message, remediation, OWASP reference
|
|
68
|
+
- **Content-based detection** — finds Kubernetes manifests and GHA workflows regardless of their directory path
|
|
69
|
+
- **Built-in deduplication** — same finding from two tools appears once
|
|
70
|
+
|
|
71
|
+
Use shieldctl in CI. Use [proofctl](https://github.com/kolawoluu/proofctl) pre-commit for fast, zero-dependency linting.
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Installation
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
pip install shieldctl
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Or for development:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
git clone https://github.com/kolawoluu/shieldctl
|
|
85
|
+
cd shieldctl
|
|
86
|
+
pip install -e ".[dev]"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Requires:** Python 3.11+
|
|
90
|
+
|
|
91
|
+
Install the scanner dependencies:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
shieldctl install-tools
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Scanner dependencies
|
|
100
|
+
|
|
101
|
+
| Scanner | Tools | Installed via |
|
|
102
|
+
|---------|-------|---------------|
|
|
103
|
+
| Terraform | checkov, tfsec | `pip install checkov`, `brew install tfsec` |
|
|
104
|
+
| Secrets | gitleaks | `brew install gitleaks` |
|
|
105
|
+
| Shell | shellcheck | `brew install shellcheck` |
|
|
106
|
+
| YAML | yamllint, checkov | `pip install yamllint checkov` |
|
|
107
|
+
| Dockerfile | hadolint | `brew install hadolint` |
|
|
108
|
+
| Python | bandit, safety | `pip install bandit safety` |
|
|
109
|
+
| Kubernetes | checkov | `pip install checkov` |
|
|
110
|
+
| GitHub Actions | actionlint, checkov | `brew install actionlint`, `pip install checkov` |
|
|
111
|
+
| Python deps | pip-audit | `pip install pip-audit` |
|
|
112
|
+
|
|
113
|
+
Install all at once:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
shieldctl install-tools
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
shieldctl **gracefully skips** any scanner whose tool is not installed — it never hard-fails for a missing binary unless you pass `--strict`.
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Quick start
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
# Scan everything from the repo root
|
|
127
|
+
shieldctl scan .
|
|
128
|
+
|
|
129
|
+
# Scan a specific directory
|
|
130
|
+
shieldctl scan ./infrastructure
|
|
131
|
+
|
|
132
|
+
# Fail the build on HIGH or above
|
|
133
|
+
shieldctl scan . --fail-on HIGH
|
|
134
|
+
|
|
135
|
+
# Only run the Terraform scanner
|
|
136
|
+
shieldctl scan . --scanner terraform
|
|
137
|
+
|
|
138
|
+
# Scan only git-changed files (fast CI mode)
|
|
139
|
+
shieldctl scan . --changed-only
|
|
140
|
+
|
|
141
|
+
# Output JSON for downstream processing
|
|
142
|
+
shieldctl scan . --format json --output findings.json
|
|
143
|
+
|
|
144
|
+
# Generate an HTML dashboard
|
|
145
|
+
shieldctl scan . --format html --output report.html
|
|
146
|
+
|
|
147
|
+
# Fail immediately if any required scanner tool is missing
|
|
148
|
+
shieldctl scan . --strict
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## CI integration (GitHub Actions)
|
|
154
|
+
|
|
155
|
+
```yaml
|
|
156
|
+
name: shieldctl
|
|
157
|
+
|
|
158
|
+
on:
|
|
159
|
+
push:
|
|
160
|
+
branches: [main]
|
|
161
|
+
pull_request:
|
|
162
|
+
|
|
163
|
+
jobs:
|
|
164
|
+
security-scan:
|
|
165
|
+
runs-on: ubuntu-latest
|
|
166
|
+
steps:
|
|
167
|
+
- uses: actions/checkout@v4
|
|
168
|
+
with:
|
|
169
|
+
fetch-depth: 0 # gitleaks needs full history
|
|
170
|
+
|
|
171
|
+
- uses: actions/setup-python@v5
|
|
172
|
+
with:
|
|
173
|
+
python-version: "3.12"
|
|
174
|
+
|
|
175
|
+
- name: Install shieldctl and scanner tools
|
|
176
|
+
run: |
|
|
177
|
+
pip install shieldctl checkov yamllint bandit safety pip-audit
|
|
178
|
+
brew install tfsec gitleaks shellcheck hadolint actionlint
|
|
179
|
+
|
|
180
|
+
- name: Run shieldctl
|
|
181
|
+
run: shieldctl scan . --fail-on HIGH
|
|
182
|
+
|
|
183
|
+
- name: Generate HTML report
|
|
184
|
+
if: failure()
|
|
185
|
+
run: shieldctl scan . --format html --output shieldctl-report.html || true
|
|
186
|
+
|
|
187
|
+
- uses: actions/upload-artifact@v4
|
|
188
|
+
if: failure()
|
|
189
|
+
with:
|
|
190
|
+
name: shieldctl-report
|
|
191
|
+
path: shieldctl-report.html
|
|
192
|
+
retention-days: 14
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
For PR-only scans to keep CI fast:
|
|
196
|
+
|
|
197
|
+
```yaml
|
|
198
|
+
- name: Run shieldctl (changed files only)
|
|
199
|
+
run: shieldctl scan . --changed-only --fail-on HIGH
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## Scanners
|
|
205
|
+
|
|
206
|
+
### Terraform
|
|
207
|
+
|
|
208
|
+
Wraps **checkov** and **tfsec**. Scans `.tf`, `.tfvars`, and `terragrunt.hcl` files.
|
|
209
|
+
|
|
210
|
+
- checkov: 1000+ policies across AWS, GCP, Azure, Kubernetes
|
|
211
|
+
- tfsec: static analysis focused on cloud misconfigurations
|
|
212
|
+
- Findings deduplicated by `(file, line, rule)`
|
|
213
|
+
|
|
214
|
+
### Secrets
|
|
215
|
+
|
|
216
|
+
Wraps **gitleaks**. Runs repo-wide (not per-file) to detect secrets in:
|
|
217
|
+
- Source code and config files
|
|
218
|
+
- Git history (including previously committed and deleted secrets)
|
|
219
|
+
- `.env` files, CI configs, Terraform variable files
|
|
220
|
+
|
|
221
|
+
All secret findings are `CRITICAL` severity.
|
|
222
|
+
|
|
223
|
+
### Shell
|
|
224
|
+
|
|
225
|
+
Wraps **shellcheck** plus **6 custom rules** (`shieldctl-bash`):
|
|
226
|
+
|
|
227
|
+
| Rule | Severity | Description |
|
|
228
|
+
|------|----------|-------------|
|
|
229
|
+
| SH-001 | HIGH | `curl`/`wget` piped directly to a shell interpreter |
|
|
230
|
+
| SH-002 | HIGH | Hardcoded credential in shell script (`PASSWORD=`, `TOKEN=`, etc.) |
|
|
231
|
+
| SH-003 | MEDIUM | `eval` with a dynamic variable operand (command injection risk) |
|
|
232
|
+
| SH-004 | LOW | Script missing `set -euo pipefail` |
|
|
233
|
+
| SH-005 | MEDIUM | `rm -rf` with a variable path |
|
|
234
|
+
| SH-006 | MEDIUM | `chmod 777` (world-writable permissions) |
|
|
235
|
+
|
|
236
|
+
### YAML
|
|
237
|
+
|
|
238
|
+
Wraps **yamllint** for structural validation of all `.yml`/`.yaml` files.
|
|
239
|
+
|
|
240
|
+
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.
|
|
241
|
+
|
|
242
|
+
### Dockerfile
|
|
243
|
+
|
|
244
|
+
Wraps **hadolint**. Scans `Dockerfile`, `Dockerfile.*`, and `*.dockerfile` files.
|
|
245
|
+
|
|
246
|
+
Rule coverage includes: unpinned base images, root user, missing `--no-install-recommends`, secrets in `ARG`/`ENV`, inefficient layer caching, and supply chain risks.
|
|
247
|
+
|
|
248
|
+
### Python
|
|
249
|
+
|
|
250
|
+
Wraps **bandit** for SAST and **safety** for known CVE scanning of installed packages.
|
|
251
|
+
|
|
252
|
+
- bandit: detects injection, hardcoded passwords, weak crypto, use of `pickle`, `subprocess` misuse, and more
|
|
253
|
+
- safety: checks installed packages against the PyUp vulnerability database
|
|
254
|
+
|
|
255
|
+
### Kubernetes
|
|
256
|
+
|
|
257
|
+
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.
|
|
258
|
+
|
|
259
|
+
Covers Kubernetes Pod Security Standards, RBAC misconfigurations, missing resource limits, and insecure pod specs.
|
|
260
|
+
|
|
261
|
+
### GitHub Actions
|
|
262
|
+
|
|
263
|
+
Wraps **actionlint** and **checkov** (`--framework github_actions`). Files are detected by content (`on:` + `jobs:` at root level).
|
|
264
|
+
|
|
265
|
+
actionlint checks:
|
|
266
|
+
- Expression injection via `${{ github.event.* }}` in `run:` steps
|
|
267
|
+
- Invalid action references and syntax errors
|
|
268
|
+
- Shell script correctness inside `run:` blocks
|
|
269
|
+
|
|
270
|
+
checkov checks:
|
|
271
|
+
- `pull_request_target` with PR head checkout (pwn request pattern)
|
|
272
|
+
- Missing top-level `permissions:` block
|
|
273
|
+
- Unpinned third-party action versions
|
|
274
|
+
|
|
275
|
+
### Python dependencies (pip-audit)
|
|
276
|
+
|
|
277
|
+
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.
|
|
278
|
+
|
|
279
|
+
All pip-audit findings are `HIGH` severity.
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
## Configuration
|
|
284
|
+
|
|
285
|
+
Place `.shieldctl.yaml` in the scan root:
|
|
286
|
+
|
|
287
|
+
```yaml
|
|
288
|
+
# Minimum severity that causes a non-zero exit (INFO | LOW | MEDIUM | HIGH | CRITICAL)
|
|
289
|
+
fail_on: HIGH
|
|
290
|
+
|
|
291
|
+
# Glob patterns to exclude from scanning
|
|
292
|
+
exclude_paths:
|
|
293
|
+
- "**/.terraform/**"
|
|
294
|
+
- "**/.terragrunt-cache/**"
|
|
295
|
+
- "**/node_modules/**"
|
|
296
|
+
- "**/.git/**"
|
|
297
|
+
|
|
298
|
+
# Suppress specific rules globally
|
|
299
|
+
suppress:
|
|
300
|
+
- rule: CKV_GCP_29
|
|
301
|
+
reason: "Allow-all-egress on bastion is intentional — tracked in JIRA-1234"
|
|
302
|
+
- rule: CKV2_GHA_1
|
|
303
|
+
reason: "write-all permissions required for release workflow"
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
The `suppress` list accepts any rule ID from any scanner (checkov rule IDs, tfsec IDs, `SH-001`, etc.).
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## Suppressing a single finding
|
|
311
|
+
|
|
312
|
+
For checkov/tfsec Terraform rules, use the native inline skip:
|
|
313
|
+
|
|
314
|
+
```hcl
|
|
315
|
+
resource "google_storage_bucket" "assets" {
|
|
316
|
+
# checkov:skip=CKV_GCP_5:Public read for static assets is intentional
|
|
317
|
+
name = "my-public-assets"
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
For shell custom rules, add a comment on the flagged line:
|
|
322
|
+
|
|
323
|
+
```bash
|
|
324
|
+
eval "$generated_cmd" # shieldctl: ignore[SH-003]
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
---
|
|
328
|
+
|
|
329
|
+
## Commands
|
|
330
|
+
|
|
331
|
+
### `shieldctl scan`
|
|
332
|
+
|
|
333
|
+
```
|
|
334
|
+
shieldctl scan [PATH] [OPTIONS]
|
|
335
|
+
|
|
336
|
+
Options:
|
|
337
|
+
--fail-on INFO | LOW | MEDIUM | HIGH | CRITICAL
|
|
338
|
+
Overrides .shieldctl.yaml fail_on setting
|
|
339
|
+
--format / -f terminal | json | html (default: terminal)
|
|
340
|
+
--output / -o Write report to file
|
|
341
|
+
--changed-only Only scan git-changed files
|
|
342
|
+
--scanner Run a single scanner: terraform | secrets | bash |
|
|
343
|
+
yaml | dockerfile | python | kubernetes | gha | pip-audit
|
|
344
|
+
--strict Exit immediately if any required scanner tool is not installed
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### `shieldctl install-tools`
|
|
348
|
+
|
|
349
|
+
Install all scanner dependencies. Detects the OS and uses the appropriate package manager.
|
|
350
|
+
|
|
351
|
+
```bash
|
|
352
|
+
shieldctl install-tools # install everything
|
|
353
|
+
shieldctl install-tools --dry-run # show what would be installed
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
## Exit codes
|
|
359
|
+
|
|
360
|
+
| Code | Meaning |
|
|
361
|
+
|------|---------|
|
|
362
|
+
| `0` | No findings |
|
|
363
|
+
| `1` | Findings present, all below MEDIUM |
|
|
364
|
+
| `2` | MEDIUM findings present |
|
|
365
|
+
| `3` | HIGH or CRITICAL findings present (or `--fail-on` threshold exceeded) |
|
|
366
|
+
| `4` | Scanner error (tool crashed or `--strict` tool not found) |
|
|
367
|
+
|
|
368
|
+
These codes are designed for CI gate configuration:
|
|
369
|
+
|
|
370
|
+
```yaml
|
|
371
|
+
# Fail the build on HIGH+
|
|
372
|
+
- run: shieldctl scan . --fail-on HIGH
|
|
373
|
+
# Fail on any finding
|
|
374
|
+
- run: shieldctl scan . --fail-on INFO
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
---
|
|
378
|
+
|
|
379
|
+
## Output formats
|
|
380
|
+
|
|
381
|
+
**Terminal** (default) — colour-coded rich table grouped by severity.
|
|
382
|
+
|
|
383
|
+
```
|
|
384
|
+
CRITICAL .github/workflows/ci.yaml:14 CKV2_GHA_1 Top-level permissions set to write-all
|
|
385
|
+
HIGH infra/gke.tf:22 AVD-GCP-0038 GKE node auto-upgrade disabled
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
**JSON** — structured output for downstream tooling:
|
|
389
|
+
|
|
390
|
+
```json
|
|
391
|
+
{
|
|
392
|
+
"summary": {
|
|
393
|
+
"total": 5,
|
|
394
|
+
"CRITICAL": 1,
|
|
395
|
+
"HIGH": 2,
|
|
396
|
+
"MEDIUM": 1,
|
|
397
|
+
"LOW": 1
|
|
398
|
+
},
|
|
399
|
+
"findings": [
|
|
400
|
+
{
|
|
401
|
+
"file": ".github/workflows/ci.yaml",
|
|
402
|
+
"line": 14,
|
|
403
|
+
"rule": "CKV2_GHA_1",
|
|
404
|
+
"severity": "CRITICAL",
|
|
405
|
+
"message": "Top-level permissions are set to write-all",
|
|
406
|
+
"remediation": "Set permissions to read-only at top level and grant write only where needed",
|
|
407
|
+
"scanner": "checkov-gha",
|
|
408
|
+
"owasp": "A05:2021 – Security Misconfiguration"
|
|
409
|
+
}
|
|
410
|
+
]
|
|
411
|
+
}
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
**HTML** — self-contained single-file dashboard with severity summary cards, scanner breakdown, and a filterable findings table.
|
|
415
|
+
|
|
416
|
+
---
|
|
417
|
+
|
|
418
|
+
## Architecture
|
|
419
|
+
|
|
420
|
+
shieldctl is a thin orchestrator that shells out to external tools, parses their output into a unified `Finding` model, deduplicates, filters, and reports.
|
|
421
|
+
|
|
422
|
+
```
|
|
423
|
+
shieldctl/
|
|
424
|
+
├── cli.py # typer commands: scan, install-tools
|
|
425
|
+
├── config.py # .shieldctl.yaml loader
|
|
426
|
+
├── dispatcher.py # walks files, dispatches scanners
|
|
427
|
+
├── models.py # Finding dataclass + Severity enum
|
|
428
|
+
├── install.py # install-tools logic
|
|
429
|
+
├── scanners/
|
|
430
|
+
│ ├── base.py # BaseScanner ABC + ScannerError
|
|
431
|
+
│ ├── terraform.py # checkov + tfsec
|
|
432
|
+
│ ├── secrets.py # gitleaks (repo-wide)
|
|
433
|
+
│ ├── bash.py # shellcheck + custom SH-001–006
|
|
434
|
+
│ ├── yaml_scanner.py # yamllint + checkov (content-based K8s/GHA detection)
|
|
435
|
+
│ ├── dockerfile.py # hadolint
|
|
436
|
+
│ ├── python_scanner.py # bandit + safety
|
|
437
|
+
│ ├── kubernetes.py # checkov --framework kubernetes (content-based)
|
|
438
|
+
│ ├── gha.py # actionlint + checkov --framework github_actions
|
|
439
|
+
│ └── pip_audit.py # pip-audit (repo-wide)
|
|
440
|
+
└── reporters/
|
|
441
|
+
├── terminal.py
|
|
442
|
+
├── json_reporter.py
|
|
443
|
+
└── html_reporter.py
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
Each scanner implements the `BaseScanner` ABC:
|
|
447
|
+
|
|
448
|
+
```python
|
|
449
|
+
class BaseScanner(ABC):
|
|
450
|
+
extensions: list[str] = [] # empty = repo-wide
|
|
451
|
+
|
|
452
|
+
@abstractmethod
|
|
453
|
+
def run(self, paths: list[Path]) -> list[Finding]: ...
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
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.
|
|
457
|
+
|
|
458
|
+
---
|
|
459
|
+
|
|
460
|
+
## Development
|
|
461
|
+
|
|
462
|
+
```bash
|
|
463
|
+
pip install -e ".[dev]"
|
|
464
|
+
pytest tests/ -v
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
200 tests. All scanners are tested with mocked subprocess calls — no external tools required to run the test suite.
|
|
468
|
+
|
|
469
|
+
```bash
|
|
470
|
+
# Run tests for a specific scanner
|
|
471
|
+
pytest tests/test_scanner_kubernetes.py -v
|
|
472
|
+
pytest tests/test_scanner_bash_custom.py -v
|
|
473
|
+
pytest tests/test_scanner_pip_audit.py -v
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
### Adding a new scanner
|
|
477
|
+
|
|
478
|
+
1. Create `shieldctl/scanners/my_scanner.py` implementing `BaseScanner`
|
|
479
|
+
2. Register it in `dispatcher.py` `_ALL_SCANNERS` list
|
|
480
|
+
3. Add tests in `tests/test_scanner_my_scanner.py`
|
|
481
|
+
|
|
482
|
+
---
|
|
483
|
+
|
|
484
|
+
## Companion tool
|
|
485
|
+
|
|
486
|
+
[**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.
|
|
487
|
+
|
|
488
|
+
| | proofctl | shieldctl |
|
|
489
|
+
|---|---|---|
|
|
490
|
+
| **When to run** | Pre-commit | CI / pre-deploy |
|
|
491
|
+
| **Speed** | < 1s | 10–60s |
|
|
492
|
+
| **External tools** | None | checkov, tfsec, gitleaks, etc. |
|
|
493
|
+
| **Python analysis** | AST (precise, zero deps) | bandit (broader, subprocess) |
|
|
494
|
+
| **Audience** | Every developer | Platform / security team |
|
|
495
|
+
|
|
496
|
+
---
|
|
497
|
+
|
|
498
|
+
## License
|
|
499
|
+
|
|
500
|
+
MIT
|