{"id":"CVE-2026-34726","aliases":["GHSA-85v3-4m8g-hrh6","PYSEC-2026-2134"],"title":"Copier `_subdirectory` allows template root escape via parent-directory traversal","summary":"Copier `_subdirectory` allows template root escape via parent-directory traversal","severity":"medium","cvss":4.4,"cvssVector":"CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N","vendor":"copier","product":"copier","ecosystem":"pip","affected":["copier < 9.14.1"],"patched":["copier 9.14.1"],"published":"2026-04-01","updated":"2026-07-13","source":"OSV","sourceUrl":"https://osv.dev/vulnerability/GHSA-85v3-4m8g-hrh6","references":[{"url":"https://github.com/copier-org/copier/security/advisories/GHSA-85v3-4m8g-hrh6"},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-34726"},{"url":"https://github.com/copier-org/copier/commit/cb80a3ffc9c3787de3ed837e04ca29a0ff8ca3df"},{"url":"https://github.com/copier-org/copier"},{"url":"https://github.com/copier-org/copier/releases/tag/v9.14.1"}],"tags":["osv","pip"],"epss":0.00383,"epssPercentile":0.32183,"ingestedAt":"2026-07-13T18:57:54.836Z","slug":"CVE-2026-34726","body":"## Overview\n\n### Summary\n\nCopier's `_subdirectory` setting is documented as the subdirectory to use as the template root. However, the current implementation accepts parent-directory traversal such as `..` and uses it directly when selecting the template root.\n\nAs a result, a template can escape its own directory and make Copier render files from the parent directory without `--UNSAFE`.\n\n### Details\n\nThe relevant code path is:\n\n1. the template defines `_subdirectory`\n2. Copier renders that string\n3. `template_copy_root` returns `self.template.local_abspath / subdir`\n4. Copier walks that directory as the template root\n\nRelevant code:\n\n- <https://github.com/copier-org/copier/blob/7aa7021bd73797c982492bac3535515d4484fdb7/copier/_main.py#L1056-L1062>\n- <https://github.com/copier-org/copier/blob/7aa7021bd73797c982492bac3535515d4484fdb7/copier/_template.py#L504-L513>\n\nThe effective sink is:\n\n```python\nsubdir = self._render_string(self.template.subdirectory) or \"\"\nreturn self.template.local_abspath / subdir\n```\n\nThere is no check that the resulting path stays inside the template directory.\n\nThe documentation for `_subdirectory` describes it as:\n\n> Subdirectory to use as the template root when generating a project.\n\nand explains it as a way to separate template metadata from template source code:\n\n<https://github.com/copier-org/copier/blob/7aa7021bd73797c982492bac3535515d4484fdb7/docs/configuring.md#L1582-L1646>\n\nThat description fits values like `template` or `poetry`, but not `..`.\n\n### PoC\n\n#### PoC 1: `_subdirectory: ..` escapes to the parent directory\n\n```sh\nmkdir -p root/template dst\necho 'loot' > root/loot.txt\nprintf '%s\\n' '_subdirectory: ..' > root/template/copier.yml\n\ncopier copy --overwrite root/template dst\nfind dst -maxdepth 3 -type f | sort\ncat dst/loot.txt\n```\n\nExpected output includes:\n\n```text\ndst/loot.txt\ndst/template/copier.yml\nloot\n```\n\nThis shows Copier is rendering from `root/` rather than from `root/template/`.\n\n### Impact\n\nIf a user runs Copier on an untrusted template, that template can change the effective template root and make Copier render files from outside the intended template directory.\n\nPractical impact:\n\n- template-root escape via `..`\n- rendering of parent-directory files that were not meant to be part of the template\n- possible without `--UNSAFE`\n\n## Affected packages\n\n- `copier < 9.14.1`\n\n## Remediation\n\nUpgrade to a patched release:\n\n- `copier 9.14.1`","depth":"sunlit","depthScore":24,"depthScoreParts":{"impact":24.2,"likelihood":0.1,"exploitation":0,"ransomware":0},"changes":[]}