Skip to content

Commit 9eb1c77

Browse files
committed
fix(security): memMsgIdx stale after trimContext drops the memory message (M5)
trimContext starts dropping messages from index 2 onward (keeps messages[0] system + messages[1] task). The memory message is initially at index 1, but when trimContext injects a trim-warning at index 1, the memory shifts to index 2 — right into the droppable zone. The existing fix-up code handles the trim-warning shift (incrementing memMsgIdx), but if trimContext later drops the memory message itself, memMsgIdx becomes stale: it points to whatever message shifted into that slot (user task, assistant response, etc.), and the next memory update overwrites that wrong message with memory content. Fix: after the trim-warning fix-up, verify that the message at memMsgIdx still has Role="system". If not, the memory was dropped — reset memMsgIdx to -1 so the memory update code re-inserts it at the correct position (index 1) instead of overwriting a user or assistant message. Fixes M5 from security audit.
1 parent 26a3a0b commit 9eb1c77

1 file changed

Lines changed: 15 additions & 0 deletions

File tree

internal/loop/loop.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,21 @@ func (e *Engine) runLoop(ctx context.Context, messages []llm.Message) (string, [
423423
}
424424
}
425425

426+
// After trimContext, verify the memory message still exists at the
427+
// tracked position. trimContext starts dropping from index 2 onward,
428+
// and the memory message can shift into that range after a trim
429+
// warning is injected (shifting it from index 1 to 2). Once at index
430+
// 2, it can be dropped by subsequent trimContext calls, leaving
431+
// memMsgIdx pointing to the wrong message (a user/assistant message
432+
// that shifted into that slot). When this happens, reset memMsgIdx
433+
// so the memory is re-inserted at the correct position.
434+
if e.memMsgIdx >= 0 && e.memMsgIdx < len(messages) {
435+
if messages[e.memMsgIdx].Role != "system" {
436+
// Memory message was dropped — re-insert on next update.
437+
e.memMsgIdx = -1
438+
}
439+
}
440+
426441
// Load relevant skills based on latest user input (once per message)
427442
if e.skillLoader != nil {
428443
if userMsg := lastUserMessage(messages); userMsg != "" && userMsg != e.lastSkillMsg {

0 commit comments

Comments
 (0)