VYPR
High severity8.4GHSA Advisory· Published May 28, 2026· Updated May 28, 2026

compliance-trestle - jinja has an Arbitrary File Write via Path Traversal

CVE-2026-46345

Description

Relevant Products/Components:

  • trestle/core/commands/author/jinja.py
  • trestle author jinja

---

Detailed

Description:

The -o/--output argument in trestle author jinja allows writing files outside the intended workspace.

The application does not properly validate:

  • ../
  • ..\
  • absolute paths

This allows arbitrary file write to attacker-controlled locations.

Vulnerable code:

output_file = trestle_root / r_output_file

An attacker can overwrite files such as:

  • .github/workflows/*.yml
  • .git/hooks/*
  • user writable config files

This can lead to CI/CD compromise or local code execution.

---

Steps

To Reproduce:

  1. Clone the repository:
git clone https://github.com/oscal-compass/compliance-trestle.git
cd compliance-trestle
  1. Create template:
echo "hello" > template.j2
  1. Run:
trestle author jinja -i template.j2 -o "subdir\..\..\..\..\..\poc.txt"
  1. Observe:
dir E:\poc.txt

The file is written outside the repository workspace.

---

Browsers

Verified In:

Not browser related.

Tested on:

  • Windows 11
  • Python 3.13

---

Supporting

Material/References:

Affected file:

trestle/core/commands/author/jinja.py

Successfully verified:

  • directory traversal using ../
  • Windows traversal using ..\
  • arbitrary file write outside workspace

---

Access

Vector Required for Exploitation:

Local

---

Vulnerability

Exists in Default Configuration?:

Yes

---

Is the exploitation trivial or does it involve a multi-step process that may depend on user/victim interaction?:

Trivial. Single command execution.

---

Exploitation

Requires Authentication?:

No

---

Under what privileges does the vulnerable service or component run?:

Runs with privileges of the user executing the trestle command.

Impact

An attacker can write files outside the intended workspace directory and overwrite sensitive files writable by the current user.

Possible impacts include:

  • overwriting .github/workflows/*.yml to execute attacker-controlled GitHub Actions workflows
  • overwriting .git/hooks/* for local code execution
  • modifying user configuration files such as .bashrc
  • tampering with repository files and generated compliance artifacts

In CI/CD environments, this may result in execution of attacker-controlled commands on build runners.

AI Insight

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

A path traversal in trestle author jinja allows arbitrary file write outside the workspace due to insufficient validation of the `--output` argument.

Vulnerability

The trestle author jinja command in the compliance-trestle project contains a path traversal vulnerability in its -o/--output argument. When constructing the output file path as trestle_root / r_output_file, the application does not validate path components such as ../, ..\, or absolute paths, enabling an attacker to write files to arbitrary locations outside the intended workspace. The vulnerability affects all versions prior to the fix commit 7d107b3ac53caca7bde97a6278b23cd739d94525 [1][2].

Exploitation

An attacker with local access to run the trestle command can exploit this vulnerability without authentication [3][4]. The attack is trivial: a single command execution. For example, on Windows, running trestle author jinja -i template.j2 -o "subdir\..\..\..\..\..\poc.txt" writes the file outside the repository workspace [3][4]. The attacker only needs to create a template and provide a malicious output path. No user interaction beyond the attacker's own command execution is required.

Impact

Successful exploitation allows an attacker to write arbitrary files to any location writable by the user running the trestle command [3][4]. This can be used to overwrite .github/workflows/*.yml to execute attacker-controlled GitHub Actions workflows, overwrite .git/hooks/* for local code execution, modify user configuration files such as .bashrc, or tamper with repository files and generated compliance artifacts [3][4]. The impact includes CI/CD compromise and local code execution.

Mitigation

The vulnerability is fixed in commit 7d107b3ac53caca7bde97a6278b23cd739d94525 [1] and the merge commit 247fcce289f60103f3d8e28d8ec51a6986b94fb6 [2]. The fix introduces path traversal validation using PathSecurityValidator.validate_local_path(output_file, trestle_root) and refactors the Jinja environment creation [1][2]. Users should update compliance-trestle to a version that includes these commits. As of the publication date, no workaround is documented other than applying the patch [3][4].

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

Affected products

2

Patches

2
7d107b3ac53c

fix: add path traversal protection and prevent SSTI in jinja templating

2 files changed · +137 34
  • tests/trestle/core/commands/author/jinja_cmd_test.py+111 1 modified
    @@ -16,12 +16,16 @@
     import os
     import pathlib
     import shutil
    +from types import SimpleNamespace
    +
    +import pytest
     
     from _pytest.monkeypatch import MonkeyPatch
     
     from tests.test_utils import execute_command_and_assert, setup_for_ssp
     
    -from trestle.core.commands.author.jinja import _number_captions
    +from trestle.common.err import TrestleError
    +from trestle.core.commands.author.jinja import JinjaCmd, _number_captions
     from trestle.core.commands.author.ssp import SSPGenerate
     from trestle.core.markdown.docs_markdown_node import DocsMarkdownNode
     
    @@ -295,3 +299,109 @@ def test_jinja_with_template_only(
             node1 = tree.get_node_for_key('# A')
             node2 = tree.get_node_for_key('# C')
             assert node1.subnodes[0].key == node2.subnodes[0].key
    +
    +
    +def test_jinja_path_traversal_protection(
    +    testdata_dir: pathlib.Path, tmp_trestle_dir: pathlib.Path, monkeypatch: MonkeyPatch
    +) -> None:
    +    """Test that path traversal attacks are blocked in jinja command."""
    +    from trestle.core.remote.security import PathSecurityValidator
    +
    +    # Test path validation directly to ensure 100% coverage of the validation code
    +    # Test 1: Path traversal with ../ should fail
    +    with pytest.raises(TrestleError) as exc_info:
    +        output_file = tmp_trestle_dir / '../../../etc/passwd'
    +        PathSecurityValidator.validate_local_path(output_file, tmp_trestle_dir)
    +    assert 'Security violation' in str(exc_info.value)
    +    assert 'Path traversal blocked' in str(exc_info.value)
    +
    +    # Test 2: Path traversal with multiple ../ should fail
    +    with pytest.raises(TrestleError) as exc_info:
    +        output_file = tmp_trestle_dir / 'subdir/../../poc.txt'
    +        PathSecurityValidator.validate_local_path(output_file, tmp_trestle_dir)
    +    assert 'Security violation' in str(exc_info.value)
    +
    +    # Test 3: Absolute path should fail
    +    with pytest.raises(TrestleError) as exc_info:
    +        output_file = pathlib.Path('/tmp/attack.md')
    +        PathSecurityValidator.validate_local_path(output_file, tmp_trestle_dir)
    +    assert 'Security violation' in str(exc_info.value)
    +
    +    # Test 4: Complex traversal should fail
    +    with pytest.raises(TrestleError) as exc_info:
    +        output_file = tmp_trestle_dir / 'a/b/c/../../../../etc/passwd'
    +        PathSecurityValidator.validate_local_path(output_file, tmp_trestle_dir)
    +    assert 'Security violation' in str(exc_info.value)
    +
    +    # Test 5: Valid relative path should succeed
    +    output_file = tmp_trestle_dir / 'output/valid.md'
    +    PathSecurityValidator.validate_local_path(output_file, tmp_trestle_dir)  # Should not raise
    +
    +
    +def test_jinja_docs_profile_path_traversal_protection(tmp_trestle_dir: pathlib.Path) -> None:
    +    """Test that path traversal attacks are blocked in jinja docs-profile mode."""
    +    from trestle.core.remote.security import PathSecurityValidator
    +
    +    # Test validation for multi-file output paths
    +    # Test 1: Path traversal in output directory should fail
    +    with pytest.raises(TrestleError) as exc_info:
    +        output_file = tmp_trestle_dir / '../../../etc/ac-1.md'
    +        PathSecurityValidator.validate_local_path(output_file, tmp_trestle_dir)
    +    assert 'Security violation' in str(exc_info.value)
    +    assert 'Path traversal blocked' in str(exc_info.value)
    +
    +    # Test 2: Complex path traversal should fail
    +    with pytest.raises(TrestleError) as exc_info:
    +        output_file = tmp_trestle_dir / 'controls/../../tmp/ac-1.md'
    +        PathSecurityValidator.validate_local_path(output_file, tmp_trestle_dir)
    +    assert 'Security violation' in str(exc_info.value)
    +
    +    # Test 3: Directory creation path traversal should fail
    +    with pytest.raises(TrestleError) as exc_info:
    +        group_dir = tmp_trestle_dir / '../../../etc/malicious'
    +        PathSecurityValidator.validate_local_path(group_dir, tmp_trestle_dir)
    +    assert 'Security violation' in str(exc_info.value)
    +
    +    # Test 4: Valid relative path should succeed
    +    output_file = tmp_trestle_dir / 'controls_output/ac/ac-1.md'
    +    PathSecurityValidator.validate_local_path(output_file, tmp_trestle_dir)  # Should not raise
    +
    +
    +def test_render_template_does_not_recursively_evaluate_untrusted_data(tmp_path: pathlib.Path) -> None:
    +    """Test that rendered attacker-controlled data is not re-evaluated as Jinja."""
    +    template_path = tmp_path / 'template.j2'
    +    template_path.write_text('Title: {{ ssp.metadata.title }}', encoding='utf-8')
    +
    +    jinja_env = JinjaCmd._create_jinja_environment(tmp_path)
    +    template = jinja_env.get_template(template_path.name)
    +
    +    lut = {
    +        'ssp': SimpleNamespace(
    +            metadata=SimpleNamespace(title="{{ namespace.__init__.__globals__.os.system('touch poc.txt') }}")
    +        )
    +    }
    +
    +    output = JinjaCmd.render_template(template, lut, tmp_path)
    +
    +    assert output.startswith('Title: {{ namespace.__init__.__globals__.os.system(')
    +    assert 'touch poc.txt' in output
    +    assert '{{' in output
    +    assert '}}' in output
    +    assert '&' in output
    +    assert not (tmp_path / 'poc.txt').exists()
    +
    +
    +def test_render_template_supports_trusted_include(tmp_path: pathlib.Path) -> None:
    +    """Test that trusted template includes continue to work."""
    +    include_path = tmp_path / 'partial.j2'
    +    include_path.write_text('World', encoding='utf-8')
    +
    +    template_path = tmp_path / 'template.j2'
    +    template_path.write_text("Hello {% include 'partial.j2' %}", encoding='utf-8')
    +
    +    jinja_env = JinjaCmd._create_jinja_environment(tmp_path)
    +    template = jinja_env.get_template(template_path.name)
    +
    +    output = JinjaCmd.render_template(template, {}, tmp_path)
    +
    +    assert output == 'Hello World'
    
  • trestle/core/commands/author/jinja.py+26 33 modified
    @@ -20,16 +20,16 @@
     import operator
     import pathlib
     import re
    -import uuid
     from typing import Any, Dict, Optional
     
    -from jinja2 import ChoiceLoader, DictLoader, Environment, FileSystemLoader, Template
    +from jinja2 import Environment, FileSystemLoader, Template
     
     from ruamel.yaml import YAML
     
     from trestle.common import const, log
     from trestle.common.err import TrestleIncorrectArgsError, handle_generic_command_exception
     from trestle.common.load_validate import load_validate_model_name
    +from trestle.core.remote.security import PathSecurityValidator
     from trestle.common.model_utils import ModelUtils
     from trestle.core.catalog.catalog_interface import CatalogInterface
     from trestle.core.commands.command_docs import CommandPlusDocs
    @@ -48,8 +48,6 @@
     class JinjaCmd(CommandPlusDocs):
         """Transform an input template to an output document using jinja templating."""
     
    -    max_recursion_depth = 2
    -
         name = 'jinja'
     
         def _init_arguments(self) -> None:
    @@ -191,9 +189,7 @@ def jinja_ify(
         ) -> int:
             """Run jinja over an input file with additional booleans."""
             template_folder = pathlib.Path.cwd()
    -        jinja_env = Environment(
    -            loader=FileSystemLoader(template_folder), extensions=extensions(), trim_blocks=True, autoescape=True
    -        )
    +        jinja_env = JinjaCmd._create_jinja_environment(template_folder)
             template = jinja_env.get_template(str(r_input_file))
             # create boolean dict
             if operator.xor(bool(ssp), bool(profile)):
    @@ -228,7 +224,10 @@ def jinja_ify(
     
             output = JinjaCmd.render_template(template, lut, template_folder)
     
    +        # Validate output path to prevent path traversal
             output_file = trestle_root / r_output_file
    +        PathSecurityValidator.validate_local_path(output_file, trestle_root)
    +
             if number_captions:
                 output_file.open('w', encoding=const.FILE_ENCODING).write(_number_captions(output))
             else:
    @@ -274,14 +273,15 @@ def jinja_multiple_md(
                     control_path = catalog_interface.get_control_path(control.id)
                     for sub_dir in control_path:
                         group_dir = group_dir / sub_dir
    -                    if not group_dir.exists():
    -                        group_dir.mkdir(parents=True, exist_ok=True)
    +                    # Validate directory path to prevent path traversal before creating directories
    +                    full_group_dir = trestle_root / group_dir
    +                    PathSecurityValidator.validate_local_path(full_group_dir, trestle_root)
    +                    if not full_group_dir.exists():
    +                        full_group_dir.mkdir(parents=True, exist_ok=True)
     
                     control_writer = DocsControlWriter()
     
    -                jinja_env = Environment(
    -                    loader=FileSystemLoader(template_folder), extensions=extensions(), trim_blocks=True, autoescape=True
    -                )
    +                jinja_env = JinjaCmd._create_jinja_environment(template_folder)
                     template = jinja_env.get_template(str(r_input_file))
                     lut['catalog_interface'] = catalog_interface
                     lut['control_interface'] = ControlInterface()
    @@ -291,33 +291,26 @@ def jinja_multiple_md(
                     lut['group_title'] = group_title
                     output = JinjaCmd.render_template(template, lut, template_folder)
     
    -                output_file = trestle_root / group_dir / pathlib.Path(control.id + const.MARKDOWN_FILE_EXT)
    +                # Validate output path to prevent path traversal
    +                relative_output_path = group_dir / pathlib.Path(control.id + const.MARKDOWN_FILE_EXT)
    +                output_file = trestle_root / relative_output_path
    +                PathSecurityValidator.validate_local_path(output_file, trestle_root)
    +
                     output_file.open('w', encoding=const.FILE_ENCODING).write(output)
     
             return CmdReturnCodes.SUCCESS.value
     
         @staticmethod
    -    def render_template(template: Template, lut: Dict[str, Any], template_folder: pathlib.Path) -> str:
    -        """Render template."""
    -        new_output = template.render(**lut)
    -        output = ''
    -        # This recursion allows nesting within expressions (e.g. an expression can contain jinja templates).
    -        error_countdown = JinjaCmd.max_recursion_depth
    -        while new_output != output and error_countdown > 0:
    -            error_countdown = error_countdown - 1
    -            output = new_output
    -            random_name = uuid.uuid4()  # Should be random and not used.
    -            dict_loader = DictLoader({str(random_name): new_output})
    -            jinja_env = Environment(
    -                loader=ChoiceLoader([dict_loader, FileSystemLoader(template_folder)]),
    -                extensions=extensions(),
    -                autoescape=True,
    -                trim_blocks=True,
    -            )
    -            template = jinja_env.get_template(str(random_name))
    -            new_output = template.render(**lut)
    +    def _create_jinja_environment(template_folder: pathlib.Path) -> Environment:
    +        """Create the trusted Jinja environment used for loading template files."""
    +        return Environment(
    +            loader=FileSystemLoader(template_folder), extensions=extensions(), trim_blocks=True, autoescape=True
    +        )
     
    -        return output
    +    @staticmethod
    +    def render_template(template: Template, lut: Dict[str, Any], template_folder: pathlib.Path) -> str:
    +        """Render a trusted template exactly once to avoid recursive SSTI of untrusted data."""
    +        return template.render(**lut)
     
     
     def _number_captions(md_body: str) -> str:
    
247fcce289f6

Merge commit from fork

2 files changed · +54 31
  • tests/trestle/core/commands/author/jinja_cmd_test.py+42 1 modified
    @@ -16,6 +16,7 @@
     import os
     import pathlib
     import shutil
    +from types import SimpleNamespace
     
     import pytest
     
    @@ -24,7 +25,7 @@
     from tests.test_utils import execute_command_and_assert, setup_for_ssp
     
     from trestle.common.err import TrestleError
    -from trestle.core.commands.author.jinja import _number_captions
    +from trestle.core.commands.author.jinja import JinjaCmd, _number_captions
     from trestle.core.commands.author.ssp import SSPGenerate
     from trestle.core.markdown.docs_markdown_node import DocsMarkdownNode
     
    @@ -364,3 +365,43 @@ def test_jinja_docs_profile_path_traversal_protection(tmp_trestle_dir: pathlib.P
         # Test 4: Valid relative path should succeed
         output_file = tmp_trestle_dir / 'controls_output/ac/ac-1.md'
         PathSecurityValidator.validate_local_path(output_file, tmp_trestle_dir)  # Should not raise
    +
    +
    +def test_render_template_does_not_recursively_evaluate_untrusted_data(tmp_path: pathlib.Path) -> None:
    +    """Test that rendered attacker-controlled data is not re-evaluated as Jinja."""
    +    template_path = tmp_path / 'template.j2'
    +    template_path.write_text('Title: {{ ssp.metadata.title }}', encoding='utf-8')
    +
    +    jinja_env = JinjaCmd._create_jinja_environment(tmp_path)
    +    template = jinja_env.get_template(template_path.name)
    +
    +    lut = {
    +        'ssp': SimpleNamespace(
    +            metadata=SimpleNamespace(title="{{ namespace.__init__.__globals__.os.system('touch poc.txt') }}")
    +        )
    +    }
    +
    +    output = JinjaCmd.render_template(template, lut, tmp_path)
    +
    +    assert output.startswith('Title: {{ namespace.__init__.__globals__.os.system(')
    +    assert 'touch poc.txt' in output
    +    assert '{{' in output
    +    assert '}}' in output
    +    assert '&' in output
    +    assert not (tmp_path / 'poc.txt').exists()
    +
    +
    +def test_render_template_supports_trusted_include(tmp_path: pathlib.Path) -> None:
    +    """Test that trusted template includes continue to work."""
    +    include_path = tmp_path / 'partial.j2'
    +    include_path.write_text('World', encoding='utf-8')
    +
    +    template_path = tmp_path / 'template.j2'
    +    template_path.write_text("Hello {% include 'partial.j2' %}", encoding='utf-8')
    +
    +    jinja_env = JinjaCmd._create_jinja_environment(tmp_path)
    +    template = jinja_env.get_template(template_path.name)
    +
    +    output = JinjaCmd.render_template(template, {}, tmp_path)
    +
    +    assert output == 'Hello World'
    
  • trestle/core/commands/author/jinja.py+12 30 modified
    @@ -20,10 +20,9 @@
     import operator
     import pathlib
     import re
    -import uuid
     from typing import Any, Dict, Optional
     
    -from jinja2 import ChoiceLoader, DictLoader, Environment, FileSystemLoader, Template
    +from jinja2 import Environment, FileSystemLoader, Template
     
     from ruamel.yaml import YAML
     
    @@ -49,8 +48,6 @@
     class JinjaCmd(CommandPlusDocs):
         """Transform an input template to an output document using jinja templating."""
     
    -    max_recursion_depth = 2
    -
         name = 'jinja'
     
         def _init_arguments(self) -> None:
    @@ -192,9 +189,7 @@ def jinja_ify(
         ) -> int:
             """Run jinja over an input file with additional booleans."""
             template_folder = pathlib.Path.cwd()
    -        jinja_env = Environment(
    -            loader=FileSystemLoader(template_folder), extensions=extensions(), trim_blocks=True, autoescape=True
    -        )
    +        jinja_env = JinjaCmd._create_jinja_environment(template_folder)
             template = jinja_env.get_template(str(r_input_file))
             # create boolean dict
             if operator.xor(bool(ssp), bool(profile)):
    @@ -286,9 +281,7 @@ def jinja_multiple_md(
     
                     control_writer = DocsControlWriter()
     
    -                jinja_env = Environment(
    -                    loader=FileSystemLoader(template_folder), extensions=extensions(), trim_blocks=True, autoescape=True
    -                )
    +                jinja_env = JinjaCmd._create_jinja_environment(template_folder)
                     template = jinja_env.get_template(str(r_input_file))
                     lut['catalog_interface'] = catalog_interface
                     lut['control_interface'] = ControlInterface()
    @@ -308,27 +301,16 @@ def jinja_multiple_md(
             return CmdReturnCodes.SUCCESS.value
     
         @staticmethod
    -    def render_template(template: Template, lut: Dict[str, Any], template_folder: pathlib.Path) -> str:
    -        """Render template."""
    -        new_output = template.render(**lut)
    -        output = ''
    -        # This recursion allows nesting within expressions (e.g. an expression can contain jinja templates).
    -        error_countdown = JinjaCmd.max_recursion_depth
    -        while new_output != output and error_countdown > 0:
    -            error_countdown = error_countdown - 1
    -            output = new_output
    -            random_name = uuid.uuid4()  # Should be random and not used.
    -            dict_loader = DictLoader({str(random_name): new_output})
    -            jinja_env = Environment(
    -                loader=ChoiceLoader([dict_loader, FileSystemLoader(template_folder)]),
    -                extensions=extensions(),
    -                autoescape=True,
    -                trim_blocks=True,
    -            )
    -            template = jinja_env.get_template(str(random_name))
    -            new_output = template.render(**lut)
    +    def _create_jinja_environment(template_folder: pathlib.Path) -> Environment:
    +        """Create the trusted Jinja environment used for loading template files."""
    +        return Environment(
    +            loader=FileSystemLoader(template_folder), extensions=extensions(), trim_blocks=True, autoescape=True
    +        )
     
    -        return output
    +    @staticmethod
    +    def render_template(template: Template, lut: Dict[str, Any], template_folder: pathlib.Path) -> str:
    +        """Render a trusted template exactly once to avoid recursive SSTI of untrusted data."""
    +        return template.render(**lut)
     
     
     def _number_captions(md_body: str) -> str:
    

Vulnerability mechanics

Root cause

"Missing path validation in the `-o/--output` argument of `trestle author jinja` allows directory traversal via `../`, `..\`, or absolute paths, enabling arbitrary file write outside the intended workspace."

Attack vector

An attacker supplies a malicious `-o/--output` value containing `../` (or `..\` on Windows) to the `trestle author jinja` command [ref_id=3]. The vulnerable code `output_file = trestle_root / r_output_file` concatenates the user-supplied path with the trestle root without sanitization [ref_id=3]. Because no authentication is required and the command runs with the user's privileges, a single command execution can write files to arbitrary locations writable by that user [ref_id=3]. The attacker can overwrite `.github/workflows/*.yml`, `.git/hooks/*`, or user config files such as `.bashrc`, leading to CI/CD compromise or local code execution [ref_id=3].

Affected code

The vulnerable code is in `trestle/core/commands/author/jinja.py` [ref_id=3]. The line `output_file = trestle_root / r_output_file` in the `jinja_ify` method directly concatenates the user-supplied `-o/--output` argument with the trestle root without any path traversal validation [ref_id=3]. The `jinja_multiple_md` method had the same issue when constructing output paths for control markdown files [patch_id=2964629].

What the fix does

Both patches [patch_id=2964629] and [patch_id=2964628] add calls to `PathSecurityValidator.validate_local_path()` before writing output files and creating directories in `jinja_ify()` and `jinja_multiple_md()` [patch_id=2964629]. The validator rejects paths that escape the trestle root via `../`, multiple `../`, or absolute paths, as confirmed by the new test cases [patch_id=2964629]. Additionally, the patches remove the recursive template re-evaluation loop (which used `ChoiceLoader` and `DictLoader` to re-render output as a Jinja template up to `max_recursion_depth` times), preventing server-side template injection (SSTI) from attacker-controlled data in rendered output [patch_id=2964629]. The `_create_jinja_environment` factory method is introduced to centralize environment creation [patch_id=2964629].

Preconditions

  • networkThe attacker must have local access to execute the `trestle author jinja` command.
  • authNo authentication is required; the command runs with the privileges of the executing user.
  • configThe vulnerability exists in the default configuration.
  • inputThe attacker supplies a malicious `-o/--output` argument containing path traversal sequences.

Reproduction

1. Clone the repository: `git clone https://github.com/oscal-compass/compliance-trestle.git && cd compliance-trestle` [ref_id=3]. 2. Create a template: `echo "hello" > template.j2` [ref_id=3]. 3. Run: `trestle author jinja -i template.j2 -o "subdir\..\..\..\..\..\poc.txt"` [ref_id=3]. 4. Observe the file is written outside the repository workspace [ref_id=3].

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

References

4

News mentions

0

No linked articles in our index yet.