fix(security): harden clawlog command execution

This commit is contained in:
Peter Steinberger
2026-03-01 23:32:53 +00:00
parent ccb415b69a
commit 6c5633598e
2 changed files with 28 additions and 15 deletions

View File

@@ -93,6 +93,7 @@ Docs: https://docs.openclaw.ai
- ACP/Harness thread spawn routing: force ACP harness thread creation through `sessions_spawn` (`runtime: "acp"`, `thread: true`) and explicitly forbid `message action=thread-create` for ACP harness requests, avoiding misrouted `Unknown channel` errors. (#30957) Thanks @dutifulbob. - ACP/Harness thread spawn routing: force ACP harness thread creation through `sessions_spawn` (`runtime: "acp"`, `thread: true`) and explicitly forbid `message action=thread-create` for ACP harness requests, avoiding misrouted `Unknown channel` errors. (#30957) Thanks @dutifulbob.
- Docs/ACP permissions: document the correct `permissionMode` default (`approve-reads`) and clarify non-interactive permission failure behavior/troubleshooting guidance. (#31044) Thanks @barronlroth. - Docs/ACP permissions: document the correct `permissionMode` default (`approve-reads`) and clarify non-interactive permission failure behavior/troubleshooting guidance. (#31044) Thanks @barronlroth.
- Security/Logging utility hardening: remove `eval`-based command execution from `scripts/clawlog.sh`, switch to argv-safe command construction, and escape predicate literals for user-supplied search/category filters to block local command/predicate injection paths.
- Security/Inbound metadata stripping: tighten sentinel matching and JSON-fence validation for inbound metadata stripping so user-authored lookalike lines no longer trigger unintended metadata removal. - Security/Inbound metadata stripping: tighten sentinel matching and JSON-fence validation for inbound metadata stripping so user-authored lookalike lines no longer trigger unintended metadata removal.
- Channels/Command parsing parity: align command-body parsing fields with channel command-gating text for Slack, Signal, Microsoft Teams, Mattermost, and BlueBubbles to avoid mention-strip mismatches and inconsistent command detection. - Channels/Command parsing parity: align command-body parsing fields with channel command-gating text for Slack, Signal, Microsoft Teams, Mattermost, and BlueBubbles to avoid mention-strip mismatches and inconsistent command detection.
- CLI/Startup (Raspberry Pi + small hosts): speed up startup by avoiding unnecessary plugin preload on fast routes, adding root `--version` fast-path bootstrap bypass, parallelizing status JSON/non-JSON scans where safe, and enabling Node compile cache at startup with env override compatibility (`NODE_COMPILE_CACHE`, `NODE_DISABLE_COMPILE_CACHE`). (#5871) Thanks @BookCatKid and @vincentkoc for raising startup reports, and @lupuletic for related startup work in #27973. - CLI/Startup (Raspberry Pi + small hosts): speed up startup by avoiding unnecessary plugin preload on fast routes, adding root `--version` fast-path bootstrap bypass, parallelizing status JSON/non-JSON scans where safe, and enabling Node compile cache at startup with env override compatibility (`NODE_COMPILE_CACHE`, `NODE_DISABLE_COMPILE_CACHE`). (#5871) Thanks @BookCatKid and @vincentkoc for raising startup reports, and @lupuletic for related startup work in #27973.

View File

@@ -44,6 +44,7 @@ SERVER_ONLY=false
TAIL_LINES=50 # Default number of lines to show TAIL_LINES=50 # Default number of lines to show
SHOW_TAIL=true SHOW_TAIL=true
SHOW_HELP=false SHOW_HELP=false
STYLE_JSON=false
# Function to show usage # Function to show usage
show_usage() { show_usage() {
@@ -137,6 +138,14 @@ list_categories() {
echo -e "\n${YELLOW}Note: Only categories with recent activity are shown${NC}" echo -e "\n${YELLOW}Note: Only categories with recent activity are shown${NC}"
} }
# Escape user input embedded in macOS log predicate string literals.
escape_predicate_literal() {
local value="$1"
value="${value//\\/\\\\}"
value="${value//\"/\\\"}"
printf '%s' "$value"
}
# Show help if no arguments provided # Show help if no arguments provided
if [[ $# -eq 0 ]]; then if [[ $# -eq 0 ]]; then
show_usage show_usage
@@ -193,7 +202,7 @@ while [[ $# -gt 0 ]]; do
exit 0 exit 0
;; ;;
--json) --json)
STYLE_ARGS="--style json" STYLE_JSON=true
shift shift
;; ;;
--all) --all)
@@ -213,7 +222,8 @@ PREDICATE="subsystem == \"$SUBSYSTEM\""
# Add category filter if specified # Add category filter if specified
if [[ -n "$CATEGORY" ]]; then if [[ -n "$CATEGORY" ]]; then
PREDICATE="$PREDICATE AND category == \"$CATEGORY\"" ESCAPED_CATEGORY=$(escape_predicate_literal "$CATEGORY")
PREDICATE="$PREDICATE AND category == \"$ESCAPED_CATEGORY\""
fi fi
# Add error filter if specified # Add error filter if specified
@@ -223,29 +233,31 @@ fi
# Add search filter if specified # Add search filter if specified
if [[ -n "$SEARCH_TEXT" ]]; then if [[ -n "$SEARCH_TEXT" ]]; then
PREDICATE="$PREDICATE AND eventMessage CONTAINS[c] \"$SEARCH_TEXT\"" ESCAPED_SEARCH_TEXT=$(escape_predicate_literal "$SEARCH_TEXT")
PREDICATE="$PREDICATE AND eventMessage CONTAINS[c] \"$ESCAPED_SEARCH_TEXT\""
fi fi
# Build the command - always use sudo with --info to show private data # Build the command as argv array to avoid shell eval injection
LOG_CMD=(sudo log)
if [[ "$STREAM_MODE" == true ]]; then if [[ "$STREAM_MODE" == true ]]; then
# Streaming mode # Streaming mode
CMD="sudo log stream --predicate '$PREDICATE' --level $LOG_LEVEL --info" LOG_CMD+=(stream --predicate "$PREDICATE" --level "$LOG_LEVEL" --info)
echo -e "${GREEN}Streaming VibeTunnel logs continuously...${NC}" echo -e "${GREEN}Streaming VibeTunnel logs continuously...${NC}"
echo -e "${YELLOW}Press Ctrl+C to stop${NC}\n" echo -e "${YELLOW}Press Ctrl+C to stop${NC}\n"
else else
# Show mode # Show mode
CMD="sudo log show --predicate '$PREDICATE'" LOG_CMD+=(show --predicate "$PREDICATE")
# Add log level for show command # Add log level for show command
if [[ "$LOG_LEVEL" == "debug" ]]; then if [[ "$LOG_LEVEL" == "debug" ]]; then
CMD="$CMD --debug" LOG_CMD+=(--debug)
else else
CMD="$CMD --info" LOG_CMD+=(--info)
fi fi
# Add time range # Add time range
CMD="$CMD --last $TIME_RANGE" LOG_CMD+=(--last "$TIME_RANGE")
if [[ "$SHOW_TAIL" == true ]]; then if [[ "$SHOW_TAIL" == true ]]; then
echo -e "${GREEN}Showing last $TAIL_LINES log lines from the past $TIME_RANGE${NC}" echo -e "${GREEN}Showing last $TAIL_LINES log lines from the past $TIME_RANGE${NC}"
@@ -267,8 +279,8 @@ else
fi fi
# Add style arguments if specified # Add style arguments if specified
if [[ -n "${STYLE_ARGS:-}" ]]; then if [[ "$STYLE_JSON" == true ]]; then
CMD="$CMD $STYLE_ARGS" LOG_CMD+=(--style json)
fi fi
# Execute the command # Execute the command
@@ -280,9 +292,9 @@ if [[ -n "$OUTPUT_FILE" ]]; then
echo -e "${BLUE}Exporting logs to: $OUTPUT_FILE${NC}\n" echo -e "${BLUE}Exporting logs to: $OUTPUT_FILE${NC}\n"
if [[ "$SHOW_TAIL" == true ]] && [[ "$STREAM_MODE" == false ]]; then if [[ "$SHOW_TAIL" == true ]] && [[ "$STREAM_MODE" == false ]]; then
eval "$CMD" 2>&1 | tail -n "$TAIL_LINES" > "$OUTPUT_FILE" "${LOG_CMD[@]}" 2>&1 | tail -n "$TAIL_LINES" > "$OUTPUT_FILE"
else else
eval "$CMD" > "$OUTPUT_FILE" 2>&1 "${LOG_CMD[@]}" > "$OUTPUT_FILE" 2>&1
fi fi
# Check if file was created and has content # Check if file was created and has content
@@ -301,9 +313,9 @@ else
if [[ "$SHOW_TAIL" == true ]] && [[ "$STREAM_MODE" == false ]]; then if [[ "$SHOW_TAIL" == true ]] && [[ "$STREAM_MODE" == false ]]; then
# Apply tail for non-streaming mode # Apply tail for non-streaming mode
eval "$CMD" 2>&1 | tail -n "$TAIL_LINES" "${LOG_CMD[@]}" 2>&1 | tail -n "$TAIL_LINES"
echo -e "\n${YELLOW}Showing last $TAIL_LINES lines. Use --all or -n to see more.${NC}" echo -e "\n${YELLOW}Showing last $TAIL_LINES lines. Use --all or -n to see more.${NC}"
else else
eval "$CMD" "${LOG_CMD[@]}"
fi fi
fi fi