fix(security): address review feedback on source_channel

- hydrate_thread_from_db now passes message.channel as source_channel
  instead of None, ensuring DB-hydrated threads get proper channel auth
- Replace is_none_or (unstable) with map_or(true, ...) for MSRV compat
- Add "gateway" to trusted approval channels alongside "web"
- Document why bootstrap thread uses None for source_channel

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
This commit is contained in:
Zaki
2026-03-28 15:10:20 +00:00
committed by Claude
co-authored by Claude Opus 4.6
parent 8bd30a970e
commit 427f908e71
2 changed files with 9 additions and 5 deletions
+7 -4
View File
@@ -843,6 +843,8 @@ impl Agent {
{
use crate::agent::session::Thread;
let mut sess = session.lock().await;
// Bootstrap thread has no incoming message -- use None for
// source_channel so approvals from any channel are permitted.
let thread = Thread::with_id(id, sess.id, None);
sess.active_thread = Some(id);
sess.threads.entry(id).or_insert(thread);
@@ -1149,10 +1151,11 @@ impl Agent {
.await;
let mut sess = session.lock().await;
if let Some(thread) = sess.threads.get(&target_thread_id) {
let authorized = thread
.source_channel
.as_ref()
.is_none_or(|src| src == &message.channel || message.channel == "web");
let authorized = thread.source_channel.as_ref().map_or(true, |src| {
src == &message.channel
|| message.channel == "web"
|| message.channel == "gateway"
});
if !authorized {
tracing::warn!(
%target_thread_id,
+2 -1
View File
@@ -141,7 +141,8 @@ impl Agent {
sess.id
};
let mut thread = crate::agent::session::Thread::with_id(thread_uuid, session_id, None);
let mut thread =
crate::agent::session::Thread::with_id(thread_uuid, session_id, Some(&message.channel));
if !chat_messages.is_empty() {
thread.restore_from_messages(chat_messages);
}