Files
guarddog-nexus/.opencode/plans/security-audit-guarddog-nexus.md
2026-05-11 20:04:16 +03:00

8.4 KiB
Raw Blame History

Security Audit Report — GuardDog Nexus

Date: 2026-05-10
Auditor: Automated security audit
Last updated: 2026-05-11
Scope: Full codebase review — security vulnerabilities, logic errors, missing controls


Summary

Severity Count Fixed Rejected Remaining
CRITICAL 5 2 2 1
HIGH 7 2 3 2
MEDIUM 8 3 0 5
LOW 6 2 0 4
Total 26 9 5 12

CRITICAL (5)

C1. SSRF via webhook downloadUrl FIXED

Severity: CRITICAL
Fix: NEXUS_ALLOWED_HOSTS env var + _validate_download_url() in core/nexus.py.

Problem: downloadUrl из webhook-пэйлода передаётся напрямую в httpx.AsyncClient.get() без валидации.

download_url = asset.get("downloadUrl") or _build_download_url(repository, asset_path)
# ...
response = await client.get(download_url)  # no validation

Real-world impact: Атакующий отправляет webhook с downloadUrl: "http://169.254.169.254/latest/meta-data/iam/security-credentials/" → сервер скачивает IAM-учётные данные облака.

Fix: Validate URL scheme (http/https only), block private IP ranges (10.x, 172.16.x, 192.168.x, 127.x, 169.254.x, ::1), optionally whitelist domain against config.nexus_url.


C2. Webhook secret not enforced by default ACCEPTED RISK

Severity: CRITICAL
Decision: Внутренний сервис, секрет опционален.


C3. Default admin credentials FIXED

Severity: CRITICAL
Fix: Убран BasicAuth из всех запросов к Nexus (анонимный доступ).


C4. XSS via LLM report verdict NOT DANGEROUS

Severity: CRITICAL — downgraded to INFO
Decision: Jinja2 autoescape блокирует инъекцию в атрибутах.


C5. LLM Prompt Injection ⚠️ PARTIALLY MITIGATED

Severity: CRITICAL
Mitigation: System prompt gives priority to system instructions. Raw finding data still in user message.


HIGH (7)

H1. No rate limiting REJECTED

H2. Path traversal ⚠️ LOW RISK

Severity: HIGH — downgraded
Analysis: os.path.basename("../../../etc/passwd")"passwd", traversal невозможен.


H3. Sensitive data in API REJECTED (source_ip is a feature)

H4. No authentication REJECTED (internal service)

H5. Memory leak in locks FIXED (bg cleanup every 30min)

H6. Race condition in URL locking FIXED (DB re-check inside lock)

H7. CSV export bounded REJECTED (acceptable for internal tool)


MEDIUM (8)

M1. No LLM response schema validation

Severity: MEDIUM
File: core/llm.py:80-82

Problem: LLM response parsed as JSON but not validated against schema. Missing report.verdict → Jinja2 renders empty string → CSS broken.

Fix: Pydantic model для валидации LLM response.


M2. No CSRF protection

Severity: MEDIUM
File: routes/web.py:205-274

Problem: POST /api/v1/findings/{id}/analyze без CSRF token.

Fix: Добавить CSRF token для всех POST endpoints.


M3. No security headers

Severity: MEDIUM
File: main.py

Problem: Отсутствие CSP, X-Content-Type-Options, X-Frame-Options, X-XSS-Protection.

Fix: Middleware для security headers.


M4. SQLite without WAL mode

Severity: MEDIUM
File: db/engine.py:12

Problem: Concurrent readers block writers → poor performance under load.

Fix: PRAGMA journal_mode=WAL in connection setup.


M5. Scoped npm packages not supported

Severity: MEDIUM
File: core/nexus.py:54-70

Problem: extract_npm_info returns None для @scope/package → пропускаются сканирования.

Fix: Обновить extractor для scoped packages.


M6. Dashboard stats — potential IndexError

Severity: MEDIUM
File: routes/api_scans.py:145-147

Problem: dashboard["latest_flagged"][0] — IndexError если latest_flagged пустой.

"latest_scan_at": dashboard["latest_flagged"][0].started_at.isoformat()

Fix: Guard с if dashboard.get("latest_flagged").


M7. Error message HTML escaping

Severity: MEDIUM
File: web/templates/scan_detail.html:30

Problem: scan.error_message rendered в template — если содержит HTML/JS, может сломать UI.

Fix: Jinja2 autoescape handles this, но стоит добавить explicit escaping для code fields.


M8. Unknown ecosystem defaults to pypi FIXED

Severity: MEDIUM
Fix: _detect_ecosystem() возвращает None → webhook reject с "unknown_ecosystem".
Duplicate: L6.


LOW (6)

L1. Dockerfile grep hack FIXED (uv pip install . --system)

L2. Health check without DB FIXED (/health/dependencies)


L3. No backup strategy for SQLite

Severity: LOW
Risk: Crash → corrupted database → data loss.

Fix: Регулярные backups через cron или switch to PostgreSQL for production.


L4. Dead code — parse_package_path unused in harvester

Severity: LOW
File: core/nexus.py:93-99

Problem: Функция определена но не используется в harvester pipeline.

Fix: Убрать или интегрировать.


L5. Hardcoded LLM API base URL

Severity: LOW
File: constants.py:139

Problem: Default https://api.openai.com/v1 — unexpected API calls для пользователей локальных моделей.

Fix: Better default или warning at startup.


L6. Unknown ecosystem defaults to pypi (webhook)

Severity: LOW
File: routes/webhooks.py:62

Problem: Неизвестный format → fallback к pypi. Maven/NuGet webhooks будут сканироваться как PyPI пакеты.

Fix: Явно reject неизвестные ecosystems.


Implementation Plan

Phase 1 — P0 (Critical)

# Task Status
1 SSRF protection FIXED
2 Mandatory WEBHOOK_SECRET ACCEPTED
3 Remove default Nexus credentials FIXED
4 LLM verdict whitelist + prompt injection ⚠️ PARTIAL
5 Path traversal fix ⚠️ LOW RISK

Phase 2 — P1 (High)

# Task Status
6 Rate limiting REJECTED
7 API authentication REJECTED
8 Memory leak fix for locks FIXED
9 Race condition fix FIXED
10 Remove source_ip from public API REJECTED
11 CSV export auth + limit REJECTED

Phase 3 — P2 (Medium)

# Task Status
12 LLM response validation (Pydantic)
13 CSRF protection
14 Security headers middleware
15 SQLite WAL mode
16 Scoped npm support
17 Dashboard None guard
18 serialize_finding вместо **f.data FIXED
19 _scan_component try/except FIXED
20 Reject unknown ecosystem FIXED

Phase 4 — P3 (Low)

# Task Status
21 Dockerfile deps FIXED
22 Health check DB ping FIXED
23 Backup strategy docs
24 Reject unknown ecosystems FIXED (duplicate)

Test Coverage Gaps

The existing 85 tests do NOT cover:

  • SSRF prevention (malicious downloadUrl)
  • Webhook signature validation with empty secret
  • Path traversal in download URLs
  • Rate limiting on webhook endpoint
  • Authentication on API endpoints
  • LLM prompt injection
  • LLM response schema validation
  • CSRF protection
  • Security headers presence
  • Memory leak in lock dictionaries
  • Race condition in URL locking
  • Scoped npm package extraction
  • Dashboard IndexError on empty data

Recommendations

  1. Immediate: Implement C1-C5 before any production deployment
  2. Short-term: Implement H1-H7 within first sprint
  3. Medium-term: Implement M1-M8 within first month
  4. Long-term: Implement L1-L6 during routine maintenance
  5. Ongoing: Add security-focused tests for all findings above