fix(gateway/restart): write sentinel with continuationMessage on coalesced restart#74443
fix(gateway/restart): write sentinel with continuationMessage on coalesced restart#74443hclsys wants to merge 1 commit into
Conversation
…esced restart When scheduleGatewaySigusr1Restart returns coalesced=true, the beforeEmit hook is never called, so writeRestartSentinel never fires and any continuationMessage passed by the caller is silently dropped. Call rewriteRestartSentinel after the coalesced return to merge the incoming continuationMessage into any existing sentinel. Fixes openclaw#74424.
🔒 Aisle Security AnalysisWe found 1 potential security issue(s) in this PR:
1. 🟡 Cross-session continuation overwrite when coalescing gateway restart requests
DescriptionIn
Vulnerable code: if (scheduled.coalesced && continuationMessage) {
await rewriteRestartSentinel((existing) => ({
...existing,
continuation: buildRestartSuccessContinuation({ sessionKey, continuationMessage }),
})).catch(() => undefined);
}RecommendationEnsure sentinel rewrites cannot mix data across sessions. Minimal safe guard: only rewrite the sentinel if it matches the current request's session identity (and optionally delivery context), otherwise leave it unchanged. if (scheduled.coalesced && continuationMessage) {
await rewriteRestartSentinel((existing) => {
if ((existing.sessionKey ?? "").trim() !== (sessionKey ?? "").trim()) {
return existing; // do not overwrite another session's continuation
}
return {
...existing,
continuation: buildRestartSuccessContinuation({ sessionKey, continuationMessage }),
};
});
}More robust options:
Analyzed PR: #74443 at commit Last updated on: 2026-04-29T15:26:50Z |
|
Closing this PR because the author has more than 10 active PRs in this repo. Please reduce the active PR queue and reopen or resubmit once it is back under the limit. You can close your own PRs to get back under the limit. |
Greptile SummaryThis PR fixes a bug where Confidence Score: 4/5Safe to merge — the fix is targeted and well-scoped, with errors swallowed to avoid surfacing on the coalesced path. No P0 or P1 issues found. The No files require special attention. Reviews (1): Last reviewed commit: "fix(gateway/restart): write sentinel wit..." | Re-trigger Greptile |
…t continuations (openclaw#86742) When two sessions issue gateway.restart with continuationMessage close together, the scheduler Path B updatePendingRestartEmitHooks unconditionally overwrote the existing pending hooks, silently dropping the first sessions continuation and potentially routing the second sessions continuation back to the first session (CWE-200 finding flagged by aisle-research-bot on prior attempt openclaw#74443). Add a session-routing guard: scheduleGatewaySigusr1Restart now accepts an optional sessionKey and tracks the pending restarts owning session. Coalesced callers from a different session are rejected at the hook- update step and the new ScheduledRestart.emitHooksQueued: false field surfaces the drop to the caller. The gateway tool propagates this as continuationQueued: false in the tool response, matching openclaw#83370 narrow report-only surface. Same-session debounce/replace and legacy hookless callers behave the same as before. Refs openclaw#86742
…t continuations (openclaw#86742) When two sessions issue gateway.restart with continuationMessage close together, the scheduler Path B updatePendingRestartEmitHooks unconditionally overwrote the existing pending hooks, silently dropping the first sessions continuation and potentially routing the second sessions continuation back to the first session (CWE-200 finding flagged by aisle-research-bot on prior attempt openclaw#74443). Add a session-routing guard: scheduleGatewaySigusr1Restart now accepts an optional sessionKey and tracks the pending restarts owning session. Coalesced callers from a different session are rejected at the hook- update step and the new ScheduledRestart.emitHooksQueued: false field surfaces the drop to the caller. The gateway tool propagates this as continuationQueued: false in the tool response, matching openclaw#83370 narrow report-only surface. Same-session debounce/replace and legacy hookless callers behave the same as before. Refs openclaw#86742
…t continuations (openclaw#86742) When two sessions issue gateway.restart with continuationMessage close together, the scheduler Path B updatePendingRestartEmitHooks unconditionally overwrote the existing pending hooks, silently dropping the first sessions continuation and potentially routing the second sessions continuation back to the first session (CWE-200 finding flagged by aisle-research-bot on prior attempt openclaw#74443). Add a session-routing guard: scheduleGatewaySigusr1Restart now accepts an optional sessionKey and tracks the pending restarts owning session. Coalesced callers from a different session are rejected at the hook- update step and the new ScheduledRestart.emitHooksQueued: false field surfaces the drop to the caller. The gateway tool propagates this as continuationQueued: false in the tool response, matching openclaw#83370 narrow report-only surface. Same-session debounce/replace and legacy hookless callers behave the same as before. Refs openclaw#86742
…t continuations (openclaw#86742) When two sessions issue gateway.restart with continuationMessage close together, the scheduler Path B updatePendingRestartEmitHooks unconditionally overwrote the existing pending hooks, silently dropping the first sessions continuation and potentially routing the second sessions continuation back to the first session (CWE-200 finding flagged by aisle-research-bot on prior attempt openclaw#74443). Add a session-routing guard: scheduleGatewaySigusr1Restart now accepts an optional sessionKey and tracks the pending restarts owning session. Coalesced callers from a different session are rejected at the hook- update step and the new ScheduledRestart.emitHooksQueued: false field surfaces the drop to the caller. The gateway tool propagates this as continuationQueued: false in the tool response, matching openclaw#83370 narrow report-only surface. Same-session debounce/replace and legacy hookless callers behave the same as before. Refs openclaw#86742
…t continuations (openclaw#86742) When two sessions issue gateway.restart with continuationMessage close together, the scheduler Path B updatePendingRestartEmitHooks unconditionally overwrote the existing pending hooks, silently dropping the first sessions continuation and potentially routing the second sessions continuation back to the first session (CWE-200 finding flagged by aisle-research-bot on prior attempt openclaw#74443). Add a session-routing guard: scheduleGatewaySigusr1Restart now accepts an optional sessionKey and tracks the pending restarts owning session. Coalesced callers from a different session are rejected at the hook- update step and the new ScheduledRestart.emitHooksQueued: false field surfaces the drop to the caller. The gateway tool propagates this as continuationQueued: false in the tool response, matching openclaw#83370 narrow report-only surface. Same-session debounce/replace and legacy hookless callers behave the same as before. Refs openclaw#86742
…t continuations (openclaw#86742) When two sessions issue gateway.restart with continuationMessage close together, the scheduler Path B updatePendingRestartEmitHooks unconditionally overwrote the existing pending hooks, silently dropping the first sessions continuation and potentially routing the second sessions continuation back to the first session (CWE-200 finding flagged by aisle-research-bot on prior attempt openclaw#74443). Add a session-routing guard: scheduleGatewaySigusr1Restart now accepts an optional sessionKey and tracks the pending restarts owning session. Coalesced callers from a different session are rejected at the hook- update step and the new ScheduledRestart.emitHooksQueued: false field surfaces the drop to the caller. The gateway tool propagates this as continuationQueued: false in the tool response, matching openclaw#83370 narrow report-only surface. Same-session debounce/replace and legacy hookless callers behave the same as before. Refs openclaw#86742
…t continuations (openclaw#86742) When two sessions issue gateway.restart with continuationMessage close together, the scheduler Path B updatePendingRestartEmitHooks unconditionally overwrote the existing pending hooks, silently dropping the first sessions continuation and potentially routing the second sessions continuation back to the first session (CWE-200 finding flagged by aisle-research-bot on prior attempt openclaw#74443). Add a session-routing guard: scheduleGatewaySigusr1Restart now accepts an optional sessionKey and tracks the pending restarts owning session. Coalesced callers from a different session are rejected at the hook- update step and the new ScheduledRestart.emitHooksQueued: false field surfaces the drop to the caller. The gateway tool propagates this as continuationQueued: false in the tool response, matching openclaw#83370 narrow report-only surface. Same-session debounce/replace and legacy hookless callers behave the same as before. Refs openclaw#86742
…t continuations (openclaw#86742) When two sessions issue gateway.restart with continuationMessage close together, the scheduler Path B updatePendingRestartEmitHooks unconditionally overwrote the existing pending hooks, silently dropping the first sessions continuation and potentially routing the second sessions continuation back to the first session (CWE-200 finding flagged by aisle-research-bot on prior attempt openclaw#74443). Add a session-routing guard: scheduleGatewaySigusr1Restart now accepts an optional sessionKey and tracks the pending restarts owning session. Coalesced callers from a different session are rejected at the hook- update step and the new ScheduledRestart.emitHooksQueued: false field surfaces the drop to the caller. The gateway tool propagates this as continuationQueued: false in the tool response, matching openclaw#83370 narrow report-only surface. Same-session debounce/replace and legacy hookless callers behave the same as before. Refs openclaw#86742
…t continuations (openclaw#86742) When two sessions issue gateway.restart with continuationMessage close together, the scheduler Path B updatePendingRestartEmitHooks unconditionally overwrote the existing pending hooks, silently dropping the first sessions continuation and potentially routing the second sessions continuation back to the first session (CWE-200 finding flagged by aisle-research-bot on prior attempt openclaw#74443). Add a session-routing guard: scheduleGatewaySigusr1Restart now accepts an optional sessionKey and tracks the pending restarts owning session. Coalesced callers from a different session are rejected at the hook- update step and the new ScheduledRestart.emitHooksQueued: false field surfaces the drop to the caller. The gateway tool propagates this as continuationQueued: false in the tool response, matching openclaw#83370 narrow report-only surface. Same-session debounce/replace and legacy hookless callers behave the same as before. Refs openclaw#86742
…t continuations (#86742) (#87323) * fix(infra/agents): session-routing guard for coalesced gateway restart continuations (#86742) When two sessions issue gateway.restart with continuationMessage close together, the scheduler Path B updatePendingRestartEmitHooks unconditionally overwrote the existing pending hooks, silently dropping the first sessions continuation and potentially routing the second sessions continuation back to the first session (CWE-200 finding flagged by aisle-research-bot on prior attempt #74443). Add a session-routing guard: scheduleGatewaySigusr1Restart now accepts an optional sessionKey and tracks the pending restarts owning session. Coalesced callers from a different session are rejected at the hook- update step and the new ScheduledRestart.emitHooksQueued: false field surfaces the drop to the caller. The gateway tool propagates this as continuationQueued: false in the tool response, matching #83370 narrow report-only surface. Same-session debounce/replace and legacy hookless callers behave the same as before. Refs #86742 * fix(infra): preserve queued restart continuation on forced bypass * fix(infra): make forced restart hook preservation explicit * fix(infra): guard restart continuation ownership before reschedule * fix(infra): report hookless coalesced restarts accurately * fix(infra): trust runtime session for restart sentinel routing * fix(infra): preserve earlier restart reschedule semantics * fix(agents): trust runtime session for update continuations * fix(infra): preserve hookless forced restart continuations --------- Co-authored-by: Peter Steinberger <[email protected]>
…t continuations (openclaw#86742) (openclaw#87323) * fix(infra/agents): session-routing guard for coalesced gateway restart continuations (openclaw#86742) When two sessions issue gateway.restart with continuationMessage close together, the scheduler Path B updatePendingRestartEmitHooks unconditionally overwrote the existing pending hooks, silently dropping the first sessions continuation and potentially routing the second sessions continuation back to the first session (CWE-200 finding flagged by aisle-research-bot on prior attempt openclaw#74443). Add a session-routing guard: scheduleGatewaySigusr1Restart now accepts an optional sessionKey and tracks the pending restarts owning session. Coalesced callers from a different session are rejected at the hook- update step and the new ScheduledRestart.emitHooksQueued: false field surfaces the drop to the caller. The gateway tool propagates this as continuationQueued: false in the tool response, matching openclaw#83370 narrow report-only surface. Same-session debounce/replace and legacy hookless callers behave the same as before. Refs openclaw#86742 * fix(infra): preserve queued restart continuation on forced bypass * fix(infra): make forced restart hook preservation explicit * fix(infra): guard restart continuation ownership before reschedule * fix(infra): report hookless coalesced restarts accurately * fix(infra): trust runtime session for restart sentinel routing * fix(infra): preserve earlier restart reschedule semantics * fix(agents): trust runtime session for update continuations * fix(infra): preserve hookless forced restart continuations --------- Co-authored-by: Peter Steinberger <[email protected]>
…t continuations (openclaw#86742) (openclaw#87323) * fix(infra/agents): session-routing guard for coalesced gateway restart continuations (openclaw#86742) When two sessions issue gateway.restart with continuationMessage close together, the scheduler Path B updatePendingRestartEmitHooks unconditionally overwrote the existing pending hooks, silently dropping the first sessions continuation and potentially routing the second sessions continuation back to the first session (CWE-200 finding flagged by aisle-research-bot on prior attempt openclaw#74443). Add a session-routing guard: scheduleGatewaySigusr1Restart now accepts an optional sessionKey and tracks the pending restarts owning session. Coalesced callers from a different session are rejected at the hook- update step and the new ScheduledRestart.emitHooksQueued: false field surfaces the drop to the caller. The gateway tool propagates this as continuationQueued: false in the tool response, matching openclaw#83370 narrow report-only surface. Same-session debounce/replace and legacy hookless callers behave the same as before. Refs openclaw#86742 * fix(infra): preserve queued restart continuation on forced bypass * fix(infra): make forced restart hook preservation explicit * fix(infra): guard restart continuation ownership before reschedule * fix(infra): report hookless coalesced restarts accurately * fix(infra): trust runtime session for restart sentinel routing * fix(infra): preserve earlier restart reschedule semantics * fix(agents): trust runtime session for update continuations * fix(infra): preserve hookless forced restart continuations --------- Co-authored-by: Peter Steinberger <[email protected]>
…t continuations (openclaw#86742) (openclaw#87323) * fix(infra/agents): session-routing guard for coalesced gateway restart continuations (openclaw#86742) When two sessions issue gateway.restart with continuationMessage close together, the scheduler Path B updatePendingRestartEmitHooks unconditionally overwrote the existing pending hooks, silently dropping the first sessions continuation and potentially routing the second sessions continuation back to the first session (CWE-200 finding flagged by aisle-research-bot on prior attempt openclaw#74443). Add a session-routing guard: scheduleGatewaySigusr1Restart now accepts an optional sessionKey and tracks the pending restarts owning session. Coalesced callers from a different session are rejected at the hook- update step and the new ScheduledRestart.emitHooksQueued: false field surfaces the drop to the caller. The gateway tool propagates this as continuationQueued: false in the tool response, matching openclaw#83370 narrow report-only surface. Same-session debounce/replace and legacy hookless callers behave the same as before. Refs openclaw#86742 * fix(infra): preserve queued restart continuation on forced bypass * fix(infra): make forced restart hook preservation explicit * fix(infra): guard restart continuation ownership before reschedule * fix(infra): report hookless coalesced restarts accurately * fix(infra): trust runtime session for restart sentinel routing * fix(infra): preserve earlier restart reschedule semantics * fix(agents): trust runtime session for update continuations * fix(infra): preserve hookless forced restart continuations --------- Co-authored-by: Peter Steinberger <[email protected]>
Problem
When
scheduleGatewaySigusr1Restartreturns{ coalesced: true }(two paths insrc/infra/restart.ts), thebeforeEmithook registered by the gateway tool is never called. This meanswriteRestartSentinelnever fires, so thecontinuationMessagesupplied by the caller is silently dropped.Fix
After
scheduleGatewaySigusr1Restartreturns, if the result is coalesced and the caller supplied acontinuationMessage, callrewriteRestartSentinelto merge the continuation into any existing sentinel file. The call is fire-and-forget (.catch(() => undefined)) so a missing sentinel does not surface as an error on the coalesced path.Testing
tsc --noEmit --project tsconfig.core.jsonupdatePendingRestartEmitHooksoverwrites, does not chain)Fixes #74424.