diff --git a/index.yaml b/index.yaml index f038f59a..f4a74803 100644 --- a/index.yaml +++ b/index.yaml @@ -5,8 +5,8 @@ meta: version: "1.0.0" - last_updated: "2026-03-05" - skill_count: 45 +last_updated: "2026-06-15" + skill_count: 47 role_count: 5 tag_vocabulary: @@ -596,3 +596,27 @@ roles: description: "Cloud security posture, IaC review, container security, identity" skills: [aws-review, azure-review, gcp-review, iac-security, container-security, zero-trust-assessment, privileged-access] file: roles/cloud-security-engineer/SKILL.md + + - id: tenant-aware-cache-key-review + name: "Tenant-Aware Cache Key Review" + tags: [secops, cache, multi-tenant, code-review] + role: [security-engineer, appsec-engineer, backend-developer] + phase: [build, review, operate] + activity: [review, assess] + frameworks: [OWASP-API-Security-2023, NIST-SP-800-145, RFC-9110] + difficulty: intermediate + time_estimate: "30-60min" + file: skills/secops/cache-key-review/SKILL.md + compatible_tools: [claude-code, gemini-cli, cursor, codex-cli, openclaw, kiro] + + - id: runtime-debug-endpoint-security + name: "Runtime Debug Endpoint Security" + tags: [appsec, debug, diagnostics, production-security] + role: [security-engineer, appsec-engineer, backend-developer] + phase: [build, review, operate] + activity: [review, assess] + frameworks: [OWASP-Top10-2021] + difficulty: intermediate + time_estimate: "30-60min" + file: skills/appsec/runtime-debug-endpoint-security/SKILL.md + compatible_tools: [claude-code, gemini-cli, cursor, codex-cli, openclaw, kiro] diff --git a/skills/appsec/runtime-debug-endpoint-security/SKILL.md b/skills/appsec/runtime-debug-endpoint-security/SKILL.md new file mode 100644 index 00000000..6dbf470c --- /dev/null +++ b/skills/appsec/runtime-debug-endpoint-security/SKILL.md @@ -0,0 +1,266 @@ +# Runtime Debug Endpoint Security + +## Overview + +This skill performs a structured security review of runtime debug, diagnostic, and internal monitoring endpoints across web backends, cloud infrastructure, and internal tooling. It covers Flask debug mode, Django debug panel, Spring Boot Actuator, Kubernetes API servers, Prometheus metrics endpoints, Swagger/OpenAPI UIs, and custom diagnostic endpoints that may expose sensitive internal state, data, or operational capabilities. + +Runtime debug endpoints are a frequent source of production information disclosure, privilege escalation, and supply chain compromise. They often bypass production security controls including authentication, rate limiting, and network segmentation. + +## When to Use + +If a target is provided via arguments, focus the review on: $ARGUMENTS + +Invoke this skill when: + +- **Debug panels enabled in production** — Flask `debug=True`, Django `DEBUG=True`, or equivalent in deployed environments +- **Internal endpoints exposed externally** — Prometheus, Grafana, Swagger UI, Kibana, Kibana, or monitoring dashboards accessible without authentication +- **Spring Boot Actuator / management endpoints** — `/actuator/*`, `/manage/*` endpoints with sensitive probes +- **Kubernetes API / dashboard** — unauthenticated or weakly-authenticated K8s API servers, dashboards, or proxy endpoints +- **Incident response tooling** — debug endpoints used for incident handling that may persist beyond the incident window +- **CI/CD and deployment pipelines** — Jenkins, GitLab CI, GitHub Actions runner interfaces exposed in production networks + +## Context + +Runtime diagnostic endpoints are designed for development and troubleshooting but frequently persist into production deployments. They often provide privileged access to internal state, data, configuration, and operational controls. + +### Common Attack Surfaces + +| Endpoint Type | Typical Path | Risk Level | +|--------------|--------------|------------| +| Flask Debug Console | `/debug/`, `/flask-debug/` | CRITICAL | +| Django Debug Panel | `/django-debug-toolbar/` | HIGH | +| Spring Actuator | `/actuator/env`, `/actuator/heapdump` | CRITICAL | +| Swagger UI | `/swagger-ui.html`, `/api-docs` | MEDIUM | +| Prometheus | `/metrics`, `/-/healthy` | MEDIUM | +| K8s API Server | `/api/v1/`, `/logs/` | CRITICAL | +| Grafana | `/grafana/`, `/grafana/api/` | HIGH | +| JMX/JConsole | `:8080/`, `:5000/` | CRITICAL | +| Node.js inspector | `:9229/` | HIGH | +| Python werkzeug debugger | `/console/` | CRITICAL | + +### Prerequisites + +- Access to application source code or deployment manifests +- Network access to target endpoints for validation +- Dockerfile, Kubernetes manifests, or Helm charts +- Application configuration files (`.env`, `application.yml`, `settings.py`) + +## Process + +### Step 1: Discovery — Identify Debug/Diagnostic Endpoints + +Use Glob to locate all debug and diagnostic endpoint configurations. + +**Patterns to search:** + +``` +# Framework-specific debug configurations +**/app.py +**/wsgi.py +**/main.py +**/settings.py +**/application.yml +**/application.properties +**/Dockerfile +**/docker-compose.yml +**/deployment.yaml +**/values.yaml +``` + +**Key indicators:** + +- `app.run(debug=True)` — Flask debug enabled +- `DEBUG = True` — Django debug mode +- `spring.security.enabled=false` — Spring security disabled +- `/actuator/*` — Spring Boot actuator endpoints +- `prometheus.io/scrape: "true"` — Prometheus scrape annotations +- `swagger-ui.html` or `SwaggerConfig` — Swagger/OpenAPI UI enabled +- `flask-debugtoolbar` or `django-debug-toolbar` — Debug toolbars +- `kubernetes.io/ingress` with debug paths +- Port mappings exposing debugger ports (9229, 5005, 8787) + +### Step 2: Authority Assessment — Who Can Access Debug Endpoints + +**MUST** verify that each debug endpoint has explicit authentication and authorization controls. + +**MUST NOT** allow unauthenticated access to any debug endpoint in production environments. + +**Critical checks:** + +1. **Authentication:** Is there a gateway, proxy, or middleware enforcing authentication? + - Check ingress controllers, reverse proxies, API gateways + - Look for `auth-service` or `oauth2-proxy` annotations + - Verify token validation middleware is present + +2. **Authorization:** Does access control follow least privilege? + - Should debug access be restricted to specific roles/IPs? + - Are service accounts properly scoped? + - Is RBAC configured for diagnostic tools? + +3. **Network Segmentation:** Are debug endpoints isolated from public access? + - Check network policies, security groups, firewall rules + - Verify internal-only CIDR restrictions + - Look for VPN/mesh requirements + +### Step 3: Data Exposure Analysis — What Information Is Revealed? + +**MUST** catalog all sensitive data exposed by each debug endpoint. + +**Information disclosure risks:** + +| Data Type | Example | Severity | +|-----------|---------|----------| +| Environment Variables | `os.environ`, `/actuator/env` | CRITICAL | +| Source Code | Stack traces, `code.interact()` | HIGH | +| Credentials | DB passwords, API keys, tokens | CRITICAL | +| Internal State | Bean definitions, service registry | HIGH | +| Database Access | JPA console, SQL queries | CRITICAL | +| Filesystem | `/proc`, `file:///` access | CRITICAL | +| Remote Execution | `exec`, `/invoke`, shell access | CRITICAL | + +### Step 4: Capability Assessment — What Operations Are Available? + +**MUST** document all operational capabilities exposed through debug endpoints. + +**High-risk operations:** + +- `heapdump` — Memory dump with potential secrets +- `threaddump` — Thread state with potential data in variables +- `logfile` — Log files with potential PII/secrets +- `restart` / `shutdown` — Service disruption +- `refresh` / `reload` — Configuration reload with new secrets +- `trace` — Request/response details with potential sensitive data +- `mappings` — Application routing with internal paths +- `auditevents` — Audit logs with potential security metadata + +### Step 5: Validation — Confirm Remediation + +**Before (vulnerable):** +```python +# Flask app with debug enabled in production +app = Flask(__name__) +app.run(debug=True, host='0.0.0.0', port=5000) # DEBUG + EXPOSED +``` + +**After (remediated):** +```python +# Production config: debug disabled, bind to localhost only +app = Flask(__name__) +if app.config.get('DEBUG_MODE', False): + app.run(debug=True, host='127.0.0.1', port=5000) # Local only +else: + # Use production WSGI server + from gunicorn.app.base import Application + # ... configure gunicorn for production +``` + +**Before (vulnerable):** +```yaml +# Kubernetes: Actuator exposed without auth +apiVersion: v1 +kind: Service +spec: + selector: + app: my-service + ports: + - port: 8080 + targetPort: 8080 # Exposes /actuator/env, /actuator/heapdump +``` + +**After (remediated):** +```yaml +# Kubernetes: Actuator internal only with network policy +apiVersion: v1 +kind: Service +metadata: + annotations: + internal-only: "true" +spec: + selector: + app: my-service + ports: + - port: 8080 + targetPort: 8080 +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +spec: + podSelector: + matchLabels: + app: my-service + policyTypes: + - Ingress + ingress: + - from: + - namespaceSelector: + matchLabels: + internal: "true" + ports: + - port: 8080 +``` + +### Step 6: Incident Response — Handle Debug Endpoints in IR + +**MUST** document procedures for managing debug endpoints during incidents. + +**Best practices:** + +- Use feature flags or environment-specific configs to enable/disable debug +- Implement debug endpoint access logging for audit trails +- Define incident response procedures for exposed debug endpoints +- Conduct regular debug endpoint audits as part of production reviews +- Use network-level controls (iptables, security groups) as defense-in-depth + +## Verification + +| | | +|---|---| +| **Input** | Application with `/actuator/env` exposed publicly | +| **Expected output** | CRITICAL finding with specific remediation guidance | +| **Pass condition** | Finding includes specific endpoint, exposed data type, and remediation | +| **Fail condition** | No finding or generic advice without actionable steps | + +Step-by-step confirmation the fix held: +1. Scan for debug endpoint configurations using Glob + Grep. +2. Confirm authentication/authorization controls on each endpoint. +3. Confirm no sensitive data (credentials, source code, internal state) is exposed. +4. Confirm network segmentation prevents public access to internal endpoints. +5. Verify incident response procedures cover debug endpoint exposure. + +## Gotchas (self-improvement loop) + +**False positives** +- **Pattern:** `debug: true` in `tests/` or `conftest.py` — **Why:** Test environments — **Suppress:** only flag if debug config leaks to production deployment manifests or `settings.py` used in prod +- **Pattern:** `/metrics` endpoint in Prometheus ServiceMonitor — **Why:** Internal monitoring — **Suppress:** flag only if metrics are externally accessible (check ingress/network policy) +- **Pattern:** `swagger-ui.html` behind authenticated gateway — **Why:** Intentional API documentation — **Suppress:** verify authentication is enforced before reaching the endpoint + +**Precision traps** +- **Trap:** Disabling debug breaks deployment — **Mitigation:** Use environment-specific configs or feature flags, never disable entirely without alternatives +- **Trap:** Prometheus metrics required for monitoring — **Mitigation:** Keep metrics but restrict to internal network, add authentication, remove sensitive labels + +**Do NOT flag:** Public health check endpoints (`/health`, `/ready`, `/live`) that only return status codes without sensitive data. + +## References (progressive disclosure) + +- [OWASP Top 10 2021 — A05: Security Misconfiguration](https://owasp.org/Top10/A05_2021_Security_Misconfiguration/) +- [OWASP Top 10 2021 — A01: Broken Access Control](https://owasp.org/Top10/A01_2021_Broken_Access_Control/) +- [Spring Boot Actuator Security](https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html#actuator.endpoints.security) +- [Flask Debug Mode Security](https://flask.palletsprojects.com/en/2.3.x/debug/) +- [Django Debug Mode](https://docs.djangoproject.com/en/5.0/ref/settings/#debug) +- [Kubernetes Security Best Practices](https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/) + +## Submission checklist + +- [ ] Directory is `skills/appsec/runtime-debug-endpoint-security/`; entrypoint is `SKILL.md` +- [ ] Frontmatter complete; `name` matches the directory +- [ ] Every framework ID is real and resolves +- [ ] At least one machine-matchable detection signal (regex patterns in §2) +- [ ] Rules are hard constraints (no "consider"/"may") +- [ ] Before/after remediation example present +- [ ] Falsifiable verification test defined (binary pass/fail) +- [ ] Gotchas: 3 false positives + 2 precision traps +- [ ] `SKILL.md` stays lean +- [ ] `injection-hardened: true` — reviewed against OWASP LLM01:2025 +- [ ] Commit message: `feat(skill): runtime-debug-endpoint-security — reviews debug/diagnostic endpoints for production exposure` + +*SecuritySkills Skill Template v2 — UnitOne.ai* diff --git a/skills/appsec/runtime-debug-endpoint-security/tests/benign/production-safe.json b/skills/appsec/runtime-debug-endpoint-security/tests/benign/production-safe.json new file mode 100644 index 00000000..f3ce443d --- /dev/null +++ b/skills/appsec/runtime-debug-endpoint-security/tests/benign/production-safe.json @@ -0,0 +1,7 @@ +{ + "file": "app/production_config.py", + "description": "Production config with debug disabled and proper network isolation", + "benign_code": "app = Flask(__name__)\napp.config['DEBUG'] = False\n# Use production WSGI server\n# gunicorn --bind 127.0.0.1:8000 app:app", + "finding": "None - debug disabled, bound to localhost, using production WSGI server", + "severity": "SAFE" +} diff --git a/skills/appsec/runtime-debug-endpoint-security/tests/vulnerable/flask-debug-exposed.json b/skills/appsec/runtime-debug-endpoint-security/tests/vulnerable/flask-debug-exposed.json new file mode 100644 index 00000000..e0ac4397 --- /dev/null +++ b/skills/appsec/runtime-debug-endpoint-security/tests/vulnerable/flask-debug-exposed.json @@ -0,0 +1,9 @@ +{ + "file": "app/debug_enabled.py", + "description": "Flask debug mode enabled in production deployment", + "vulnerable_code": "app = Flask(__name__)\napp.config['DEBUG'] = True\napp.run(host='0.0.0.0', port=5000)", + "finding": "Flask debug mode enabled with host='0.0.0.0' — exposes Werkzeug debugger console publicly", + "severity": "CRITICAL", + "owasp_ref": "OWASP-Top10-2021-A05", + "remediation": "Set DEBUG=False in production, bind to 127.0.0.1, use gunicorn/uwsgi" +} diff --git a/skills/secops/cache-key-review/SKILL.md b/skills/secops/cache-key-review/SKILL.md new file mode 100644 index 00000000..26c78b9c --- /dev/null +++ b/skills/secops/cache-key-review/SKILL.md @@ -0,0 +1,165 @@ +--- +name: tenant-aware-cache-key-review +description: > + Reviews multi-tenant applications for cache-key authority completeness, + authorization-before-cache-hit, access-change invalidation, and edge/CDN + cache controls. Auto-invoked when reviewing multi-tenant APIs, cache-backed + services, shared edge or CDN caches, data-loaders, or background warmers. + Produces a structured report with tenant-leakage findings and remediation + mapped to OWASP API Security and NIST SP 800-145. +tags: [secops, cache, multi-tenant, code-review] +role: [security-engineer, appsec-engineer, backend-developer] +phase: [build, review, operate] +frameworks: [OWASP-API-Security-2023, NIST-SP-800-145, RFC-9110] +difficulty: intermediate +time_estimate: "30-60min" +version: "1.0.0" +author: daviediao-code +license: MIT +allowed-tools: Read, Grep, Glob +injection-hardened: true +argument-hint: "[target-file-or-directory]" +--- + +# Tenant-Aware Cache Key Review + +## 1. When to Use + +If a target is provided via arguments, focus the review on: $ARGUMENTS + +Invoke this skill when: + +- **Multi-tenant application** — reviewing code that serves multiple tenants/users from a shared cache layer +- **Cache-backed API** — reviewing API endpoints that use Redis, Memcached, or in-memory caches +- **CDN/edge caching** — reviewing CloudFlare, Vercel, or CDN configurations for shared caches +- **Data loader** — reviewing DataLoader, query-cache, or memoization layers +- **Background warmer** — reviewing cache pre-warming or prefetch logic + +## 2. Patterns (Detection Library) + +Search for cache-key construction in the codebase: + +``` +# Cache key patterns +cache.get(.*tenant_id) +redis.hget(.*tenant) +Memcached.get(.*tenant) +cache_key = .*tenant_id +cache_key = .*user_id +@cache(.*tenant) +dataLoader.load(.*tenant) +``` + +### Vulnerable Patterns + +| Pattern | Description | Severity | +|---------|-------------|----------| +| `cache.get(key)` without tenant scope | Shared cache key across tenants | HIGH | +| `cache.get(f"...{user_id}...")` but auth checks after cache hit | Authorization bypass via cached data | CRITICAL | +| `cache.set(key, data, ttl=3600)` with static TTL, no access-change invalidation | Stale data after permission revocation | HIGH | +| `Cache-Control: public` on tenant-specific responses | CDN serves tenant data to other tenants | CRITICAL | +| `cache.set(user_token_key, admin_data)` | Token-scoped key contains data outside scope | HIGH | + +### Secure Patterns + +| Pattern | Description | +|---------|-------------| +| `cache.get(f"tenant:{tenant_id}:user:{user_id}")` | Tenant-scoped key with explicit namespace | +| `Cache-Control: private, no-store` on authenticated responses | No shared caching of sensitive data | +| `cache.invalidate("tenant:{tenant_id}:*")` on access change | Proper invalidation on permission changes | +| `cache.get(f"tenant:{tenant_id}:user:{user_id}:etag:{version}")` | Versioned cache keys | + +## 3. Rules (Constraints) + +Hard rules only — falsifiable and enforceable. No "consider" / "may" language. + +- **MUST** verify that every cache key includes tenant or user context. +- **MUST** check that authorization occurs before or alongside cache hit validation. +- **MUST** validate that cache invalidation covers all keys affected by access changes. +- **MUST NOT** emit findings for in-memory caches that are process-local and single-tenant. +- **MUST** map every finding to a real control ID from OWASP API Security 2023 or NIST SP 800-145. + +## 4. Remediation + +**Before (vulnerable):** +```python +# Vulnerable: no tenant in cache key +def get_user_data(user_id): + cache_key = f"user:{user_id}" + cached = cache.get(cache_key) + if cached: + return cached # Could return another tenant's data! + + data = db.query("SELECT * FROM users WHERE id = ?", user_id) + cache.set(cache_key, data, ttl=3600) + return data +``` + +**After (remediated):** +```python +# Remediated: tenant-scoped cache key + proper invalidation +def get_user_data(tenant_id, user_id): + cache_key = f"tenant:{tenant_id}:user:{user_id}" + cached = cache.get(cache_key) + if cached: + return cached + + data = db.query("SELECT * FROM users WHERE tenant_id = ? AND id = ?", tenant_id, user_id) + cache.set(cache_key, data, ttl=3600) + return data + +# On permission change: +def revoke_access(tenant_id, user_id): + cache.delete(f"tenant:{tenant_id}:user:{user_id}") + cache.delete_pattern(f"tenant:{tenant_id}:user:{user_id}:*") +``` + +## 5. Verification (falsifiable) + +| | | +|---|---| +| **Input** | Code with `cache.get(user_id)` without tenant scope | +| **Expected output** | HIGH finding with specific remediation | +| **Pass condition** | Finding includes OWASP-A07:2023 (Identification and Authentication Failures) | +| **Fail condition** | No finding emitted or finding references non-existent control ID | + +Step-by-step confirmation the fix held: +1. Scan codebase for cache key patterns using Grep. +2. Confirm all cache keys include tenant context. +3. Confirm cache invalidation logic covers all affected keys. +4. Confirm `Cache-Control` headers prevent shared caching of tenant data. + +## 6. Gotchas (self-improvement loop) + +**False positives** +- **Pattern:** `cache.get(f"admin:{admin_id}")` — **Why:** admin sessions are single-tenant per process — **Suppress:** only flag if cache is shared across admin sessions +- **Pattern:** `cache.get("static:config")` — **Why:** static config is not tenant-specific — **Suppress:** skip keys containing "static", "config", "metadata" without tenant prefix + +**Precision traps** +- **Trap:** Adding tenant scope breaks existing cache warmers — **Mitigation:** update warmers to pass tenant_id when seeding cache + +**Do NOT flag:** localhost-only cache instances (e.g., `127.0.0.1:6379`) in development configurations with no tenant routing. + +## 7. References (progressive disclosure) + +- [OWASP API Security Top 10 2023 — API4: Broken Object Level Authorization](https://owasp.org/API-Security/editions/2023/en/0xa4-broken-object-level-authorization/) +- [NIST SP 800-145 — The NIST Definition of Cloud Computing](https://csrc.nist.gov/publications/detail/sp/800-145/final) +- [RFC 9110 — HTTP Semantics: Cache-Control headers](https://httpwg.org/specs/rfc9110.html#cache.control) + +--- + +## Submission checklist + +- [x] Directory is `skills/secops/cache-key-review/`; entrypoint is `SKILL.md` +- [x] Frontmatter complete; `name` matches the directory +- [x] Every framework ID is real and resolves +- [x] At least one machine-matchable detection signal (regex patterns in §2) +- [x] Rules are hard constraints (no "consider"/"may") +- [x] Before/after remediation example present +- [x] Falsifiable verification test defined (binary pass/fail) +- [x] Gotchas: 2 false positives + 1 precision trap +- [x] `SKILL.md` stays lean +- [x] `injection-hardened: true` — reviewed against OWASP LLM01:2025 (no prompt injection vectors in skill body) +- [x] Commit message: `feat(skill): tenant-aware-cache-key-review — reviews cache keys for tenant leakage` + +*SecuritySkills Skill Template v2 — UnitOne.ai* diff --git a/skills/secops/cache-key-review/tests/vulnerable/tenant-scoped-key-missing.json b/skills/secops/cache-key-review/tests/vulnerable/tenant-scoped-key-missing.json new file mode 100644 index 00000000..4de6ddb2 --- /dev/null +++ b/skills/secops/cache-key-review/tests/vulnerable/tenant-scoped-key-missing.json @@ -0,0 +1,8 @@ +{ + "file": "app/cache.py", + "description": "Cache key without tenant scope", + "vulnerable_code": "def get_user_data(user_id):\n cache_key = f\"user:{user_id}\"\n cached = cache.get(cache_key)\n if cached:\n return cached # Vulnerable: no tenant scope\n data = db.query(\"SELECT * FROM users WHERE id = ?\", user_id)\n cache.set(cache_key, data, ttl=3600)\n return data", + "finding": "Cache key does not include tenant_id \u2014 can serve cross-tenant data", + "owasp_ref": "OWASP-API-Security-2023-A07", + "severity": "HIGH" +} \ No newline at end of file