Skip to content

Gateway: harden node event trust boundaries#57691

Merged
jacobtomlinson merged 3 commits intoopenclaw:mainfrom
jacobtomlinson:fix/fix-103
Mar 30, 2026
Merged

Gateway: harden node event trust boundaries#57691
jacobtomlinson merged 3 commits intoopenclaw:mainfrom
jacobtomlinson:fix/fix-103

Conversation

@jacobtomlinson
Copy link
Copy Markdown
Contributor

Summary

  • restrict node-originated agent runs to a small safe tool subset
  • mark notification-derived system events as untrusted and sanitize notification text before enqueueing
  • add regression coverage for node tool gating and untrusted system event formatting

Changes

  • added a node-specific message-provider allowlist so node-triggered runs cannot access host, session, or outbound mutation tools
  • extended queued system events with a trust bit and render untrusted events as System (untrusted): lines
  • sanitized notifications.changed title/text before compacting and enqueueing the summary

Validation

  • ran pnpm test -- src/gateway/server-node-events.test.ts src/infra/system-events.test.ts src/agents/pi-tools.whatsapp-login-gating.test.ts
  • ran pnpm check
  • ran pnpm build
  • ran local agentic review with claude -p "/review" and addressed the cleanup feedback

Notes

  • residual risk: mobile share and canvas flows now run with the reduced node-safe tool subset; I did not run device E2E in this workspace

@aisle-research-bot
Copy link
Copy Markdown

aisle-research-bot bot commented Mar 30, 2026

🤖 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. ⌛
We'll post the results here as soon as they're ready.

Progress:

  • Analysis
  • Finalization

Latest run failed. Keeping previous successful results. Trace ID: 019d3ebe00c79641990b003376a0ce38.

Last updated on: 2026-03-30T15:55:42Z

Latest run failed. Keeping previous successful results. Trace ID: 019d3ec796bd5b69ddb3c8e6f91c05ee.

Last updated on: 2026-03-30T16:40:47Z

@openclaw-barnacle openclaw-barnacle bot added gateway Gateway runtime agents Agent runtime and tooling size: S maintainer Maintainer-authored PR labels Mar 30, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 30, 2026

Greptile Summary

This PR hardens the gateway's trust boundaries across three vectors: node-originated agent runs are restricted to a small allowlist of safe tools (canvas, image, pdf, tts, web_fetch, web_search); all notifications.changed system events are marked trusted: false and notification title/text is sanitized with sanitizeInboundSystemTags before embedding; and drainFormattedSystemEvents is refactored so each event carries its own System: / System (untrusted): prefix rather than having the prefix applied uniformly at return time.

Issues found:

  • Regression in channel summary formatting (P1): In the old code the System: prefix was applied at the final return to all systemLines, including lines unshifted from buildChannelSummary. In the new code the prefix is applied per-event during the flatMap, so channel summary lines (e.g. WhatsApp: linked +1234567890) are joined into the output with no prefix. On new main sessions the model loses the System: attribution that marks these lines as trusted, gateway-originated content. The fix is to wrap summary lines with System: before inserting them into systemLines.

  • packageName / key not sanitized before embedding in summary (P2): title and text are sanitized via sanitizeInboundSystemTags, but packageName and key from the node payload are embedded verbatim. The trusted: false marking on the whole event mitigates the risk (the model sees System (untrusted): regardless), but consistent sanitization across all node-supplied fields would be more robust.

Confidence Score: 4/5

Mostly 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.

Important Files Changed

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)

  1. src/gateway/server-node-events.ts, line 483-491 (link)

    P2 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.

    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

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

@jacobtomlinson jacobtomlinson merged commit a77928b into openclaw:main Mar 30, 2026
8 checks passed
@jacobtomlinson jacobtomlinson deleted the fix/fix-103 branch March 30, 2026 13:22
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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"],
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

pgondhi987 pushed a commit to pgondhi987/openclaw that referenced this pull request Mar 31, 2026
* Gateway: harden node event trust boundaries

* Gateway: preserve trusted summary prefixes

* Gateway: prefix multiline channel summaries
pgondhi987 pushed a commit to pgondhi987/openclaw that referenced this pull request Mar 31, 2026
* Gateway: harden node event trust boundaries

* Gateway: preserve trusted summary prefixes

* Gateway: prefix multiline channel summaries
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling gateway Gateway runtime maintainer Maintainer-authored PR size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant