spottersec 0.6.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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 SpotterSec
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,491 @@
1
+ Metadata-Version: 2.4
2
+ Name: spottersec
3
+ Version: 0.6.0
4
+ Summary: Kubernetes security scanner — attack paths, RBAC blast radius, interactive TUI
5
+ Author-email: SpotterSec <hello@spottersec.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://spottersec.com
8
+ Project-URL: Repository, https://github.com/spottersec/spotter
9
+ Project-URL: Bug Tracker, https://github.com/spottersec/spotter/issues
10
+ Keywords: kubernetes,security,k8s,rbac,scanner,devsecops,pentest,audit,attack-path
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: Information Technology
15
+ Classifier: Intended Audience :: System Administrators
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Topic :: Security
23
+ Classifier: Topic :: System :: Systems Administration
24
+ Requires-Python: >=3.10
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Requires-Dist: PyYAML>=6.0
28
+ Requires-Dist: click>=8.0
29
+ Provides-Extra: dev
30
+ Requires-Dist: pytest>=7.0; extra == "dev"
31
+ Requires-Dist: pytest-cov; extra == "dev"
32
+ Dynamic: license-file
33
+
34
+ # SpotterSec
35
+
36
+ **Kubernetes security scanner that traces attack paths, not just misconfigs.**
37
+
38
+ SpotterSec goes beyond linting — it finds exploitable chains from internet exposure through SA token theft to cluster-admin, scores RBAC blast radius per service account, and gives you the exact `kubectl patch` command to fix each finding. Built for security engineers and operators who want to understand *what an attacker would actually do*.
39
+
40
+ ```
41
+ spottersec audit --interactive
42
+ ```
43
+
44
+ ```
45
+ 23 CRITICAL · 65 HIGH · 137 MEDIUM · 84 LOW (309 findings)
46
+ Risk Score 99/100 F Critical Risk
47
+
48
+ ⚡ Dangerous combinations detected:
49
+ +10 hostPath + hostNetwork = node filesystem + network sniff
50
+ +12 hostPID + SYS_PTRACE = dump memory of any process
51
+ +20 Privileged pod + cluster-admin = instant full cluster
52
+
53
+ ▶ ClusterRoleBinding/cluster-reconciler-flux-system
54
+ 💀 CRITICAL cluster-admin binding → ServiceAccount/kustomize-controller
55
+
56
+ ▶ Pod/monitoring/node-exporter (11 findings)
57
+ 💀 CRITICAL hostPath mount on dangerous path: /
58
+ 💀 CRITICAL hostPath mount on dangerous path: /proc
59
+ 🔴 HIGH hostNetwork: true — host network namespace
60
+ ```
61
+
62
+ ---
63
+
64
+ ## Install
65
+
66
+ ```bash
67
+ pip install spotter
68
+ ```
69
+
70
+ Or from source:
71
+
72
+ ```bash
73
+ git clone https://github.com/spottersec/spotter
74
+ cd cli
75
+ pip install .
76
+ ```
77
+
78
+ **Requirements:** Python 3.10+, `kubectl` in PATH for live cluster commands
79
+
80
+ ---
81
+
82
+ ## Quick Start
83
+
84
+ ```bash
85
+ # Scan a YAML file
86
+ spottersec scan pod.yaml
87
+
88
+ # Scan your live cluster and browse findings interactively
89
+ spottersec audit --interactive
90
+
91
+ # Trace all attack paths from internet to cluster-admin
92
+ spottersec attack
93
+
94
+ # What can an attacker do with this pod's SA token?
95
+ spottersec blast-radius my-pod-name
96
+ ```
97
+
98
+ ---
99
+
100
+ ## Commands
101
+
102
+ ### `spottersec scan` — Scan YAML files
103
+
104
+ ```bash
105
+ spottersec scan pod.yaml
106
+ spottersec scan ./k8s/ --explain --group
107
+ spottersec scan - --explain # stdin — pipe from kubectl
108
+ ```
109
+
110
+ **Flags:**
111
+
112
+ | Flag | Description |
113
+ |------|-------------|
114
+ | `--explain` | Show full attack chains and fix snippets |
115
+ | `--group` | Group findings by resource |
116
+ | `--output` | `table` (default) \| `json` \| `sarif` |
117
+ | `--fail-on` | Exit 1 if findings at this severity or above exist |
118
+ | `--severity` | Only show findings at or above this level |
119
+ | `--ignore` | Comma-separated rule IDs to suppress |
120
+ | `--fix` | Print auto-patched YAML to stdout |
121
+ | `--diff` | Show unified diff of what `--fix` would change |
122
+ | `--write-fix` | Overwrite file(s) with patched YAML in place |
123
+
124
+ **Pipe from kubectl:**
125
+
126
+ ```bash
127
+ kubectl get pod mypod -o yaml | spottersec scan -
128
+ kubectl get deployments -A -o yaml | spottersec scan - --explain
129
+ kubectl get clusterrolebindings,clusterroles -A -o yaml | spottersec scan -
130
+ ```
131
+
132
+ ---
133
+
134
+ ### `spottersec audit` — Live cluster scan
135
+
136
+ Fetches 18 resource types via `kubectl` and runs the full ruleset against your live cluster.
137
+
138
+ ```bash
139
+ spottersec audit # all namespaces
140
+ spottersec audit -n production # single namespace
141
+ spottersec audit --explain # full attack chains + fixes
142
+ spottersec audit --skip-system-roles # hide built-in k8s system roles
143
+ spottersec audit --interactive # scan then drop into TUI
144
+ spottersec audit --output json > scan.json
145
+ ```
146
+
147
+ ---
148
+
149
+ ### `spottersec attack` — Attack path analysis
150
+
151
+ Builds a full cluster graph and traces every exploitable path from internet-exposed entry points through SA token theft to cluster-admin. Three categories of paths:
152
+
153
+ 1. **Internet-exposed** — Ingress hosts + LoadBalancer/NodePort services with dangerous SAs
154
+ 2. **Internal** — any pod with RCE and score ≥50, traced to full impact
155
+ 3. **Residual SA risk** — completed Jobs whose SA still has dangerous RBAC bindings
156
+
157
+ ```bash
158
+ spottersec attack
159
+ spottersec attack -n production
160
+ ```
161
+
162
+ **Example output:**
163
+
164
+ ```
165
+ INTERNET-EXPOSED PATHS (1 found)
166
+
167
+ Path 1 · Score 85/100
168
+ Entry: Ingress/monitoring/grafana [grafana.example.com]
169
+ ├─▶ SA/monitoring/grafana (token theft — 85/100)
170
+ ├─▶ kubectl get secrets -A (Step 1 — exfil DB creds, TLS keys)
171
+ └─▶ kubectl run pwn --overrides hostPID+hostNetwork (Step 2 — node escape)
172
+
173
+ INTERNAL PATHS (11 found)
174
+
175
+ Path 1 · Score 100/100
176
+ Entry: Pod/flux-system/kustomize-controller (RCE)
177
+ ├─▶ SA/flux-system/kustomize-controller (token theft — 100/100)
178
+ └─▶ cluster-admin (Full cluster compromise)
179
+
180
+ RESIDUAL SA RISK (1 found)
181
+ Job helm-install-traefik (completed) → SA still has cluster-admin
182
+ ```
183
+
184
+ ---
185
+
186
+ ### `spottersec blast-radius` — RBAC blast radius
187
+
188
+ Given any pod or service account: what can an attacker do with that SA token? Resolves all RBAC bindings, scores 0–100, and shows the exact exploit steps.
189
+
190
+ ```bash
191
+ spottersec blast-radius my-app # auto-detect pod/SA
192
+ spottersec blast-radius pod/production/api-server-abc123 # specific pod
193
+ spottersec blast-radius sa/flux-system/kustomize-controller # specific SA
194
+ ```
195
+
196
+ **Example output:**
197
+
198
+ ```
199
+ Target: ServiceAccount/flux-system/kustomize-controller
200
+ Score: 100/100 CRITICAL — cluster compromise likely
201
+
202
+ Capabilities if compromised:
203
+ 💀 FULL CLUSTER-ADMIN (verbs:* resources:*)
204
+
205
+ Active RBAC bindings:
206
+ ▶ ClusterRoleBinding/cluster-reconciler-flux-system
207
+ → ClusterRole/cluster-admin (scope: cluster)
208
+
209
+ Attacker steps after RCE:
210
+ 1. cat /var/run/secrets/kubernetes.io/serviceaccount/token
211
+ 2. export TOKEN=$(cat ...)
212
+ 3. kubectl get secrets -A --token=$TOKEN
213
+ 4. kubectl create clusterrolebinding pwned \
214
+ --clusterrole=cluster-admin \
215
+ --serviceaccount=flux-system:kustomize-controller
216
+ 5. Full cluster owned
217
+ ```
218
+
219
+ ---
220
+
221
+ ### `spottersec tui` — Interactive finding browser
222
+
223
+ Browse all findings interactively. Per finding: press `f` for the exact `kubectl patch` fix, `e` for the exploit chain with runnable commands.
224
+
225
+ ```bash
226
+ spottersec tui # scan live cluster, then browse
227
+ spottersec tui results.json # load from saved scan
228
+ spottersec audit --interactive # scan then drop into TUI
229
+ ```
230
+
231
+ **Navigation:**
232
+
233
+ | Key | Action |
234
+ |-----|--------|
235
+ | `↑↓` / number | Navigate findings |
236
+ | `Enter` | View finding detail |
237
+ | `f` | Fix view — exact kubectl patch for this resource |
238
+ | `e` | Exploit view — attacker commands |
239
+ | `←→` | Previous / next finding in detail view |
240
+ | `b` | Back to list |
241
+ | `q` | Quit |
242
+
243
+ ---
244
+
245
+ ### `spottersec fix` — Interactive patch approval
246
+
247
+ Review and apply fixes with a diff before each change.
248
+
249
+ ```bash
250
+ spottersec fix ./k8s/ # review each fix before applying
251
+ spottersec fix ./k8s/ --yes # apply all without prompting (CI)
252
+ ```
253
+
254
+ ---
255
+
256
+ ### `spottersec watch` — Re-scan on save
257
+
258
+ ```bash
259
+ spottersec watch pod.yaml
260
+ spottersec watch ./k8s/ --explain
261
+ ```
262
+
263
+ ---
264
+
265
+ ### `spottersec trend` — Compare two scans
266
+
267
+ ```bash
268
+ spottersec scan ./k8s/ --output json > baseline.json
269
+ # ... make changes ...
270
+ spottersec scan ./k8s/ --output json > current.json
271
+ spottersec trend baseline.json current.json
272
+ ```
273
+
274
+ ```
275
+ ↓ 3 fixed ↑ 1 new = 18 unchanged
276
+ Risk: 72 → 61 (-11) C → C
277
+ ```
278
+
279
+ ---
280
+
281
+ ### `spottersec rules` — List all rules
282
+
283
+ ```bash
284
+ spottersec rules
285
+ spottersec rules --severity high
286
+ ```
287
+
288
+ ---
289
+
290
+ ## Rule Reference
291
+
292
+ 50 rules across pods, RBAC, network, ingress, secrets, webhooks, and Helm.
293
+
294
+ ### Container Security (SC)
295
+
296
+ | Rule | Severity | Description |
297
+ |------|----------|-------------|
298
+ | SC001 | 💀 CRITICAL | `privileged: true` — full container escape path |
299
+ | SC002 | 🔴 HIGH | `runAsUser: 0` — explicit root |
300
+ | SC003 | 🔴 HIGH | `runAsNonRoot: false` |
301
+ | SC004 | 🟡 MEDIUM | `runAsNonRoot` not set |
302
+ | SC005 | 🔴 HIGH | `allowPrivilegeEscalation: true` |
303
+ | SC006 | 🔵 LOW | `allowPrivilegeEscalation` not set |
304
+ | SC007 | 🔵 LOW | `readOnlyRootFilesystem` not set |
305
+ | SC008 | 🔵 LOW | No seccomp profile |
306
+
307
+ ### Volumes (VOL)
308
+
309
+ | Rule | Severity | Description |
310
+ |------|----------|-------------|
311
+ | VOL001 | 💀 CRITICAL | `hostPath` mount on dangerous path (`/`, `/etc`, `/proc`, `/sys`, `/var/run`) |
312
+
313
+ ### Namespace / Host (NS)
314
+
315
+ | Rule | Severity | Description |
316
+ |------|----------|-------------|
317
+ | NS001 | 🔴 HIGH | `hostPID: true` — process namespace sharing |
318
+ | NS002 | 🔴 HIGH | `hostNetwork: true` — host network namespace |
319
+ | NS003 | 🟡 MEDIUM | `hostIPC: true` — host IPC namespace |
320
+ | NS010 | 🔴 HIGH | Namespace missing Pod Security Admission enforce label |
321
+ | NS011 | 🔴 HIGH | Namespace PSA enforce level is `privileged` |
322
+
323
+ ### RBAC
324
+
325
+ | Rule | Severity | Description |
326
+ |------|----------|-------------|
327
+ | RBAC001 | 💀 CRITICAL | `cluster-admin` binding |
328
+ | RBAC002 | 🔴 HIGH | Wildcard verbs on pod/workload resources |
329
+ | RBAC003 | 🔴 HIGH | Wildcard resources in RBAC rule |
330
+ | RBAC004 | 🔴 HIGH | Role grants Secrets read access |
331
+ | RBAC005 | 🔴 HIGH | Binding to built-in privileged role |
332
+ | RBAC006 | 💀 CRITICAL | ClusterRole with full wildcard (`verbs:* resources:* apiGroups:*`) |
333
+ | RBAC007 | 🔴 HIGH | Role can create/modify pods — escalation path |
334
+ | RBAC008 | 💀 CRITICAL | Role can write RBAC — self-grant escalation |
335
+ | RBAC009 | 💀 CRITICAL | Role grants impersonation |
336
+
337
+ ### Service Accounts (SA)
338
+
339
+ | Rule | Severity | Description |
340
+ |------|----------|-------------|
341
+ | SA001 | 🟡 MEDIUM | SA token auto-mounted |
342
+ | SA002 | 🟡 MEDIUM | Uses `default` ServiceAccount |
343
+
344
+ ### Capabilities (CAP)
345
+
346
+ | Rule | Severity | Description |
347
+ |------|----------|-------------|
348
+ | CAP001_* | 💀/🔴 varies | Dangerous Linux capability added (NET_ADMIN, SYS_PTRACE, etc.) |
349
+ | CAP002 | 🟡 MEDIUM | `capabilities.drop: [ALL]` missing |
350
+
351
+ ### Network (NET)
352
+
353
+ | Rule | Severity | Description |
354
+ |------|----------|-------------|
355
+ | NET001 | 🟡 MEDIUM | No NetworkPolicy covers workload |
356
+ | NET002 | 🟡 MEDIUM | NetworkPolicy has catch-all ingress |
357
+ | NET003 | 🟡 MEDIUM | NetworkPolicy has no egress restriction |
358
+
359
+ ### Services (SVC)
360
+
361
+ | Rule | Severity | Description |
362
+ |------|----------|-------------|
363
+ | SVC001 | 🟡 MEDIUM | Service type LoadBalancer — public IP |
364
+ | SVC002 | 🔴 HIGH | `externalIPs` set — CVE-2020-8554 |
365
+ | SVC003 | 🔵 LOW | Service type NodePort |
366
+
367
+ ### Ingress (ING)
368
+
369
+ | Rule | Severity | Description |
370
+ |------|----------|-------------|
371
+ | ING001 | 🟡 MEDIUM | Ingress host with no TLS |
372
+ | ING002 | 🟡 MEDIUM | Ingress wildcard/missing host |
373
+ | ING003 | 🔵 LOW | Ingress has no auth annotation |
374
+
375
+ ### Admission Webhooks (WHK)
376
+
377
+ | Rule | Severity | Description |
378
+ |------|----------|-------------|
379
+ | WHK001 | 🔴 HIGH | Webhook `failurePolicy: Ignore` — misconfigs bypass admission |
380
+ | WHK002 | 🔵 LOW | Webhook high timeout |
381
+ | WHK003 | 🟡 MEDIUM | Webhook intercepts all resources and operations |
382
+
383
+ ### Environment / ConfigMap (ENV)
384
+
385
+ | Rule | Severity | Description |
386
+ |------|----------|-------------|
387
+ | ENV001 | 🔴 HIGH | Hardcoded credential in container env var |
388
+ | ENV002 | 🔴 HIGH | Credential stored in ConfigMap (should be a Secret) |
389
+
390
+ ### etcd (ETCD)
391
+
392
+ | Rule | Severity | Description |
393
+ |------|----------|-------------|
394
+ | ETCD001 | 💀 CRITICAL | etcd running without TLS |
395
+ | ETCD002 | 💀 CRITICAL | etcd bound to `0.0.0.0` — publicly accessible |
396
+ | ETCD003 | 🟡 MEDIUM | Secrets not encrypted at rest |
397
+
398
+ ### Images (IMG)
399
+
400
+ | Rule | Severity | Description |
401
+ |------|----------|-------------|
402
+ | IMG001 | 🔵 LOW | Mutable image tag (`:latest` or no tag) |
403
+
404
+ ### Resources (RES)
405
+
406
+ | Rule | Severity | Description |
407
+ |------|----------|-------------|
408
+ | RES001 | 🔵 LOW | No resource limits set |
409
+
410
+ ### Helm (HLM)
411
+
412
+ | Rule | Severity | Description |
413
+ |------|----------|-------------|
414
+ | HLM001 | 🔴 HIGH | Hardcoded secret in Helm values |
415
+ | HLM002 | 🔴 HIGH | `auth.enabled: false` in Helm values |
416
+
417
+ ### Availability (PDB)
418
+
419
+ | Rule | Severity | Description |
420
+ |------|----------|-------------|
421
+ | PDB001 | 🟡 MEDIUM | Single replica — no redundancy |
422
+ | PDB002 | 🔵 LOW | No PodDisruptionBudget |
423
+
424
+ ---
425
+
426
+ ## Config File
427
+
428
+ Commit a `.spottersec.yaml` to your repo to set defaults for the whole team.
429
+
430
+ ```yaml
431
+ # .spottersec.yaml
432
+ scan:
433
+ fail_on: high # CI: exit 1 on HIGH or CRITICAL
434
+ severity: medium # show MEDIUM+ findings
435
+ ignore:
436
+ - NET001 # NetworkPolicy managed externally
437
+ - SC008 # seccomp set by admission controller
438
+ paths:
439
+ - ./k8s/
440
+ - ./helm/templates/
441
+ ```
442
+
443
+ SpotterSec checks `.spottersec.yaml` in the current directory, then `~/.spottersec/config.yaml`. CLI flags always override.
444
+
445
+ ---
446
+
447
+ ## CI/CD Integration
448
+
449
+ ### GitHub Actions
450
+
451
+ ```yaml
452
+ # .github/workflows/k8s-security.yml
453
+ name: K8s Security Scan
454
+ on: [pull_request]
455
+
456
+ jobs:
457
+ scan:
458
+ runs-on: ubuntu-latest
459
+ steps:
460
+ - uses: actions/checkout@v4
461
+ - name: Install SpotterSec
462
+ run: pip install spotter
463
+ - name: Scan K8s manifests
464
+ run: spottersec scan ./k8s/ --fail-on high --output sarif
465
+ - uses: github/codeql-action/upload-sarif@v3
466
+ with:
467
+ sarif_file: spottersec.sarif
468
+ # Findings appear as annotations in GitHub Security tab
469
+ ```
470
+
471
+ ### GitLab CI
472
+
473
+ ```yaml
474
+ k8s-security:
475
+ image: python:3.12-slim
476
+ script:
477
+ - pip install spotter
478
+ - spottersec scan ./k8s/ --fail-on high
479
+ ```
480
+
481
+ ---
482
+
483
+ ## Learn More
484
+
485
+ SpotterSec CLI is the companion tool to [spottersec.com](https://spottersec.com) — an interactive Kubernetes attack path learning platform with 10 scenarios covering the most common K8s compromise chains.
486
+
487
+ ---
488
+
489
+ ## License
490
+
491
+ MIT