Skip to content

fix(synology-chat): add SSRF guard to sendFileUrl [AI-assisted]#69603

Open
pgondhi987 wants to merge 26 commits intoopenclaw:mainfrom
pgondhi987:fix/fix-476
Open

fix(synology-chat): add SSRF guard to sendFileUrl [AI-assisted]#69603
pgondhi987 wants to merge 26 commits intoopenclaw:mainfrom
pgondhi987:fix/fix-476

Conversation

@pgondhi987
Copy link
Copy Markdown
Contributor

Summary

  • Problem: sendFileUrl in the Synology Chat extension passed the fileUrl parameter directly to the NAS incoming webhook as file_url with no URL validation, protocol restriction, or private-IP check. The NAS then fetches that URL from its network position, enabling SSRF against the NAS's internal network (cloud metadata endpoints, RFC1918 hosts, loopback services).
  • Why it matters: Synology NAS devices are typically deployed on internal networks, giving the SSRF access to resources unreachable from the internet. The same vulnerability class was previously fixed in the QQBot extension.
  • What changed: Added isSafeWebhookFileUrl() guard in extensions/synology-chat/src/client.ts that rejects non-http/https protocols, empty hostnames, private/loopback IP ranges (via the shared isPrivateOrLoopbackHost helper from openclaw/plugin-sdk/ssrf-runtime), and the unspecified addresses 0.0.0.0 / [::] which are not covered by the shared helper. sendFileUrl returns false immediately for any URL that fails the guard.
  • What did NOT change: sendMessage, fetchChatUsers, resolveLegacyWebhookNameToChatUserId, and the incomingUrl (NAS webhook) code path are untouched. No behavior change for legitimate public file URLs.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • This PR fixes a bug or regression

Root Cause (if applicable)

  • Root cause: sendFileUrl was added without applying the SSRF guard pattern used elsewhere in the codebase for media/file URL forwarding.
  • Missing detection / guardrail: No URL validation before constructing the file_url webhook payload.
  • Contributing context (if known): The same fix was applied to QQBot media paths but the Synology Chat extension was not included in those changes.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file: extensions/synology-chat/src/client.test.ts
  • Scenario the test should lock in: sendFileUrl returns false and never calls https.request for loopback IPs, localhost, private-network IPs, 0.0.0.0, [::], non-http schemes, and malformed URLs.
  • Why this is the smallest reliable guardrail: The guard is a pure synchronous function; unit tests at the sendFileUrl boundary fully cover the decision logic.
  • Existing test that already covers this (if any): None — new tests added.
  • If no new test is added, why not: N/A

New test cases added:

  • Blocks http://127.0.0.1:8080/... (IPv4 loopback)
  • Blocks http://localhost/... (loopback hostname)
  • Blocks http://192.168.1.10/... (RFC1918 private)
  • Blocks http://0.0.0.0/... (unspecified address)
  • Blocks http://[::]/... (IPv6 unspecified, bracketed form)
  • Blocks file:///etc/passwd (non-http scheme)
  • Blocks not a url at all (malformed URL)

User-visible / Behavior Changes

Attempts to send a file URL pointing to a private/loopback/unspecified address or a non-http(s) URL via the Synology Chat extension will silently return false (no message sent) instead of forwarding the URL to the NAS. Legitimate public file URLs are unaffected.

Diagram (if applicable)

Before:
sendFileUrl(incomingUrl, fileUrl) -> buildWebhookBody({file_url: fileUrl}) -> doPost(NAS)

After:
sendFileUrl(incomingUrl, fileUrl) -> isSafeWebhookFileUrl(fileUrl) -[fail]-> return false
                                                                   -[pass]-> buildWebhookBody({file_url: fileUrl}) -> doPost(NAS)

Security Impact (required)

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? No — outbound calls are now blocked for unsafe URLs rather than forwarded
  • Command/tool execution surface changed? No
  • Data access scope changed? No
  • Risk + mitigation: The guard is additive (fail-closed). It cannot be bypassed via URL encoding because new URL() normalizes before the hostname check. IPv6 bracket stripping is explicit to handle WHATWG URL parser behavior.

Repro + Verification

Environment

  • OS: Linux (CI)
  • Runtime/container: Node 22
  • Integration/channel: Synology Chat extension
  • Relevant config: N/A

Steps

  1. Call sendFileUrl with a private/loopback/unspecified/non-http file URL
  2. Observe that the function returns false without making any network call

Expected

  • sendFileUrl returns false
  • https.request is not called

Actual (after fix)

  • All blocked-URL test cases pass

Evidence

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Seven new unit tests were added covering the full blocked-URL surface. All pass.

Human Verification (required)

Note: This PR was generated by AI (Codex) and reviewed by an AI reviewer (Claude). No human manually verified the runtime behavior.

  • Verified scenarios: Unit test coverage for all blocked URL classes; confirmed isBlockedSpecialIpLiteral correctly strips IPv6 brackets before net.isIP check (runtime-verified via Node.js REPL during review).
  • Edge cases checked: [::] bracketed IPv6 unspecified (WHATWG URL parser returns "[::]", not "::"); 0.0.0.0 unspecified not covered by shared helper; localhost hostname; malformed URL parse failure path.
  • What you did not verify: Full end-to-end test with a live Synology NAS; behavior of the NAS itself when receiving blocked requests.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? Yes — only affects invalid/unsafe fileUrl inputs that previously forwarded unchecked
  • Config/env changes? No
  • Migration needed? No

Risks and Mitigations

  • Risk: A legitimate fileUrl that resolves via internal DNS to a public IP is blocked at the hostname level (no DNS resolution at guard time).
    • Mitigation: This is the standard tradeoff for synchronous SSRF guards; DNS-rebinding protection requires network-level egress filtering. The guard is consistent with how other extensions handle this.

@openclaw-barnacle openclaw-barnacle Bot added size: S maintainer Maintainer-authored PR labels Apr 21, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 21, 2026

Greptile Summary

This PR adds a isSafeWebhookFileUrl() guard to sendFileUrl in the Synology Chat extension, blocking non-http(s) schemes, empty hostnames, private/loopback IPs (via the shared isPrivateOrLoopbackHost helper), and unspecified addresses (0.0.0.0 / [::]) that the shared helper explicitly excludes. The implementation is correct and consistent with the SSRF guard pattern used elsewhere in the codebase.

Confidence Score: 5/5

Safe to merge — the SSRF guard is correct, fail-closed, and consistent with the codebase pattern; only a minor missing test for IPv6 loopback remains.

The isBlockedSpecialIpLiteral function correctly strips IPv6 brackets before the net.isIP check, and properly handles [::] / 0.0.0.0 which isPrivateOrLoopbackHost intentionally excludes. The WHATWG URL normalizer handles obfuscated IPv4 forms (octal, hex) before the guard runs. The ordering (special-IP check before the shared helper) is logically necessary. All P0/P1 scenarios are covered; the only remaining item is a P2 suggestion to add an explicit [::1] loopback test.

No files require special attention.

Prompt To Fix All With AI
This is a comment left during a code review.
Path: extensions/synology-chat/src/client.test.ts
Line: 192-214

Comment:
**Missing IPv6 loopback test case**

The suite tests `[::]` (unspecified) but not `[::1]` (loopback). `isPrivateOrLoopbackHost` in `gateway/net.ts` does handle bracketed IPv6 correctly via `parseHostForAddressChecks`, so `[::1]` is blocked, but an explicit test would lock in the integration between the guard and the shared helper for this common loopback form.

```suggestion
  it("blocks IPv6 unspecified address file URLs before reaching the NAS webhook", async () => {
    const result = await settleTimers(
      sendFileUrl("https://nas.example.com/incoming", "http://[::]/api/internal"),
    );
    expect(result).toBe(false);
    expect(vi.mocked(https.request)).not.toHaveBeenCalled();
  });

  it("blocks IPv6 loopback file URLs before reaching the NAS webhook", async () => {
    const result = await settleTimers(
      sendFileUrl("https://nas.example.com/incoming", "http://[::1]/api/internal"),
    );
    expect(result).toBe(false);
    expect(vi.mocked(https.request)).not.toHaveBeenCalled();
  });

  it("blocks non-http file URLs before reaching the NAS webhook", async () => {
    const result = await settleTimers(
      sendFileUrl("https://nas.example.com/incoming", "file:///etc/passwd"),
    );
    expect(result).toBe(false);
    expect(vi.mocked(https.request)).not.toHaveBeenCalled();
  });

  it("blocks malformed file URLs before reaching the NAS webhook", async () => {
    const result = await settleTimers(
      sendFileUrl("https://nas.example.com/incoming", "not a url at all"),
    );
    expect(result).toBe(false);
    expect(vi.mocked(https.request)).not.toHaveBeenCalled();
  });
```

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix: address review feedback" | Re-trigger Greptile

Comment thread extensions/synology-chat/src/client.test.ts
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: 58c3e6a8fa

ℹ️ 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".

Comment thread extensions/synology-chat/src/client.ts Outdated
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: 256659e2b4

ℹ️ 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".

Comment thread extensions/synology-chat/src/client.ts Outdated
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: 193daea801

ℹ️ 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".

Comment thread extensions/synology-chat/src/client.ts Outdated
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: e673af9687

ℹ️ 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".

Comment thread extensions/synology-chat/src/media-proxy.ts
@openclaw-barnacle openclaw-barnacle Bot added the docs Improvements or additions to documentation label Apr 21, 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: 6bd47b228a

ℹ️ 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".

Comment thread extensions/synology-chat/src/setup-surface.ts
Comment thread extensions/synology-chat/src/setup-surface.ts
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: 1dc6fb55e1

ℹ️ 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".

Comment thread extensions/synology-chat/src/setup-surface.ts
Comment thread extensions/synology-chat/src/media-proxy.ts Outdated
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: e9b8fff306

ℹ️ 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".

Comment thread extensions/synology-chat/src/media-proxy.ts Outdated
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: 0ba29ea320

ℹ️ 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".

Comment thread extensions/synology-chat/src/media-proxy.ts Outdated
Comment thread extensions/synology-chat/src/media-proxy.ts
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: 29856e5c8b

ℹ️ 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".

Comment thread extensions/synology-chat/src/media-proxy.ts Outdated
Comment thread extensions/synology-chat/src/media-proxy.ts Outdated
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: a034145d44

ℹ️ 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".

Comment thread extensions/synology-chat/src/media-proxy.ts Outdated
@pgondhi987
Copy link
Copy Markdown
Contributor Author

@codex review

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: 270f14941d

ℹ️ 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".

Comment thread extensions/synology-chat/src/gateway-runtime.ts Outdated
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: 5f7d6abb4c

ℹ️ 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".

Comment thread extensions/synology-chat/src/media-proxy.ts Outdated
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: 46d3a3e2e5

ℹ️ 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".

Comment thread extensions/synology-chat/src/media-proxy.ts
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: 41f34c2c8a

ℹ️ 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".

Comment thread extensions/synology-chat/src/media-proxy.ts Outdated
@pgondhi987
Copy link
Copy Markdown
Contributor Author

@codex review

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: 5fe75814ae

ℹ️ 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".

Comment on lines +232 to +235
if (routeRegistrationFailed) {
unregisterWebhookRoute();
unregisterMediaRoute();
return () => {};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep webhook route active when media route conflicts

If the hosted-media prefix route fails to register (for example, a conflicting prefix already exists), routeRegistrationFailed causes both unregister callbacks to run, which tears down the already-registered webhook route too. That makes inbound Synology messages stop working even though only hosted-media setup failed; before this change, webhook handling could still continue. This should only disable hosted media (or log/degrade), not remove the primary webhook route.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Improvements or additions to documentation maintainer Maintainer-authored PR size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant