mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-15 20:15:48 +00:00
fix(gateway): override synthetic senders with MetaOriginSenderID + propagate through dashboard dispatch
Closes the two last unpropagated paths identified by the #915 e2e audit. processNormalMessage now prefers MetaOriginSenderID when the on-wire SenderID is a synthetic internal sender (notification:, ticker:, session_send_tool, system:escalation, etc.). Real user senders pass through unchanged. Defense-in-depth: MetaOriginSenderID is ignored if it itself looks synthetic, preventing a propagation bug from turning into a permission bypass. teams_tasks_mutations dashboard dispatch now injects MetaOriginSenderID into the teammate InboundMessage metadata, preferring the WS caller's sender (live dashboard) and falling back to origin_sender_id stored on the task (for deferred dispatches). Without this, dashboard-triggered team tasks in group chats would hit the empty-sender DENY rule. team_tasks_create persists origin_sender_id into task.Metadata at creation time so the deferred dashboard dispatch path can reconstruct the original actor identity.
This commit is contained in:
1 parent
2fcb9a9ee0
commit
1bd90123c6
3 files changed
+36
-1
No files matched your search
@@ -346,6 +346,24 @@ func processNormalMessage(
|
||||
schedCtx = tools.WithRunKind(schedCtx, rk)
|
||||
}
|
||||
|
||||
// Resolve effective sender: prefer MetaOriginSenderID when the on-wire
|
||||
// SenderID is an internal/synthetic one (e.g. "notification:progress",
|
||||
// "ticker:system", "system:escalation", "session_send_tool"). This lets
|
||||
// system-initiated turns that DO have a real user behind them (because
|
||||
// they propagated the origin via metadata) attribute actions to that user
|
||||
// — e.g. for CheckFileWriterPermission in group chats (#915). Synthetic
|
||||
// senders without propagation keep their on-wire value and hit F1's
|
||||
// deny-in-group rule (safe default).
|
||||
effectiveSenderID := msg.SenderID
|
||||
if bus.IsInternalSender(effectiveSenderID) {
|
||||
// Defense-in-depth: if a propagation bug ever writes a synthetic
|
||||
// value into MetaOriginSenderID, do NOT honour it. We want only real
|
||||
// user senders to override the on-wire synthetic.
|
||||
if realSender := msg.Metadata[tools.MetaOriginSenderID]; realSender != "" && !bus.IsInternalSender(realSender) {
|
||||
effectiveSenderID = realSender
|
||||
}
|
||||
}
|
||||
|
||||
// Schedule through main lane (per-session concurrency controlled by maxConcurrent)
|
||||
outCh := deps.Sched.ScheduleWithOpts(schedCtx, "main", agent.RunRequest{
|
||||
SessionKey: sessionKey,
|
||||
@@ -359,7 +377,7 @@ func processNormalMessage(
|
||||
PeerKind: peerKind,
|
||||
LocalKey: msg.Metadata["local_key"],
|
||||
UserID: userID,
|
||||
SenderID: msg.SenderID,
|
||||
SenderID: effectiveSenderID,
|
||||
SenderName: resolveSenderName(msg),
|
||||
RunID: runID,
|
||||
Stream: enableStream,
|
||||
|
||||
@@ -398,6 +398,18 @@ func (m *TeamsMethods) dispatchTaskToAgent(ctx context.Context, task *store.Team
|
||||
meta["origin_local_key"] = lk
|
||||
}
|
||||
}
|
||||
// Preserve acting sender through dashboard dispatch: prefer the live WS
|
||||
// caller's sender (rarely set) then fall back to the stored origin sender
|
||||
// from the task at creation time. Without this, group-scope tasks
|
||||
// dispatched from the dashboard would hit the empty-sender DENY rule in
|
||||
// CheckFileWriterPermission (#915 Flow F).
|
||||
if dispatchSender := store.SenderIDFromContext(ctx); dispatchSender != "" {
|
||||
meta["origin_sender_id"] = dispatchSender
|
||||
} else if task.Metadata != nil {
|
||||
if taskSender, _ := task.Metadata["origin_sender_id"].(string); taskSender != "" {
|
||||
meta["origin_sender_id"] = taskSender
|
||||
}
|
||||
}
|
||||
|
||||
m.msgBus.PublishInbound(bus.InboundMessage{
|
||||
Channel: "system",
|
||||
|
||||
@@ -204,6 +204,11 @@ func (t *TeamTasksTool) executeCreate(ctx context.Context, args map[string]any)
|
||||
if rootSpanID := tracing.ParentSpanIDFromContext(ctx); rootSpanID != uuid.Nil {
|
||||
taskMeta[TaskMetaOriginRootSpan] = rootSpanID.String()
|
||||
}
|
||||
// Persist the real acting sender so deferred/dashboard dispatches can
|
||||
// restore permission attribution when the teammate runs (#915 Flow F).
|
||||
if sender := store.SenderIDFromContext(ctx); sender != "" {
|
||||
taskMeta["origin_sender_id"] = sender
|
||||
}
|
||||
|
||||
task := &store.TeamTaskData{
|
||||
TeamID: team.ID,
|
||||
|
||||
Reference in new issue
Block a user