- openClawRoutes was mounted at root causing /status vs /openclaw/status mismatch
- ctx.docker is a typed wrapper {client,pull,...} — all calls now use docker.client.*
- templates/deploy/removal/restore sub-routers had /apps/ hardcoded in inner routes
causing double-stacking when mounted under /apps (→ /apps/apps/templates etc)
- openclaw.js: GET /status, POST /deploy, GET/POST /proxy/*, DELETE /
1. WebSocket exec auth bypass (exec.js): Require valid JWT or API key
before accepting WebSocket upgrade. Reject unauthenticated requests
with 401 before the upgrade completes.
2. Shell injection in router auto-login (session-handlers.js): Validate
baseUrl against safe hostname pattern before embedding in wget shell
command. Reject with null session if invalid.
3. Path traversal in credentials routes (services.js): Add explicit
serviceId validation (alphanumeric + dash/underscore/dot, max 100
chars) to all three credential endpoints. Removed redundant
try/catch wrapper.
4. execSync injection in CA CSR generation (ca.js): Add sanitize step
replacing any non-alphanumeric domain chars with underscore before
interpolation into shell subj argument. Redundant with existing
validation but provides defense-in-depth.
5. Auth bypass when TOTP disabled (middleware.js): Split the logic
cleanly — disabled TOTP means no auth (initial setup state), enabled
TOTP means all auth methods checked (session/JWT/API key). Removed
the sessionDuration:never conflating shortcut.