isPrivateOrLocalAddress() never matches IPv6 addresses — URL.hostname returns "[::1]", not "::1"
- 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
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