[P0] ZARA-012 — Remove shell interpolation from alerts, search, app launch, and dictation control #13

Closed
opened 2026-07-18 19:20:18 +00:00 by lost-rob0t · 1 comment
lost-rob0t commented 2026-07-18 19:20:18 +00:00 (Migrated from github.com)

Problem

Several paths construct shell command strings from data that can contain user, config, or LLM-controlled text:

  • modules/alert.pl embeds title/message inside single-quoted notify-send shell text without escaping.
  • modules/commands.pl runs app mappings through /bin/sh -c and invokes search through shell/1.
  • modules/dictate.pl builds start and kill commands through sh -c.

This creates injection risk and makes argument boundaries/exit status unreliable.

Required fix

  • Use process_create/3 or Python subprocess argument arrays with no shell wherever possible.
  • Represent configured commands as executable plus argv, or parse a deliberately constrained trusted format.
  • Use direct xdg-open argv for URLs.
  • Preserve detached launch behavior while returning real startup errors.
  • Validate any remaining trusted shell escape hatch and keep it opt-in and clearly named.

Required tests

  • Injection regression corpus containing quotes, semicolons, command substitutions, newlines, leading dashes, and Unicode.
  • Assert exact executable/argv passed to mocked process APIs.
  • Add scripts/test-process-safety.sh; it must use harmless fake executables and prove no marker command is executed.

Acceptance

  • Normal runtime paths perform no sh -c/shell/1 with dynamic text.
  • Alerts and searches preserve literal content.
  • Launch failures propagate accurately.

Branch

fix/zara-012-process-safety

Dependencies

ZARA-003, ZARA-004, ZARA-005.

## Problem Several paths construct shell command strings from data that can contain user, config, or LLM-controlled text: - `modules/alert.pl` embeds title/message inside single-quoted `notify-send` shell text without escaping. - `modules/commands.pl` runs app mappings through `/bin/sh -c` and invokes search through `shell/1`. - `modules/dictate.pl` builds start and kill commands through `sh -c`. This creates injection risk and makes argument boundaries/exit status unreliable. ## Required fix - Use `process_create/3` or Python subprocess argument arrays with no shell wherever possible. - Represent configured commands as executable plus argv, or parse a deliberately constrained trusted format. - Use direct `xdg-open` argv for URLs. - Preserve detached launch behavior while returning real startup errors. - Validate any remaining trusted shell escape hatch and keep it opt-in and clearly named. ## Required tests - Injection regression corpus containing quotes, semicolons, command substitutions, newlines, leading dashes, and Unicode. - Assert exact executable/argv passed to mocked process APIs. - Add `scripts/test-process-safety.sh`; it must use harmless fake executables and prove no marker command is executed. ## Acceptance - Normal runtime paths perform no `sh -c`/`shell/1` with dynamic text. - Alerts and searches preserve literal content. - Launch failures propagate accurately. ## Branch `fix/zara-012-process-safety` ## Dependencies ZARA-003, ZARA-004, ZARA-005.
Owner

Reopened as a current regression on exact master 7340ca414fff8b543a3a279acd93c2bb613b1eab.

modules/commands.pl:open_app/1 still has a fallback branch that takes an unknown AppName from the resolved open intent and calls launch_process(AppName, []). Because launch_process/2 resolves bare atoms with path/1, an unregistered speech/user-supplied executable name can bypass app_mapping/2 + direct_app/1 allow-listing. This is no longer shell interpolation, but it violates the original closed argument/process-safety intent and the current typed capability invariant.

RAGE recovery target: prove RED for unknown app names, fail closed instead of executing them, preserve configured app_mapping/2 and direct_app/1, then run the focused process-safety gate, full scripts/test-all.sh, nix flake check, build/package gates, and exact-head Actions. Zara Core only; no android/** or external repositories.

Reopened as a current regression on exact master `7340ca414fff8b543a3a279acd93c2bb613b1eab`. `modules/commands.pl:open_app/1` still has a fallback branch that takes an unknown `AppName` from the resolved `open` intent and calls `launch_process(AppName, [])`. Because `launch_process/2` resolves bare atoms with `path/1`, an unregistered speech/user-supplied executable name can bypass `app_mapping/2` + `direct_app/1` allow-listing. This is no longer shell interpolation, but it violates the original closed argument/process-safety intent and the current typed capability invariant. RAGE recovery target: prove RED for unknown app names, fail closed instead of executing them, preserve configured `app_mapping/2` and `direct_app/1`, then run the focused process-safety gate, full `scripts/test-all.sh`, `nix flake check`, build/package gates, and exact-head Actions. Zara Core only; no `android/**` or external repositories.
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
nsaspy/zara#13
No description provided.