Root cause: Telegram appends @BotUsername to commands in group chats:
/menu@OpenClawAwoool_Bot
The parser did:
q = question.lstrip('/') → 'menu@OpenClawAwoool_Bot'
cmd = q.split()[0].lower() → 'menu@openclawawoool_bot'
This did NOT match 'menu' in KNOWN set, so the command fell through
to openclaw_answer() (natural language mode) → no menu appeared.
Fix: cmd = raw_cmd.split('@')[0]
→ strips @mention suffix before KNOWN lookup
→ /menu@OpenClawAwoool_Bot now correctly dispatches to handle_cmd('menu')
Affects all slash commands in group chat mode.
Issues fixed:
1. [CRITICAL] /api/alert/fix unauthenticated (CWE-306)
POST /api/alert/fix had no @check_alert_auth and was CSRF-exempt.
Any unauthenticated caller could trigger docker restart or
docker exec on arbitrary container names (container_name is validated
by is_valid_container_name but restart of any valid name is still
a DoS vector). Fix: @check_alert_auth added.
2. [HIGH] Hardcoded ALERT_WEBHOOK_PASSWORD fallback (CWE-798)
Default 'wooo_alert_2026' exposed in source. Fix: default='',
startup warning if unset. check_alert_auth now fail-secure:
returns 503 if password not configured.
3. [MEDIUM] /api/alert/history and /api/alert/analyze unauthenticated
Both endpoints expose container names, memory usage, CPU stats,
system recommendations. Fix: @check_alert_auth added to both.
4. [MEDIUM] issue_type unvalidated in manual_fix (CWE-20)
Any string value could be passed through to auto_fix_container.
Fix: ALLOWED_ISSUE_TYPES frozenset — only memory/cpu variants allowed.
5. [LOW] limit parameter unbounded in get_alert_history
Arbitrarily large limit → large list slice → memory pressure.
Fix: clamped to [1, 200].
NOTE: L177 docker stats command (original report) is SAFE as-is —
list argv, fixed arguments, no user input. nosec B603 correctly placed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Issues fixed:
1. [CRITICAL] No authentication on destructive routes (CWE-306)
POST /api/system/cleanup/docker was unauthenticated (system_bp is
CSRF-exempt, before_request only refreshes session, no login check).
Any unauthenticated HTTP client could trigger docker system prune.
Fix: _require_internal_key() checks X-Internal-Key header against
INTERNAL_API_KEY env var on all 4 routes; fail-secure if key unset.
2. [MEDIUM] Unvalidated numeric inputs in find commands (CWE-20)
max_size_mb / older_than_hours came from POST body and were
interpolated into find -size / -mmin args. Negative/huge values
could cause unexpected behavior.
Fix: _validate_int() clamps to [1..10000] / [1..8760] with defaults.
3. [LOW] find -mmin arg missing leading '+' (logic bug)
'-mmin 168' matches FILES EXACTLY 168 min old, not older-than.
Fix: '-mmin', f'+{older_than_hours * 60}' (+ = older than)
4. [LOW] subprocess(['date', ...]) in health_check replaced
with Python datetime.now(UTC).isoformat() — no subprocess needed.
INTERNAL_API_KEY added to .env.example with generation instructions.
Generate with: openssl rand -hex 32
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
CVE-class issues fixed:
1. [HIGH] Shell Injection in gitlab_api_via_ssh (CWE-78)
endpoint and json_data were interpolated into f-string cmd and passed
as a single SSH remote command string → shell parses it → injection.
Fix: build remote_argv as list; each curl argument is a separate item,
SSH receives them as independent argv (no shell parsing of user data).
2. [HIGH] Hardcoded credentials in source code (CWE-798)
GITLAB_TOKEN, TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID all had live
secrets as default fallback values. Tokens are now '' (empty) with a
startup warning if env vars are missing.
3. [MEDIUM] Missing pre-validation allowlist on fix_action (CWE-20)
ALLOWED_FIX_ACTIONS frozenset added before route handler; any unknown
action is rejected with 400 before reaching execution logic.
Note: fix_registry/fix_pods/execute_*_rollback use static SSH commands
(no user input in cmd strings) so they are not injection risks.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>