ipc: close unauthenticated event-spoofing gap in emit's no-source path
The IPC "emit" method's no-source path took a bare event+data and sent it
straight to emit_tx tagged AdapterSource::System with zero validation of
the event name. Any same-UID process on the socket could send e.g.
{"event":"bread.power.ac.connected",...} and have it delivered to every
Lua subscriber indistinguishable from a real adapter event, since System
is the same tag the daemon uses for its own trusted, Rust-originated
sends (bread.system.startup, bread.profile.activated).
Fix, scoped to the actual threat model (same-UID Unix socket trust means
there's no way to cryptographically distinguish "the real bread-cli
binary" from any other local process, so a generic connection-identity
handshake would be theater):
- New AdapterSource::Manual tag for the no-source emit path. System is
now reserved for daemon-internal, Rust-code-originated sends only and
can never again be produced from data that arrived over the wire.
- The event name is rejected if its top-level dotted segment is one of
the reserved, adapter-owned domains (RESERVED_DOMAINS in
bread-shared/src/apps.rs) -- extended with bluetooth/workspace/window/
monitor, event families the Hyprland and Bluetooth adapters already
publish under but that were missing from that list. Freely-named
custom/test event names are untouched, so `bread emit <name>` and
bread-emit's fire-and-forget single-line-write design keep working
exactly as documented.
Bumped API_VERSION to 1.5.0 and updated Documentation.md's IPC emit
section and Namespaces reserved-domains list accordingly.
Also fixed a subscribe/emit race that surfaced while adding regression
tests for this: events.subscribe's ack is written to the client before
the server task actually registers on the broadcast channel, so a test
that emits immediately after reading the ack can race the registration.
Added a settle delay plus an explicit timeout (instead of an unbounded
read loop) so a future regression fails the test instead of hanging the
whole binary.
This commit is contained in:
parent
96639516b1
commit
7bb6fbb20f
6 changed files with 281 additions and 11 deletions
|
|
@ -32,9 +32,25 @@ pub enum AdapterSource {
|
|||
Power,
|
||||
/// Network state (rtnetlink / NetworkManager).
|
||||
Network,
|
||||
/// Internal events synthesized by the daemon itself
|
||||
/// (e.g. `bread.profile.activated`, `bread.state.changed.*`).
|
||||
/// Internal events synthesized by the daemon itself, i.e. trusted,
|
||||
/// Rust-code-originated sends via `emit_tx` (e.g. `bread.system.startup`,
|
||||
/// `bread.profile.activated`, `bread.state.changed.*`, and Lua's
|
||||
/// `bread.emit()` binding). Never assignable from data that arrived
|
||||
/// over the IPC socket — see [`Manual`](AdapterSource::Manual) for that
|
||||
/// case. *Since: v1.5 — this constraint is now enforced; previously the
|
||||
/// IPC `emit` method's no-`source` path could also tag events `System`.*
|
||||
System,
|
||||
/// A manual `emit` IPC request with no `source` param — a human or
|
||||
/// script poked the daemon's Unix socket directly (e.g. `bread emit
|
||||
/// <event>` for testing Lua handlers without unplugging cables).
|
||||
/// Distinct from [`System`](AdapterSource::System) so downstream Lua
|
||||
/// modules and tooling can tell "someone manually injected this event"
|
||||
/// apart from "a real adapter observed this" or "the daemon itself
|
||||
/// produced this." The IPC boundary restricts which event names may be
|
||||
/// tagged this way — it may not claim an adapter-owned namespace (see
|
||||
/// `apps::is_reserved_domain`), but is otherwise free for custom/test
|
||||
/// event names. *Since: v1.5*
|
||||
Manual,
|
||||
/// BlueZ Bluetooth stack via D-Bus.
|
||||
Bluetooth,
|
||||
/// Shell precmd/preexec hooks (terminal command lifecycle, cwd changes).
|
||||
|
|
@ -222,6 +238,10 @@ mod tests {
|
|||
serde_json::to_string(&AdapterSource::System).unwrap(),
|
||||
"\"system\""
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::to_string(&AdapterSource::Manual).unwrap(),
|
||||
"\"manual\""
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::to_string(&AdapterSource::Bluetooth).unwrap(),
|
||||
"\"bluetooth\""
|
||||
|
|
@ -268,6 +288,7 @@ mod tests {
|
|||
AdapterSource::Power,
|
||||
AdapterSource::Network,
|
||||
AdapterSource::System,
|
||||
AdapterSource::Manual,
|
||||
AdapterSource::Bluetooth,
|
||||
AdapterSource::Terminal,
|
||||
AdapterSource::Git,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue