Gateway: harden node event trust boundaries#57691
Gateway: harden node event trust boundaries#57691jacobtomlinson merged 3 commits intoopenclaw:mainfrom
Conversation
|
🤖 We're reviewing this PR with Aisle We're running a security check on the changes in this PR now. This usually takes a few minutes. ⌛ Progress:
Latest run failed. Keeping previous successful results. Trace ID: Last updated on: 2026-03-30T15:55:42Z Latest run failed. Keeping previous successful results. Trace ID: Last updated on: 2026-03-30T16:40:47Z |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e1276252c7
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Greptile SummaryThis PR hardens the gateway's trust boundaries across three vectors: node-originated agent runs are restricted to a small allowlist of safe tools ( Issues found:
Confidence Score: 4/5Mostly safe, but a formatting regression causes channel summary lines to appear without their System: prefix in the model's prompt context on new sessions. The tool-allowlist and trust-bit changes are correct and well-tested. The P1 regression in session-system-events.ts — where buildChannelSummary lines lose their System: prefix after the refactor — is a real behavioral change that affects what the model sees on new main sessions. It should be fixed before merging. src/auto-reply/reply/session-system-events.ts — the buildChannelSummary unshift path needs System: prefix applied to each line.
|
| Filename | Overview |
|---|---|
| src/agents/pi-tools.ts | Adds a TOOL_ALLOW_BY_MESSAGE_PROVIDER allowlist for the 'node' provider, restricting runs to canvas, image, pdf, tts, web_fetch, and web_search. Allowlist check correctly takes precedence over the existing denylist. |
| src/agents/pi-tools.whatsapp-login-gating.test.ts | Adds regression test verifying that node-originated runs are limited to the safe tool subset and cannot access exec, read, write, edit, message, sessions_send, or subagents. |
| src/auto-reply/reply/session-system-events.ts | Refactors formatting to apply System/System (untrusted) prefix per-event rather than uniformly at return time. Introduces a regression: buildChannelSummary lines are unshift-ed into systemLines without any prefix, so they appear in the output without the System: attribution that all other lines carry. |
| src/gateway/server-node-events.ts | Applies sanitizeInboundSystemTags to notification title and text before embedding in the event summary, and marks all notification-derived events as trusted: false. packageName and key are not sanitized (P2 defense-in-depth concern, mitigated by the trusted: false wrapper). |
| src/gateway/server-node-events.test.ts | Updates existing notification test expectations to include trusted: false and adds a new test validating that System: and bracketed markers in notification payloads are neutralized before enqueueing. |
| src/infra/system-events.ts | Adds an optional trusted?: boolean field to SystemEvent and SystemEventOptions, stored as options.trusted !== false (defaults to true), correctly implementing an opt-out trust model. |
| src/infra/system-events.test.ts | Adds a test confirming that events enqueued with trusted: false are formatted with the System (untrusted): prefix by drainFormattedSystemEvents. |
Comments Outside Diff (1)
-
src/gateway/server-node-events.ts, line 483-491 (link)packageNameandkeyembedded without sanitizationtitleandtextare passed throughsanitizeInboundSystemTagsbefore being embedded insummary, butpackageNameandkeycome from the node payload and are inserted verbatim. A malicious node could craft apackageNamethat closes the parenthetical early and appends a rawSystem:token inside the event body.In practice the whole event is marked
trusted: false, sodrainFormattedSystemEventswraps everything inSystem (untrusted):regardless. However, applying the same sanitization to these fields would be more consistent and avoid confusing nested untrusted markers inside the body. This is a defense-in-depth suggestion.Prompt To Fix With AI
This is a comment left during a code review. Path: src/gateway/server-node-events.ts Line: 483-491 Comment: **`packageName` and `key` embedded without sanitization** `title` and `text` are passed through `sanitizeInboundSystemTags` before being embedded in `summary`, but `packageName` and `key` come from the node payload and are inserted verbatim. A malicious node could craft a `packageName` that closes the parenthetical early and appends a raw `System:` token inside the event body. In practice the whole event is marked `trusted: false`, so `drainFormattedSystemEvents` wraps everything in `System (untrusted):` regardless. However, applying the same sanitization to these fields would be more consistent and avoid confusing nested untrusted markers inside the body. This is a defense-in-depth suggestion. How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/auto-reply/reply/session-system-events.ts
Line: 96-108
Comment:
**Channel summary lines lose their `System:` prefix**
In the old code, the `System:` prefix was applied at the final `return` statement, uniformly covering **all** `systemLines` — including those `unshift`ed from `buildChannelSummary`. After this refactor, the prefix is applied per-event during the `flatMap`, but the channel-summary lines are inserted via `systemLines.unshift(...summary)` and then joined with no additional wrapping. They end up in the output without any `System:` prefix.
Old output (on a new main session):
```
System: WhatsApp: linked +1234567890 auth 2h ago
System: - default (linked)
System: [ts] Node connected
```
New output:
```
WhatsApp: linked +1234567890 auth 2h ago
- default (linked)
System: [ts] Node connected
```
The model loses the trusted attribution marker for channel-status lines, and they become visually indistinguishable from user content. The fix is to apply the trusted prefix when inserting summary lines:
```ts
if (params.isMainSession && params.isNewSession) {
const summary = await buildChannelSummary(params.cfg);
if (summary.length > 0) {
systemLines.unshift(...summary.map((line) => `System: ${line}`));
}
}
```
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: src/gateway/server-node-events.ts
Line: 483-491
Comment:
**`packageName` and `key` embedded without sanitization**
`title` and `text` are passed through `sanitizeInboundSystemTags` before being embedded in `summary`, but `packageName` and `key` come from the node payload and are inserted verbatim. A malicious node could craft a `packageName` that closes the parenthetical early and appends a raw `System:` token inside the event body.
In practice the whole event is marked `trusted: false`, so `drainFormattedSystemEvents` wraps everything in `System (untrusted):` regardless. However, applying the same sanitization to these fields would be more consistent and avoid confusing nested untrusted markers inside the body. This is a defense-in-depth suggestion.
How can I resolve this? If you propose a fix, please make it concise.Reviews (1): Last reviewed commit: "Gateway: harden node event trust boundar..." | Re-trigger Greptile
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3cc06876bd
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8a60acf9fb
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| voice: ["tts"], | ||
| }; | ||
| const TOOL_ALLOW_BY_MESSAGE_PROVIDER: Readonly<Record<string, readonly string[]>> = { | ||
| node: ["canvas", "image", "pdf", "tts", "web_fetch", "web_search"], |
There was a problem hiding this comment.
Keep memory-flush read/write tools for node sessions
The new node allowlist excludes read/write, but memory-flush turns are narrowed to exactly those two tools before message-provider filtering (toolsForMemoryFlush in src/agents/pi-tools.ts). Because node runs always pass messageChannel: "node" from src/gateway/server-node-events.ts, the subsequent allowlist filter removes the flush tools and leaves memory-flush runs with no writable path, so node sessions can silently skip persistence of compaction memory even when flush logic is triggered.
Useful? React with 👍 / 👎.
* Gateway: harden node event trust boundaries * Gateway: preserve trusted summary prefixes * Gateway: prefix multiline channel summaries
* Gateway: harden node event trust boundaries * Gateway: preserve trusted summary prefixes * Gateway: prefix multiline channel summaries
Summary
Changes
System (untrusted):linesnotifications.changedtitle/text before compacting and enqueueing the summaryValidation
pnpm test -- src/gateway/server-node-events.test.ts src/infra/system-events.test.ts src/agents/pi-tools.whatsapp-login-gating.test.tspnpm checkpnpm buildclaude -p "/review"and addressed the cleanup feedbackNotes