PYSEC-2026-3603

See a problem?
Import Source
https://github.com/pypa/advisory-database/blob/main/vulns/open-webui/PYSEC-2026-3603.yaml
JSON Data
https://api.osv.dev/v1/vulns/PYSEC-2026-3603
Aliases
Published
2026-08-04T11:34:43.226981Z
Modified
2026-08-04T14:30:28.965245431Z
Severity
  • 4.3 (Medium) CVSS_V3 - CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N CVSS Calculator
Summary
Open WebUI: `WEB_FETCH_FILTER_LIST` host allow/block filter bypassable via URL path and non-label-boundary matching
Details

Summary

The administrator-configured WEB_FETCH_FILTER_LIST (the allow/block list applied to server-side web fetches: RAG URL ingestion, URL-to-markdown, web-search content fetch) matches hostnames incorrectly, so the filter can be bypassed.

Details

is_string_allowed (backend/open_webui/utils/misc.py) matches with str.endswith(...), and the primary web-fetch call site (backend/open_webui/retrieval/web/utils.py) called it with the full URL string, not the hostname:

  • Blocklist bypass via path. A blocklist entry !internal.example.com only matches a URL that ends with that string. Any URL with a path (https://internal.example.com/x) ends with /x, so the entry never matches and the fetch proceeds. The blocklist effectively only stopped path-less URLs.
  • Allowlist false-reject and bypass. An allowlist company.com rejected the legitimate https://api.company.com/status and admitted https://attacker.example/path/company.com.
  • Non-label-boundary matching at the hostname-shaped call site (retrieval/web/main.py): endswith('corp.com') also matched evilcorp.com, and 10.0.0.1 matched 110.0.0.1.

Impact

An authenticated user able to trigger a server-side web fetch can reach hosts the administrator intended to block with WEB_FETCH_FILTER_LIST.

Open WebUI's primary SSRF protection is a separate, always-on guard that rejects any URL resolving to a non-global IP (validate_url and the connection-layer _ssrf_safe_new_conn, active whenever ENABLE_RAG_LOCAL_WEB_FETCH is off, the default). That guard is unaffected by this issue and continues to block loopback, RFC1918 and link-local addresses, including the 169.254.169.254 cloud-metadata endpoint. This bypass therefore does not grant access to those internal targets. What it defeats is the administrator's ability to block specific publicly-resolvable hosts (internal services reachable from the server over a public IP, e.g. split-horizon DNS or internal PaaS endpoints) and to enforce an allowlist. Fetched content is returned to the requester, so for hosts reachable from the server's network position this is a read/content-disclosure SSRF against the admin-blocked host.

Patch

Matching is now performed on the parsed hostname using DNS label boundaries. A dedicated is_host_allowed(host, ...) matches an entry only when host and entry are equal or the entry is a parent domain (host == entry or host.endswith('.' + entry)), so corp.com matches api.corp.com but not evilcorp.com, and IP entries match only the identical address. Both web-fetch call sites pass the parsed hostname rather than the full URL. The generic is_string_allowed is retained unchanged for unrelated non-host filters.

Credit

Reported by @addcontent.

References

Affected packages

PyPI / open-webui

Package

Affected ranges

Type
ECOSYSTEM
Events
Introduced
0Unknown introduced version / All previous versions are affected
Fixed
0.10.0

Affected versions

0.*
0.1.124
0.1.125
0.2.0
0.2.1
0.2.2
0.2.3
0.2.4
0.2.5
0.3.0
0.3.1
0.3.2
0.3.3
0.3.4
0.3.5
0.3.6
0.3.7
0.3.8
0.3.9
0.3.10
0.3.12
0.3.13
0.3.14
0.3.15
0.3.16
0.3.17.dev2
0.3.17.dev3
0.3.17.dev4
0.3.17.dev5
0.3.17
0.3.18
0.3.19
0.3.20
0.3.21
0.3.22
0.3.23
0.3.24
0.3.25
0.3.26
0.3.27.dev1
0.3.27.dev2
0.3.27.dev3
0.3.27
0.3.28
0.3.29
0.3.30.dev1
0.3.30.dev2
0.3.30
0.3.31.dev1
0.3.31
0.3.32
0.3.33.dev1
0.3.33
0.3.34
0.3.35
0.4.0.dev1
0.4.0.dev2
0.4.0
0.4.1
0.4.2
0.4.3
0.4.4
0.4.5
0.4.6.dev1
0.4.6
0.4.7
0.4.8
0.5.0.dev1
0.5.0.dev2
0.5.0
0.5.1
0.5.2
0.5.3.dev1
0.5.3
0.5.4
0.5.5
0.5.6
0.5.7
0.5.8
0.5.9
0.5.10
0.5.11
0.5.12
0.5.13
0.5.14
0.5.15
0.5.16
0.5.17
0.5.18
0.5.19
0.5.20
0.6.0
0.6.1
0.6.2
0.6.3
0.6.4
0.6.5
0.6.6.dev1
0.6.6
0.6.7
0.6.8
0.6.9
0.6.10
0.6.11
0.6.12
0.6.13
0.6.14
0.6.15
0.6.16
0.6.18
0.6.19
0.6.20
0.6.21
0.6.22
0.6.23
0.6.24
0.6.25
0.6.26.dev1
0.6.26
0.6.27
0.6.28
0.6.29
0.6.30
0.6.31
0.6.32
0.6.33
0.6.34
0.6.35
0.6.36
0.6.37
0.6.38
0.6.39
0.6.40
0.6.41
0.6.42
0.6.43
0.7.0
0.7.1
0.7.2
0.8.0
0.8.1
0.8.2
0.8.3
0.8.4
0.8.5
0.8.6
0.8.7
0.8.8
0.8.9
0.8.10
0.8.11
0.8.12
0.9.0
0.9.1
0.9.2
0.9.3
0.9.4
0.9.5
0.9.6

Database specific

source
"https://github.com/pypa/advisory-database/blob/main/vulns/open-webui/PYSEC-2026-3603.yaml"