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.
Files changed (47) hide show
  1. shieldctl-0.1.0/LICENSE +21 -0
  2. shieldctl-0.1.0/PKG-INFO +500 -0
  3. shieldctl-0.1.0/README.md +468 -0
  4. shieldctl-0.1.0/pyproject.toml +51 -0
  5. shieldctl-0.1.0/setup.cfg +4 -0
  6. shieldctl-0.1.0/shieldctl/__init__.py +0 -0
  7. shieldctl-0.1.0/shieldctl/cli.py +154 -0
  8. shieldctl-0.1.0/shieldctl/config.py +59 -0
  9. shieldctl-0.1.0/shieldctl/dispatcher.py +170 -0
  10. shieldctl-0.1.0/shieldctl/install.py +224 -0
  11. shieldctl-0.1.0/shieldctl/models.py +38 -0
  12. shieldctl-0.1.0/shieldctl/reporters/__init__.py +0 -0
  13. shieldctl-0.1.0/shieldctl/reporters/html_reporter.py +164 -0
  14. shieldctl-0.1.0/shieldctl/reporters/json_reporter.py +22 -0
  15. shieldctl-0.1.0/shieldctl/reporters/terminal.py +108 -0
  16. shieldctl-0.1.0/shieldctl/scanners/__init__.py +0 -0
  17. shieldctl-0.1.0/shieldctl/scanners/base.py +42 -0
  18. shieldctl-0.1.0/shieldctl/scanners/bash.py +159 -0
  19. shieldctl-0.1.0/shieldctl/scanners/dockerfile.py +61 -0
  20. shieldctl-0.1.0/shieldctl/scanners/gha.py +111 -0
  21. shieldctl-0.1.0/shieldctl/scanners/kubernetes.py +76 -0
  22. shieldctl-0.1.0/shieldctl/scanners/pip_audit.py +65 -0
  23. shieldctl-0.1.0/shieldctl/scanners/python_scanner.py +100 -0
  24. shieldctl-0.1.0/shieldctl/scanners/secrets.py +49 -0
  25. shieldctl-0.1.0/shieldctl/scanners/terraform.py +240 -0
  26. shieldctl-0.1.0/shieldctl/scanners/yaml_scanner.py +126 -0
  27. shieldctl-0.1.0/shieldctl.egg-info/PKG-INFO +500 -0
  28. shieldctl-0.1.0/shieldctl.egg-info/SOURCES.txt +45 -0
  29. shieldctl-0.1.0/shieldctl.egg-info/dependency_links.txt +1 -0
  30. shieldctl-0.1.0/shieldctl.egg-info/entry_points.txt +2 -0
  31. shieldctl-0.1.0/shieldctl.egg-info/requires.txt +7 -0
  32. shieldctl-0.1.0/shieldctl.egg-info/top_level.txt +1 -0
  33. shieldctl-0.1.0/tests/test_config.py +103 -0
  34. shieldctl-0.1.0/tests/test_dispatcher.py +301 -0
  35. shieldctl-0.1.0/tests/test_models.py +65 -0
  36. shieldctl-0.1.0/tests/test_reporter_html.py +135 -0
  37. shieldctl-0.1.0/tests/test_reporter_json.py +106 -0
  38. shieldctl-0.1.0/tests/test_scanner_bash.py +118 -0
  39. shieldctl-0.1.0/tests/test_scanner_bash_custom.py +194 -0
  40. shieldctl-0.1.0/tests/test_scanner_dockerfile.py +164 -0
  41. shieldctl-0.1.0/tests/test_scanner_gha.py +181 -0
  42. shieldctl-0.1.0/tests/test_scanner_kubernetes.py +159 -0
  43. shieldctl-0.1.0/tests/test_scanner_pip_audit.py +156 -0
  44. shieldctl-0.1.0/tests/test_scanner_python.py +232 -0
  45. shieldctl-0.1.0/tests/test_scanner_secrets.py +103 -0
  46. shieldctl-0.1.0/tests/test_scanner_terraform.py +206 -0
  47. shieldctl-0.1.0/tests/test_scanner_yaml.py +136 -0
@@ -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.
@@ -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