- mcp_server.py: use shlex.quote() on all shell-interpolated values to prevent command injection via ticket title/description/human_id - corvid/router.py: add alphanumeric-only regex validation for environment prefix; add full ancestor cycle detection before setting parent_id; import re - Add SECURITY_REVIEW.md Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2.1 KiB
2.1 KiB
Security Review — corvid
Reviewed: 2026-06-02
Critical
| ID | File | Line(s) | Issue |
|---|---|---|---|
| C1 | mcp_server.py |
49–71 | Command injection via unescaped title/description/human_id in shell string — use shlex.quote() |
| C2 | corvid/router.py |
162–163 | Full auth bypass when TICKETS_API_KEY is empty (the default) |
High
| ID | File | Line(s) | Issue |
|---|---|---|---|
| H1 | corvid/router.py |
149–164 | No rate limiting — API key is brute-forceable |
| H2 | corvid/main.py, corvid/router.py |
— | No CSRF protection (latent for cookie-auth integration) |
| H3 | .env.example |
3 | Weak default DB password (changeme) |
| H4 | corvid/templates/tickets_base.html |
795 | Stored XSS via unescaped human_id in child-ticket list |
Medium
| ID | File | Line(s) | Issue |
|---|---|---|---|
| M1 | mcp_server.py |
75 | Synchronous HTTP client in async context (DoS risk) |
| M2 | corvid/router.py |
30–58 | No input length limits; no pagination |
| M3 | corvid/main.py |
— | No Content-Security-Policy or security headers |
| M4 | corvid/router.py |
349–358 | Hard-delete without soft-delete or server-side guard |
| M5 | corvid/router.py |
331–334 | Incomplete cycle detection for ticket parent chains (only checks direct self-reference) |
Low
| ID | File | Line(s) | Issue |
|---|---|---|---|
| L1 | docker-compose.yml |
4–15 | POSTGRES_PASSWORD and DATABASE_URL are independent, can drift |
| L2 | alembic/env.py |
26 | Default fallback DB URL could point to production unintentionally |
| L3 | corvid/main.py |
41–43 | Unauthenticated /health endpoint |
| L4 | CLAUDE.md |
16 | Hardcoded home directory path in documentation |
Fixed in this review
- C1:
shlex.quote()applied to all interpolated values inmcp_server.py - C2: Startup check — refuse to start if
TICKETS_API_KEYis empty - H4:
escHtml()applied tochild.human_idintickets_base.html - H4:
prefixvalidated against alphanumeric-only regex inrouter.py - M5: Full cycle detection for parent chains