VYPR
High severity7.7NVD Advisory· Published May 18, 2026· Updated May 19, 2026

CVE-2026-41948

CVE-2026-41948

Description

Dify version 1.14.1 and prior contain a path traversal vulnerability that allows authenticated users to manipulate requests forwarded to the Plugin Daemon's internal REST API by exploiting insufficient URL path sanitization. Attackers can traverse out of their authorized tenant path using unencoded dot sequences in task identifiers or manipulated filename parameters to access internal endpoints such as debug interfaces, requiring only knowledge of the victim tenant's UUID. NOTE: Dify Cloud allows unauthenticated free self-registration, making account creation trivially accessible to any attacker.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

CVE-2026-41948 is a path traversal vulnerability in Dify ≤1.14.1 allowing authenticated users to access arbitrary Plugin Daemon internal endpoints via manipulated URL paths.

Vulnerability

CVE-2026-41948 is a path traversal vulnerability in Dify version 1.14.1 and prior [1][2]. The flaw exists in BasePluginClient._prepare_request, where caller-supplied path segments are joined to the Plugin Daemon base URL without sufficient sanitization [1]. By injecting unencoded dot sequences (..) or URL-encoded equivalents (%2e%2e) into task identifiers or filename parameters, an authenticated attacker can escape the intended inner-api namespace and reach arbitrary internal endpoints of the Plugin Daemon (a Go service) [1][2]. The internal API is not exposed directly to users, but the forwarding logic trusts the incoming path without proper validation [1].

Exploitation

An attacker must have an authenticated Dify session; Dify Cloud permits unauthenticated free self-registration, making account creation trivially accessible [2]. The attacker also needs knowledge of the victim tenant's UUID [2]. The exploitation sequence involves sending a crafted request to the Dify backend with a manipulated path segment (e.g., ../debug or %2e%2e/debug) in a task identifier or filename parameter. The backend forwards this request to the Plugin Daemon, which interprets the malicious path as targeting an internal endpoint such as a debug interface [2]. No additional user interaction is required beyond the attacker's own authenticated request [2].

Impact

A successful attack enables an authenticated user to access internal Plugin Daemon endpoints that are not intended for user-facing access [2]. This can lead to information disclosure, including debug information and other internal data, potentially compromising the confidentiality of the system [2]. The attacker does not gain direct RCE or write access from the path traversal alone, but the exposure of debug interfaces may reveal further attack surface [2].

Mitigation

The fix was merged in pull request #35796 on the Dify GitHub repository [1]. The patch adds a decode-and-check at the controller boundary before constructing the URL, rejecting the request with a ValueError if any segment after URL decoding equals .. [1]. Users should upgrade to a version containing this patch (post-v1.14.1) as soon as it is officially released [1][2]. No workaround is documented for unpatched versions [2]. The vulnerability is not listed in CISA's Known Exploited Vulnerabilities (KEV) catalog at the time of publication.

AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

2
  • Langgenius/Difyreferences2 versions
    (expand)+ 1 more
    • (no CPE)
    • (no CPE)range: <=1.14.1

Patches

4
2a8c3e34fcf1

Merge a688ad31a411dfd84a1ebacdb0d26eefbc5a7d77 into f04d80942606486c42d4832e928b19540baa98d2

https://github.com/langgenius/difyTim RenMay 20, 2026via nvd-ref
1 file changed · +4 0
  • api/core/plugin/impl/base.py+4 0 modified
    @@ -3,6 +3,7 @@
     import logging
     from collections.abc import Callable, Generator
     from typing import Any, cast
    +from urllib.parse import unquote
     
     import httpx
     from pydantic import BaseModel
    @@ -103,6 +104,9 @@ def _prepare_request(
             params: dict[str, Any] | None,
             files: dict[str, Any] | None,
         ) -> tuple[str, dict[str, str], bytes | dict[str, Any] | str | None, dict[str, Any] | None, dict[str, Any] | None]:
    +        decoded_path = unquote(path)
    +        if any(seg.lower() in ("..", "%2e%2e") for seg in decoded_path.split("/")):
    +            raise ValueError(f"Invalid plugin daemon path: traversal sequence detected in {path!r}")
             url = plugin_daemon_inner_api_baseurl / path
             prepared_headers = dict(headers or {})
             prepared_headers["X-Api-Key"] = dify_config.PLUGIN_DAEMON_KEY
    
307bd6e8cef6

Merge a688ad31a411dfd84a1ebacdb0d26eefbc5a7d77 into 05408af8a14c84d5becfae8158cd7a7031dd65d7

https://github.com/langgenius/difyTim RenMay 19, 2026via nvd-ref
1 file changed · +4 0
  • api/core/plugin/impl/base.py+4 0 modified
    @@ -3,6 +3,7 @@
     import logging
     from collections.abc import Callable, Generator
     from typing import Any, cast
    +from urllib.parse import unquote
     
     import httpx
     from pydantic import BaseModel
    @@ -103,6 +104,9 @@ def _prepare_request(
             params: dict[str, Any] | None,
             files: dict[str, Any] | None,
         ) -> tuple[str, dict[str, str], bytes | dict[str, Any] | str | None, dict[str, Any] | None, dict[str, Any] | None]:
    +        decoded_path = unquote(path)
    +        if any(seg.lower() in ("..", "%2e%2e") for seg in decoded_path.split("/")):
    +            raise ValueError(f"Invalid plugin daemon path: traversal sequence detected in {path!r}")
             url = plugin_daemon_inner_api_baseurl / path
             prepared_headers = dict(headers or {})
             prepared_headers["X-Api-Key"] = dify_config.PLUGIN_DAEMON_KEY
    
f4b54a2b9eb5

Merge a688ad31a411dfd84a1ebacdb0d26eefbc5a7d77 into b0a3399774c6f32cd956460ad6beeb0d169b9071

https://github.com/langgenius/difyTim RenMay 19, 2026via nvd-ref
1 file changed · +4 0
  • api/core/plugin/impl/base.py+4 0 modified
    @@ -3,6 +3,7 @@
     import logging
     from collections.abc import Callable, Generator
     from typing import Any, cast
    +from urllib.parse import unquote
     
     import httpx
     from pydantic import BaseModel
    @@ -103,6 +104,9 @@ def _prepare_request(
             params: dict[str, Any] | None,
             files: dict[str, Any] | None,
         ) -> tuple[str, dict[str, str], bytes | dict[str, Any] | str | None, dict[str, Any] | None, dict[str, Any] | None]:
    +        decoded_path = unquote(path)
    +        if any(seg.lower() in ("..", "%2e%2e") for seg in decoded_path.split("/")):
    +            raise ValueError(f"Invalid plugin daemon path: traversal sequence detected in {path!r}")
             url = plugin_daemon_inner_api_baseurl / path
             prepared_headers = dict(headers or {})
             prepared_headers["X-Api-Key"] = dify_config.PLUGIN_DAEMON_KEY
    
caca4825839b

Merge a688ad31a411dfd84a1ebacdb0d26eefbc5a7d77 into 2d5186fb28f26e783c9e27a3589349ffc0b41c41

https://github.com/langgenius/difyTim RenMay 18, 2026via nvd-ref
1 file changed · +4 0
  • api/core/plugin/impl/base.py+4 0 modified
    @@ -3,6 +3,7 @@
     import logging
     from collections.abc import Callable, Generator
     from typing import Any, cast
    +from urllib.parse import unquote
     
     import httpx
     from pydantic import BaseModel
    @@ -103,6 +104,9 @@ def _prepare_request(
             params: dict[str, Any] | None,
             files: dict[str, Any] | None,
         ) -> tuple[str, dict[str, str], bytes | dict[str, Any] | str | None, dict[str, Any] | None, dict[str, Any] | None]:
    +        decoded_path = unquote(path)
    +        if any(seg.lower() in ("..", "%2e%2e") for seg in decoded_path.split("/")):
    +            raise ValueError(f"Invalid plugin daemon path: traversal sequence detected in {path!r}")
             url = plugin_daemon_inner_api_baseurl / path
             prepared_headers = dict(headers or {})
             prepared_headers["X-Api-Key"] = dify_config.PLUGIN_DAEMON_KEY
    

Vulnerability mechanics

Root cause

"Insufficient sanitization of URL path components allows relative path traversal sequences to reach internal Plugin Daemon REST API endpoints outside the authorized tenant scope."

Attack vector

An authenticated attacker sends crafted HTTP requests to the Dify API where the path or filename parameter contains unencoded or double-encoded dot-dot sequences (e.g., `..` or `%2e%2e`). Because the application forwards these paths to the Plugin Daemon's internal REST API without validating traversal sequences, the attacker can escape the intended tenant-specific sub-path. The attacker only needs to know the victim tenant's UUID; on Dify Cloud, unauthenticated self-registration is freely available, making account acquisition trivial. The bug is reachable via task identifiers or manipulated filename parameters that are incorporated into the forwarded URL path [CWE-23].

Affected code

The vulnerability resides in `api/core/plugin/impl/base.py` within the `_prepare_request` method. This method constructs a URL by concatenating a base URL with a user-influenced `path` parameter and forwards the request to the Plugin Daemon's internal REST API. No sanitization of dot-dot sequences existed prior to the patch [patch_id=424435].

What the fix does

The patch adds a URL-decoding step via `urllib.parse.unquote` on the incoming path before checking for traversal sequences. It then rejects the request if any path segment (after splitting on `/`) equals `..` or `%2e%2e` (case-insensitive). This closes the vulnerability by catching both literal dot-dot sequences and their URL-encoded variants that were previously passed through to the Plugin Daemon unscrutinized [patch_id=424435].

Preconditions

  • authAttacker must be authenticated (on Dify Cloud, unauthenticated self-registration is freely available)
  • inputAttacker must know the victim tenant's UUID
  • networkAttacker must be able to send crafted HTTP requests to the Dify API

Generated on May 19, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

3

News mentions

0

No linked articles in our index yet.