fix(agent): block thread_id-based context pollution across users (#760)

* fix(agent): prevent forged thread UUID context/write contamination

* fix(agent): close thread_id race and reject forged UUID hydration

* fix(ci): satisfy clippy and fmt checks after rebase
This commit is contained in:
pikaxinge
2026-03-11 16:52:31 -07:00
committed by GitHub
parent c8cac0925d
commit 2094d6e30d
10 changed files with 448 additions and 54 deletions
+56 -6
View File
@@ -641,14 +641,20 @@ mod tests {
let conv_id = uuid::Uuid::new_v4();
// ensure_conversation should create the row.
db.ensure_conversation(conv_id, "web", "carol", None)
.await
.expect("ensure first");
assert!(
db.ensure_conversation(conv_id, "web", "carol", None)
.await
.expect("ensure first"),
"first ensure_conversation should create the row"
);
// Calling again with the same ID should not error.
db.ensure_conversation(conv_id, "web", "carol", None)
.await
.expect("ensure second (idempotent)");
assert!(
db.ensure_conversation(conv_id, "web", "carol", None)
.await
.expect("ensure second (idempotent)"),
"second ensure_conversation should touch owned row"
);
// Should be able to add messages to it.
let msg_id = db
@@ -666,6 +672,50 @@ mod tests {
assert_eq!(msgs[0].content, "test message");
}
#[cfg(feature = "libsql")]
#[tokio::test]
async fn test_ensure_conversation_foreign_conflict_does_not_touch_last_activity() {
let harness = TestHarnessBuilder::new().build().await;
let db = &harness.db;
let conv_id = db
.create_conversation("web", "alice", None)
.await
.expect("create conversation");
let before = db
.list_conversations_all_channels("alice", 10)
.await
.expect("list conversations before foreign ensure")
.into_iter()
.find(|c| c.id == conv_id)
.expect("conversation must exist before foreign ensure")
.last_activity;
tokio::time::sleep(std::time::Duration::from_millis(25)).await;
assert!(
!db.ensure_conversation(conv_id, "web", "mallory", None)
.await
.expect("foreign ensure should not error"),
"foreign ensure_conversation should report not ensured"
);
let after = db
.list_conversations_all_channels("alice", 10)
.await
.expect("list conversations after foreign ensure")
.into_iter()
.find(|c| c.id == conv_id)
.expect("conversation must still exist after foreign ensure")
.last_activity;
assert_eq!(
after, before,
"foreign ensure_conversation should not mutate last_activity"
);
}
#[cfg(feature = "libsql")]
#[tokio::test]
async fn test_paginated_messages() {