langgenius / langgenius/dify

isPrivateOrLocalAddress() never matches IPv6 addresses — URL.hostname returns "[::1]", not "::1"

Open
#41,865 1 comment 1 reaction 0 assignees View on GitHub
🐞 bug
Dominant language
TypeScript
Stars
156k
Forks
24.6k
Avg merge
20h 50m
Merged PRs (30d)
586

Description

### Self Checks

- [x] I have read the [Contributing Guide](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md) and [Language Policy](https://github.com/langgenius/dify/issues/1542).
- [x] This is only for bug report, if you would like to ask a question, please head to [Discussions](https://github.com/langgenius/dify/discussions/categories/general).
- [x] I have searched for existing issues [search for existing issues](https://github.com/langgenius/dify/issues), including closed ones.
- [x] I confirm that I am using English to submit this report, otherwise it will be closed.
- [x] 【中文用户 & Non English User】请使用英语提交,否则会被关闭 :)
- [x] Please do not modify this template :) and fill in all the required fields.

### Dify version

1.17.0

### Cloud or Self Hosted

Self Hosted (Docker)

### Steps to reproduce

`isPrivateOrLocalAddress()` in `web/utils/urlValidation.ts` never detects IPv6 addresses. The
check on line 33 compares `hostname` against the bare string `'::1'`:

```ts
// web/utils/urlValidation.ts:33
if (hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '::1') return true
```

That comparison is unreachable. The WHATWG URL parser **keeps the square brackets** in
`URL.hostname` for IPv6 literals. This can be confirmed in any browser console, no Dify
checkout required:

```js
new URL('http://[::1]:8080/x').hostname
// → "[::1]" — never equal to "::1", so the check can never fire
```

Behaviour of the current function, for each input:

| URL | `URL.hostname` | Returns | Expected |
|---|---|---|---|
| `http://127.0.0.1` | `127.0.0.1` | `true` | `true` |
| `http://192.168.1.1` | `192.168.1.1` | `true` | `true` |
| `http://[::1]:8080/x` | `[::1]` | **`false`** | `true` |
| `http://[fd00::1]/x` | `[fd00::1]` | **`false`** | `true` |
| `http://127.0.0.2` | `127.0.0.2` | **`false`** | `true` |
| `http://0.0.0.0` | `0.0.0.0` | **`false`** | `true` |

For maintainers, this can be reproduced as a unit test by appending to
`web/utils/urlValidation.spec.ts` — all four cases fail:

```ts
import { isPrivateOrLocalAddress } from './urlValidation'

describe('isPrivateOrLocalAddress', () => {
it.each([
['http://[::1]:8080/x', true], // IPv6 loopback
['http://[fd00::1]/x', true], // IPv6 unique local
['http://127.0.0.2', true], // 127.0.0.0/8 beyond .1
['http://0.0.0.0', true], // unspecified address
])('flags %s as private/local', (url, expected) => {
expect(isPrivateOrLocalAddress(url)).toBe(expected)
})
})
```

The two call sites are:

- `web/app/components/workflow/nodes/trigger-webhook/panel.tsx:199` —
the `debugUrlPrivateAddressWarning` hint on the Webhook Trigger node
- `web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/use-common-modal-state.helpers.ts:171` —
the `callbackUrl.privateAddressWarning` on plugin subscription callback URLs

Both receive a server-generated URL derived from the deployment's configured base URL, so this
surfaces on self-hosted instances reachable at an IPv6 literal (e.g. `http://[::1]:5001`) or at a
`127.0.0.0/8` address other than `127.0.0.1`: the warning that should appear is silently absent.

> Note: this is **not** an SSRF report. `isPrivateOrLocalAddress` only drives a client-side
> advisory label; the server-side SSRF protection is separate and unaffected.

### ✔️ Expected Behavior

`isPrivateOrLocalAddress()` returns `true` for IPv6 loopback (`[::1]`) and IPv6 unique-local
(`fd00::/8`) hosts, for the whole `127.0.0.0/8` range, and for `0.0.0.0` — so the "this looks like
a private or local address" warning is shown for those URLs, as it already is for `127.0.0.1`,
`10.0.0.0/8`, `172.16.0.0/12` and `192.168.0.0/16`.

### ❌ Actual Behavior

It returns `false` for all of them, and no warning is rendered. The `'::1'` comparison on line 33
is unreachable, because `URL.hostname` yields `'[::1]'` for IPv6 literals.

A minimal fix is to strip the surrounding brackets before the comparison and extend the loopback
check to the full `127.0.0.0/8` range plus `0.0.0.0`. I'm happy to open a PR for this.

Contributor guide

Open the contributing guide

Research direction

Start in web/utils/urlValidation.ts and inspect the existing isPrivateOrLocalAddress checks, then review web/utils/urlValidation.spec.ts. Add coverage for the IPv6, 127.0.0.0/8, and 0.0.0.0 cases described in the issue, and verify the warning call sites continue to receive the expected result.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
frontend, web-dev
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
84/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.