Every CVE Search/ Get API key

CVE-2026-113731

djust: Arbitrary absolute-path file read in the find_handlers_for_template MCP tool

3.3 LowFix available

djust provides Phoenix LiveView-style reactive server-side rendering for Django with Rust-powered performance. From 0.9.0 until 1.0.10, 1.1.7, 1.2.4, and 1.3.0rc8, the find_handlers_for_template MCP tool in python/djust/mcp/server.py accepts an existing absolute template_path and reads it without restricting the resolved path to the project or configured template directories. A local MCP client can use the tool as a file existence and readability oracle and can obtain substrings that match the tool's dj-click, dj-submit, dj-change, dj-input, dj-keydown, or dj-keyup attribute pattern from files readable by the djust process. The maintainer assessment states that the MCP server communicates over local stdio as the same operating-system user and has no network listener, and the tool does not return arbitrary raw file contents. This issue is fixed in versions 1.0.10, 1.1.7, 1.2.4, and 1.3.0rc8.

Attack profile

How easy it is to exploit

Attack vector
Local
Complexity
Low
Privileges needed
Low
User interaction
None

What an attacker can affect

Confidentiality
Low
Integrity
None
Availability
None

Exploit likelihood

0.19%Unchanged since Oct 21, 2026
90%50%10%1%

What each source reports

CVE List Score used 3.3

djust: Arbitrary absolute-path file read in the find_handlers_for_template MCP tool

djust provides Phoenix LiveView-style reactive server-side rendering for Django with Rust-powered performance. From 0.9.0 until 1.0.10, 1.1.7, 1.2.4, and 1.3.0rc8, the find_handlers_for_template MCP tool in python/djust/mcp/server.py accepts an existing absolute template_path and reads it without restricting the resolved path to the project or configured template directories. A local MCP client can use the tool as a file existence and readability oracle and can obtain substrings that match the tool's dj-click, dj-submit, dj-change, dj-input, dj-keydown, or dj-keyup attribute pattern from files readable by the djust process. The maintainer assessment states that the MCP server communicates over local stdio as the same operating-system user and has no network listener, and the tool does not return arbitrary raw file contents. This issue is fixed in versions 1.0.10, 1.1.7, 1.2.4, and 1.3.0rc8.

Record
CVE-2026-113731
CVSS 3.1
3.3 LowCVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N
Weakness
CWE-22
Updated
2026-11-02
EUVDEUVD-2026-106855 3.3

djust provides Phoenix LiveView-style reactive server-side rendering for Django with Rust-powered performance. From 0.9.0 until 1.0.10, 1.1.7, 1.2.4, and 1.3.0rc8, the find_handlers_for_template MCP tool in python/djust/mcp/server.py accepts an existing absolute template_path and reads it without restricting the resolved path to the project or configured template directories. A local MCP client can use the tool as a file existence and readability oracle and can obtain substrings that match the tool's dj-click, dj-submit, dj-change, dj-input, dj-keydown, or dj-keyup attribute pattern from files readable by the djust process. The maintainer assessment states that the MCP server communicates over local stdio as the same operating-system user and has no network listener, and the tool does not return arbitrary raw file contents. This issue is fixed in versions 1.0.10, 1.1.7, 1.2.4, and 1.3.0rc8.

Record
EUVD-2026-106855
CVSS 3.1
3.3 LowCVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N
Updated
2026-11-02
GitHub AdvisoriesGHSA-r57g-mc79-8hj2 3.3

djust: Arbitrary absolute-path file read in the find_handlers_for_template MCP tool

Affected version / file / line

Version: djust v1.1.4 (latest release, published 2026-09-23). File: python/djust/mcp/server.py:354-433 (MCP tool find_handlers_for_template). Confirmed identical on the commit used for triage, a7e4fafd1667b4db0b1465652d4a63cb17117a07.

Description

@mcp.tool()
def find_handlers_for_template(template_path: str) -> str:
    """
    Args:
        template_path: Either a logical template name (e.g.
            'demos/counter.html' — what you'd pass to `render`) OR
            an absolute filesystem path. Both are searched.
    """
    ...
    resolved_path: str | None = None
    if os.path.isabs(template_path) and os.path.isfile(template_path):
        resolved_path = template_path
    else:
        ...  # logical-name resolution via Django's template loader
    ...
    with open(resolved_path, "r", encoding="utf-8", errors="replace") as f:
        source = f.read()
    handler_names_in_template = sorted({m.group(2) for m in dj_event_attr_re.finditer(source)})

When template_path is an absolute path to an existing file, it is used directly, with no confinement to the project/template directories — no realpath() + prefix check, no allowlist. open(resolved_path, ...) then reads the file.

This is inconsistent with the sibling tool in the same file (loaddata, ~line 290-303), which explicitly rejects paths outside the project directory for its own path-like parameter:

candidate = os.path.realpath(...)
if base_dir and not candidate.startswith(os.path.realpath(str(base_dir))):
    return json.dumps({"error": f"fixture path '{p}' resolves outside the project directory", ...})

find_handlers_for_template has no equivalent check for template_path before the open() call.

Impact

An MCP client with access to this tool (available whenever the Django project is running the MCP server via python manage.py djust_mcp — no separate opt-in flag gates this specific tool beyond that) can read the contents of any file on disk that the process has read permission to and that exists, by passing an absolute path — e.g. /etc/passwd, application .env files, SSH keys, or any other sensitive file reachable by the server process's user.

Mitigating factor (reduces this from High to Medium): the tool does not return the raw file content. It runs one fixed regex (dj-(?:click|submit|change|input|keydown|keyup)\s*=\s*'"['"]) against the content and returns only the matched handler-name strings, plus confirmation that the file exists and is readable (an existence/readability oracle on its own, independent of the regex match). If the targeted file happens to contain text matching that HTML-attribute-like pattern (plausible for config files, logs, or any text file with key="value"-shaped content), those substrings are returned in full — so the read is not purely blind, but it is not a general-purpose arbitrary-file-read primitive either.

Proof of Concept / Reproduction Traced via direct source read; no live Django project was exercised for this report (would require a full djust project scaffold, which was out of scope for this pass — see limitation below).

  1. MCP client calls: find_handlers_for_template(template_path="/etc/passwd")
  2. server.py ~line 393:
       if os.path.isabs(template_path) and os.path.isfile(template_path):
           resolved_path = template_path
    
    resolved_path == "/etc/passwd", unchanged, unconfined
  3. server.py ~line 430:
       with open(resolved_path, "r", encoding="utf-8", errors="replace") as f:
           source = f.read()
    
    /etc/passwd is now fully read into source
  4. The regex dj_event_attr_re.finditer(source) is applied to the real /etc/passwd content. /etc/passwd will not match this specific pattern, so the response would report an empty handler list plus "file exists and was read" - which already confirms the path-confinement bypass (compare with passing a nonexistent absolute path, which correctly…
Record
GHSA-r57g-mc79-8hj2
CVSS 3.1
3.3 LowCVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N
Weakness
CWE-22
Updated
2026-11-02
NVD 3.3

djust provides Phoenix LiveView-style reactive server-side rendering for Django with Rust-powered performance. From 0.9.0 until 1.0.10, 1.1.7, 1.2.4, and 1.3.0rc8, the find_handlers_for_template MCP tool in python/djust/mcp/server.py accepts an existing absolute template_path and reads it without restricting the resolved path to the project or configured template directories. A local MCP client can use the tool as a file existence and readability oracle and can obtain substrings that match the tool's dj-click, dj-submit, dj-change, dj-input, dj-keydown, or dj-keyup attribute pattern from files readable by the djust process. The maintainer assessment states that the MCP server communicates over local stdio as the same operating-system user and has no network listener, and the tool does not return arbitrary raw file contents. This issue is fixed in versions 1.0.10, 1.1.7, 1.2.4, and 1.3.0rc8.

Record
CVE-2026-113731
CVSS 3.1
3.3 LowCVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N
Weakness
CWE-22
Updated
2026-11-02
OSV 3.3

djust: Arbitrary absolute-path file read in the find_handlers_for_template MCP tool

djust provides Phoenix LiveView-style reactive server-side rendering for Django with Rust-powered performance. From 0.9.0 until 1.0.10, 1.1.7, 1.2.4, and 1.3.0rc8, the find_handlers_for_template MCP tool in python/djust/mcp/server.py accepts an existing absolute template_path and reads it without restricting the resolved path to the project or configured template directories. A local MCP client can use the tool as a file existence and readability oracle and can obtain substrings that match the tool's dj-click, dj-submit, dj-change, dj-input, dj-keydown, or dj-keyup attribute pattern from files readable by the djust process. The maintainer assessment states that the MCP server communicates over local stdio as the same operating-system user and has no network listener, and the tool does not return arbitrary raw file contents. This issue is fixed in versions 1.0.10, 1.1.7, 1.2.4, and 1.3.0rc8.

Record
CVE-2026-113731
CVSS 3.1
3.3 LowCVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N
Weakness
CWE-22
Updated
2026-11-05
OSVGHSA-r57g-mc79-8hj2 3.3

djust: Arbitrary absolute-path file read in the find_handlers_for_template MCP tool

Affected version / file / line

Version: djust v1.1.4 (latest release, published 2026-09-23). File: python/djust/mcp/server.py:354-433 (MCP tool find_handlers_for_template). Confirmed identical on the commit used for triage, a7e4fafd1667b4db0b1465652d4a63cb17117a07.

Description

@mcp.tool()
def find_handlers_for_template(template_path: str) -> str:
    """
    Args:
        template_path: Either a logical template name (e.g.
            'demos/counter.html' — what you'd pass to `render`) OR
            an absolute filesystem path. Both are searched.
    """
    ...
    resolved_path: str | None = None
    if os.path.isabs(template_path) and os.path.isfile(template_path):
        resolved_path = template_path
    else:
        ...  # logical-name resolution via Django's template loader
    ...
    with open(resolved_path, "r", encoding="utf-8", errors="replace") as f:
        source = f.read()
    handler_names_in_template = sorted({m.group(2) for m in dj_event_attr_re.finditer(source)})

When template_path is an absolute path to an existing file, it is used directly, with no confinement to the project/template directories — no realpath() + prefix check, no allowlist. open(resolved_path, ...) then reads the file.

This is inconsistent with the sibling tool in the same file (loaddata, ~line 290-303), which explicitly rejects paths outside the project directory for its own path-like parameter:

candidate = os.path.realpath(...)
if base_dir and not candidate.startswith(os.path.realpath(str(base_dir))):
    return json.dumps({"error": f"fixture path '{p}' resolves outside the project directory", ...})

find_handlers_for_template has no equivalent check for template_path before the open() call.

Impact

An MCP client with access to this tool (available whenever the Django project is running the MCP server via python manage.py djust_mcp — no separate opt-in flag gates this specific tool beyond that) can read the contents of any file on disk that the process has read permission to and that exists, by passing an absolute path — e.g. /etc/passwd, application .env files, SSH keys, or any other sensitive file reachable by the server process's user.

Mitigating factor (reduces this from High to Medium): the tool does not return the raw file content. It runs one fixed regex (dj-(?:click|submit|change|input|keydown|keyup)\s*=\s*'"['"]) against the content and returns only the matched handler-name strings, plus confirmation that the file exists and is readable (an existence/readability oracle on its own, independent of the regex match). If the targeted file happens to contain text matching that HTML-attribute-like pattern (plausible for config files, logs, or any text file with key="value"-shaped content), those substrings are returned in full — so the read is not purely blind, but it is not a general-purpose arbitrary-file-read primitive either.

Proof of Concept / Reproduction Traced via direct source read; no live Django project was exercised for this report (would require a full djust project scaffold, which was out of scope for this pass — see limitation below).

  1. MCP client calls: find_handlers_for_template(template_path="/etc/passwd")
  2. server.py ~line 393:
       if os.path.isabs(template_path) and os.path.isfile(template_path):
           resolved_path = template_path
    
    resolved_path == "/etc/passwd", unchanged, unconfined
  3. server.py ~line 430:
       with open(resolved_path, "r", encoding="utf-8", errors="replace") as f:
           source = f.read()
    
    /etc/passwd is now fully read into source
  4. The regex dj_event_attr_re.finditer(source) is applied to the real /etc/passwd content. /etc/passwd will not match this specific pattern, so the response would report an empty handler list plus "file exists and was read" - which already confirms the path-confinement bypass (compare with passing a nonexistent absolute path, which correctly…
Record
GHSA-r57g-mc79-8hj2
CVSS 3.1
3.3 LowCVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N
Weakness
CWE-22
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
djust
PyPI
0.9.0 to 1.0.9
1.0.10
djust
PyPI
1.1.0 to 1.1.6
1.1.7
djust
PyPI
1.2.0.dev0 to 1.2.3
1.2.4
djust
PyPI
1.3.0.dev0 to 1.3.0rc7
1.3.0rc8
djust
PyPI
0.9.0 up to 1.0.10, plus listed versions
0.9.10.9.20.9.40.9.50.9.6
Show 12 moreShow less
0.9.71.0.01.0.0rc111.0.11.0.21.0.31.0.41.0.51.0.61.0.71.0.81.0.9
1.0.10
djust
PyPI
1.1.0 up to 1.1.7, plus listed versions
1.1.11.1.21.1.31.1.41.1.51.1.6
1.1.7
djust
PyPI
1.2.0.dev0 up to 1.2.4, plus listed versions
1.2.01.2.11.2.1rc11.2.1rc21.2.21.2.3
1.2.4
djust
PyPI
1.3.0.dev0 up to 1.3.0rc8, plus listed versions
1.3.0rc41.3.0rc51.3.0rc61.3.0rc7
1.3.0rc8

History

History
RecordedChangedSources
factsOSV
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-113731 is a low vulnerability (CVSS 3.3) in Djust-org Djust, published October 20, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.19%, the 8th percentile. Fixed in djust 1.0.10.

References

Show 7 moreShow less
This page doesn't list everything

Left out here: the rest of 2 source descriptions. The JSON record has them.

MoveEnter Open