Skip to content

fix(commands): require trusted session for /image path reads#5988

Merged
bug-ops merged 1 commit into
mainfrom
fix/5967-image-arbitrary-read
Jul 10, 2026
Merged

fix(commands): require trusted session for /image path reads#5988
bug-ops merged 1 commit into
mainfrom
fix/5967-image-arbitrary-read

Conversation

@bug-ops

@bug-ops bug-ops commented Jul 10, 2026

Copy link
Copy Markdown
Owner

Summary

  • ImageCommand never overrode CommandHandler::requires_auth() (default false), so remote channels — Telegram, Discord, Slack — could invoke /image <path> and read any file reachable under the server's working directory via load_image's std::fs::read, subject only to path_guard::classify_relative_path's "no absolute path, no .." check (CWE-284).
  • ImageCommand::requires_auth() now returns true, closing the remote vector at the existing dispatch gate (CommandRegistry::dispatch), matching the pattern already used by the sibling /cache-stats and /notify-test handlers in the same module. /image remains available from trusted local sessions (CLI/TUI/ACP-stdio).
  • Added dispatch-level regression tests asserting /image is allowed when trusted and rejected when untrusted.

Out of scope (filed separately)

  • The CLI-layer duplicate implementation in zeph-channels/src/cli.rs is local-operator-only (intercepted at the stdin loop before dispatch) — not remote-exploitable, left unchanged.
  • A related same-class gap was found during review: the gateway webhook path can inherit trusted=true from a local primary channel when no bearer token is configured, letting an unauthenticated webhook POST reach /image the same way. This is a pre-existing, broader gap not introduced or fixed by this change; a follow-up issue is being filed separately.

Fixes #5967

Test plan

  • cargo +nightly fmt --check
  • cargo clippy --profile ci --workspace --all-targets --features "desktop,ide,server,chat,pdf,scheduler,testing" -- -D warnings
  • cargo nextest run --config-file .github/nextest.toml --workspace --features "desktop,ide,server,chat,pdf,scheduler" --lib --bins (12639 passed)
  • cargo nextest run --config-file .github/nextest.toml -p zeph-commands (133/133, includes new dispatch tests)
  • RUSTFLAGS="-D warnings" RUSTDOCFLAGS="--deny rustdoc::broken_intra_doc_links" cargo doc --no-deps --workspace --features "desktop,ide,server,chat,pdf,scheduler"

@github-actions github-actions Bot added bug Something isn't working size/S Small PR (11-50 lines) documentation Improvements or additions to documentation rust Rust code changes labels Jul 10, 2026
@bug-ops

bug-ops commented Jul 10, 2026

Copy link
Copy Markdown
Owner Author

Follow-up filed for the out-of-scope gateway webhook trust-inheritance gap found during review (impl-critic finding S1): #5989

ImageCommand never overrode CommandHandler::requires_auth(), so
Telegram/Discord/Slack sessions -- none of which are trusted by
CommandRegistry::dispatch -- could invoke /image <path> to read any
file reachable under the server's working directory (CWE-284).

Override requires_auth() to true, matching the existing
/cache-stats and /notify-test handlers in the same module. /image
remains available from local CLI/TUI/ACP-stdio sessions.
@bug-ops
bug-ops force-pushed the fix/5967-image-arbitrary-read branch from 1d5df59 to 0bc8326 Compare July 10, 2026 23:14
@bug-ops
bug-ops enabled auto-merge (squash) July 10, 2026 23:14
@bug-ops
bug-ops merged commit d09049b into main Jul 10, 2026
43 checks passed
@bug-ops
bug-ops deleted the fix/5967-image-arbitrary-read branch July 10, 2026 23:22
bug-ops added a commit that referenced this pull request Jul 13, 2026
… durable replay dedup (#6203)

CommandHandler::requires_auth() defaulted to false (accessible from all
channels), a recurring defect class where new handlers silently stayed
reachable from untrusted remote channels until an audit caught them (4th
recurrence: #5967/#5988, #5997/#6002, #6003/PR#6033). Flip the default to
true (fail-closed); the six previously-implicit read-only handlers now
override it explicitly to preserve behavior.

Also fixes durable subagent replay's channel-notify side effects, which
bypassed the ctx.step() dedup discipline (spec-064 INV-10): a resumed
parent could refire the "replayed from durable journal" notice on every
restart. Rather than gating via ctx.step (found to break step-id
determinism between fresh and replay runs), dedup via an out-of-band
notified_at claim column on durable_promises, consuming no step id.

Tightens the doc comment on resolve_durable_spawn_gate's still-pending
resume branch to make the LocalBackend-only safety rationale explicit
at the call site.

Closes #6034
Closes #6027
Closes #6010
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working documentation Improvements or additions to documentation rust Rust code changes size/S Small PR (11-50 lines)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SECURITY: /image command allows unauthenticated remote users (Telegram/Discord/Slack) to read arbitrary files under the server's working directory

1 participant