Metadata-Version: 2.4
Name: spottersec
Version: 0.6.1
Summary: Kubernetes security scanner — attack paths, RBAC blast radius, interactive TUI
Author-email: SpotterSec <hello@spottersec.com>
License: MIT
Project-URL: Homepage, https://spottersec.com
Project-URL: Repository, https://github.com/spottersec/spotter
Project-URL: Bug Tracker, https://github.com/spottersec/spotter/issues
Keywords: kubernetes,security,k8s,rbac,scanner,devsecops,pentest,audit,attack-path
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Information Technology
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.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Security
Classifier: Topic :: System :: Systems Administration
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: PyYAML>=6.0
Requires-Dist: click>=8.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-cov; extra == "dev"
Dynamic: license-file

# SpotterSec

**Kubernetes security scanner that traces attack paths, not just misconfigs.**

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*.

```
spottersec audit --interactive
```

```
  23 CRITICAL · 65 HIGH · 137 MEDIUM · 84 LOW  (309 findings)
  Risk Score   99/100  F  Critical Risk

  ⚡ Dangerous combinations detected:
    +10 hostPath + hostNetwork = node filesystem + network sniff
    +12 hostPID + SYS_PTRACE = dump memory of any process
    +20 Privileged pod + cluster-admin = instant full cluster

  ▶ ClusterRoleBinding/cluster-reconciler-flux-system
  💀 CRITICAL  cluster-admin binding → ServiceAccount/kustomize-controller

  ▶ Pod/monitoring/node-exporter  (11 findings)
  💀 CRITICAL  hostPath mount on dangerous path: /
  💀 CRITICAL  hostPath mount on dangerous path: /proc
  🔴 HIGH      hostNetwork: true — host network namespace
```

---

## Install

```bash
pip install spotter
```

Or from source:

```bash
git clone https://github.com/spottersec/spotter
cd cli
pip install .
```

**Requirements:** Python 3.10+, `kubectl` in PATH for live cluster commands

---

## Quick Start

```bash
# Scan a YAML file
spottersec scan pod.yaml

# Scan your live cluster and browse findings interactively
spottersec audit --interactive

# Trace all attack paths from internet to cluster-admin
spottersec attack

# What can an attacker do with this pod's SA token?
spottersec blast-radius my-pod-name
```

---

## Commands

### `spottersec scan` — Scan YAML files

```bash
spottersec scan pod.yaml
spottersec scan ./k8s/ --explain --group
spottersec scan - --explain              # stdin — pipe from kubectl
```

**Flags:**

| Flag | Description |
|------|-------------|
| `--explain` | Show full attack chains and fix snippets |
| `--group` | Group findings by resource |
| `--output` | `table` (default) \| `json` \| `sarif` |
| `--fail-on` | Exit 1 if findings at this severity or above exist |
| `--severity` | Only show findings at or above this level |
| `--ignore` | Comma-separated rule IDs to suppress |
| `--fix` | Print auto-patched YAML to stdout |
| `--diff` | Show unified diff of what `--fix` would change |
| `--write-fix` | Overwrite file(s) with patched YAML in place |

**Pipe from kubectl:**

```bash
kubectl get pod mypod -o yaml | spottersec scan -
kubectl get deployments -A -o yaml | spottersec scan - --explain
kubectl get clusterrolebindings,clusterroles -A -o yaml | spottersec scan -
```

---

### `spottersec audit` — Live cluster scan

Fetches 18 resource types via `kubectl` and runs the full ruleset against your live cluster.

```bash
spottersec audit                        # all namespaces
spottersec audit -n production          # single namespace
spottersec audit --explain              # full attack chains + fixes
spottersec audit --skip-system-roles    # hide built-in k8s system roles
spottersec audit --interactive          # scan then drop into TUI
spottersec audit --output json > scan.json
```

---

### `spottersec attack` — Attack path analysis

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:

1. **Internet-exposed** — Ingress hosts + LoadBalancer/NodePort services with dangerous SAs
2. **Internal** — any pod with RCE and score ≥50, traced to full impact
3. **Residual SA risk** — completed Jobs whose SA still has dangerous RBAC bindings

```bash
spottersec attack
spottersec attack -n production
```

**Example output:**

```
INTERNET-EXPOSED PATHS  (1 found)

Path 1  ·  Score 85/100
Entry: Ingress/monitoring/grafana [grafana.example.com]
  ├─▶ SA/monitoring/grafana  (token theft — 85/100)
  ├─▶ kubectl get secrets -A  (Step 1 — exfil DB creds, TLS keys)
  └─▶ kubectl run pwn --overrides hostPID+hostNetwork  (Step 2 — node escape)

INTERNAL PATHS  (11 found)

Path 1  ·  Score 100/100
Entry: Pod/flux-system/kustomize-controller (RCE)
  ├─▶ SA/flux-system/kustomize-controller  (token theft — 100/100)
  └─▶ cluster-admin  (Full cluster compromise)

RESIDUAL SA RISK  (1 found)
  Job helm-install-traefik (completed) → SA still has cluster-admin
```

---

### `spottersec blast-radius` — RBAC blast radius

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.

```bash
spottersec blast-radius my-app                              # auto-detect pod/SA
spottersec blast-radius pod/production/api-server-abc123    # specific pod
spottersec blast-radius sa/flux-system/kustomize-controller # specific SA
```

**Example output:**

```
Target:  ServiceAccount/flux-system/kustomize-controller
Score:   100/100  CRITICAL — cluster compromise likely

Capabilities if compromised:
💀  FULL CLUSTER-ADMIN (verbs:* resources:*)

Active RBAC bindings:
  ▶ ClusterRoleBinding/cluster-reconciler-flux-system
    → ClusterRole/cluster-admin (scope: cluster)

Attacker steps after RCE:
  1. cat /var/run/secrets/kubernetes.io/serviceaccount/token
  2. export TOKEN=$(cat ...)
  3. kubectl get secrets -A --token=$TOKEN
  4. kubectl create clusterrolebinding pwned \
         --clusterrole=cluster-admin \
         --serviceaccount=flux-system:kustomize-controller
  5. Full cluster owned
```

---

### `spottersec tui` — Interactive finding browser

Browse all findings interactively. Per finding: press `f` for the exact `kubectl patch` fix, `e` for the exploit chain with runnable commands.

```bash
spottersec tui                  # scan live cluster, then browse
spottersec tui results.json     # load from saved scan
spottersec audit --interactive  # scan then drop into TUI
```

**Navigation:**

| Key | Action |
|-----|--------|
| `↑↓` / number | Navigate findings |
| `Enter` | View finding detail |
| `f` | Fix view — exact kubectl patch for this resource |
| `e` | Exploit view — attacker commands |
| `←→` | Previous / next finding in detail view |
| `b` | Back to list |
| `q` | Quit |

---

### `spottersec fix` — Interactive patch approval

Review and apply fixes with a diff before each change.

```bash
spottersec fix ./k8s/        # review each fix before applying
spottersec fix ./k8s/ --yes  # apply all without prompting (CI)
```

---

### `spottersec watch` — Re-scan on save

```bash
spottersec watch pod.yaml
spottersec watch ./k8s/ --explain
```

---

### `spottersec trend` — Compare two scans

```bash
spottersec scan ./k8s/ --output json > baseline.json
# ... make changes ...
spottersec scan ./k8s/ --output json > current.json
spottersec trend baseline.json current.json
```

```
↓ 3 fixed    ↑ 1 new    = 18 unchanged
Risk: 72 → 61  (-11)  C → C
```

---

### `spottersec rules` — List all rules

```bash
spottersec rules
spottersec rules --severity high
```

---

## Rule Reference

50 rules across pods, RBAC, network, ingress, secrets, webhooks, and Helm.

### Container Security (SC)

| Rule | Severity | Description |
|------|----------|-------------|
| SC001 | 💀 CRITICAL | `privileged: true` — full container escape path |
| SC002 | 🔴 HIGH | `runAsUser: 0` — explicit root |
| SC003 | 🔴 HIGH | `runAsNonRoot: false` |
| SC004 | 🟡 MEDIUM | `runAsNonRoot` not set |
| SC005 | 🔴 HIGH | `allowPrivilegeEscalation: true` |
| SC006 | 🔵 LOW | `allowPrivilegeEscalation` not set |
| SC007 | 🔵 LOW | `readOnlyRootFilesystem` not set |
| SC008 | 🔵 LOW | No seccomp profile |

### Volumes (VOL)

| Rule | Severity | Description |
|------|----------|-------------|
| VOL001 | 💀 CRITICAL | `hostPath` mount on dangerous path (`/`, `/etc`, `/proc`, `/sys`, `/var/run`) |

### Namespace / Host (NS)

| Rule | Severity | Description |
|------|----------|-------------|
| NS001 | 🔴 HIGH | `hostPID: true` — process namespace sharing |
| NS002 | 🔴 HIGH | `hostNetwork: true` — host network namespace |
| NS003 | 🟡 MEDIUM | `hostIPC: true` — host IPC namespace |
| NS010 | 🔴 HIGH | Namespace missing Pod Security Admission enforce label |
| NS011 | 🔴 HIGH | Namespace PSA enforce level is `privileged` |

### RBAC

| Rule | Severity | Description |
|------|----------|-------------|
| RBAC001 | 💀 CRITICAL | `cluster-admin` binding |
| RBAC002 | 🔴 HIGH | Wildcard verbs on pod/workload resources |
| RBAC003 | 🔴 HIGH | Wildcard resources in RBAC rule |
| RBAC004 | 🔴 HIGH | Role grants Secrets read access |
| RBAC005 | 🔴 HIGH | Binding to built-in privileged role |
| RBAC006 | 💀 CRITICAL | ClusterRole with full wildcard (`verbs:* resources:* apiGroups:*`) |
| RBAC007 | 🔴 HIGH | Role can create/modify pods — escalation path |
| RBAC008 | 💀 CRITICAL | Role can write RBAC — self-grant escalation |
| RBAC009 | 💀 CRITICAL | Role grants impersonation |

### Service Accounts (SA)

| Rule | Severity | Description |
|------|----------|-------------|
| SA001 | 🟡 MEDIUM | SA token auto-mounted |
| SA002 | 🟡 MEDIUM | Uses `default` ServiceAccount |

### Capabilities (CAP)

| Rule | Severity | Description |
|------|----------|-------------|
| CAP001_* | 💀/🔴 varies | Dangerous Linux capability added (NET_ADMIN, SYS_PTRACE, etc.) |
| CAP002 | 🟡 MEDIUM | `capabilities.drop: [ALL]` missing |

### Network (NET)

| Rule | Severity | Description |
|------|----------|-------------|
| NET001 | 🟡 MEDIUM | No NetworkPolicy covers workload |
| NET002 | 🟡 MEDIUM | NetworkPolicy has catch-all ingress |
| NET003 | 🟡 MEDIUM | NetworkPolicy has no egress restriction |

### Services (SVC)

| Rule | Severity | Description |
|------|----------|-------------|
| SVC001 | 🟡 MEDIUM | Service type LoadBalancer — public IP |
| SVC002 | 🔴 HIGH | `externalIPs` set — CVE-2020-8554 |
| SVC003 | 🔵 LOW | Service type NodePort |

### Ingress (ING)

| Rule | Severity | Description |
|------|----------|-------------|
| ING001 | 🟡 MEDIUM | Ingress host with no TLS |
| ING002 | 🟡 MEDIUM | Ingress wildcard/missing host |
| ING003 | 🔵 LOW | Ingress has no auth annotation |

### Admission Webhooks (WHK)

| Rule | Severity | Description |
|------|----------|-------------|
| WHK001 | 🔴 HIGH | Webhook `failurePolicy: Ignore` — misconfigs bypass admission |
| WHK002 | 🔵 LOW | Webhook high timeout |
| WHK003 | 🟡 MEDIUM | Webhook intercepts all resources and operations |

### Environment / ConfigMap (ENV)

| Rule | Severity | Description |
|------|----------|-------------|
| ENV001 | 🔴 HIGH | Hardcoded credential in container env var |
| ENV002 | 🔴 HIGH | Credential stored in ConfigMap (should be a Secret) |

### etcd (ETCD)

| Rule | Severity | Description |
|------|----------|-------------|
| ETCD001 | 💀 CRITICAL | etcd running without TLS |
| ETCD002 | 💀 CRITICAL | etcd bound to `0.0.0.0` — publicly accessible |
| ETCD003 | 🟡 MEDIUM | Secrets not encrypted at rest |

### Images (IMG)

| Rule | Severity | Description |
|------|----------|-------------|
| IMG001 | 🔵 LOW | Mutable image tag (`:latest` or no tag) |

### Resources (RES)

| Rule | Severity | Description |
|------|----------|-------------|
| RES001 | 🔵 LOW | No resource limits set |

### Helm (HLM)

| Rule | Severity | Description |
|------|----------|-------------|
| HLM001 | 🔴 HIGH | Hardcoded secret in Helm values |
| HLM002 | 🔴 HIGH | `auth.enabled: false` in Helm values |

### Availability (PDB)

| Rule | Severity | Description |
|------|----------|-------------|
| PDB001 | 🟡 MEDIUM | Single replica — no redundancy |
| PDB002 | 🔵 LOW | No PodDisruptionBudget |

---

## Config File

Commit a `.spottersec.yaml` to your repo to set defaults for the whole team.

```yaml
# .spottersec.yaml
scan:
  fail_on: high          # CI: exit 1 on HIGH or CRITICAL
  severity: medium       # show MEDIUM+ findings
  ignore:
    - NET001             # NetworkPolicy managed externally
    - SC008              # seccomp set by admission controller
  paths:
    - ./k8s/
    - ./helm/templates/
```

SpotterSec checks `.spottersec.yaml` in the current directory, then `~/.spottersec/config.yaml`. CLI flags always override.

---

## CI/CD Integration

### GitHub Actions

```yaml
# .github/workflows/k8s-security.yml
name: K8s Security Scan
on: [pull_request]

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - name: Install SpotterSec
      run: pip install spotter
    - name: Scan K8s manifests
      run: spottersec scan ./k8s/ --fail-on high --output sarif
    - uses: github/codeql-action/upload-sarif@v3
      with:
        sarif_file: spottersec.sarif
      # Findings appear as annotations in GitHub Security tab
```

### GitLab CI

```yaml
k8s-security:
  image: python:3.12-slim
  script:
    - pip install spotter
    - spottersec scan ./k8s/ --fail-on high
```

---

## Learn More

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.

---

## License

MIT
