A AegiFlow
HIGHCVSS 8.8EPSS 0.2%

CVE-2026-53951

Copier has a trust-prefix bypass via path traversal that runs tasks unprompted

Published
2026-08-19
Modified
2026-08-19
EPSS percentile
9%
Aliases
GHSA-9gmc-jqmh-3rvm
Sources
github-advisory

Summary

# Copier: trust-prefix bypass via path traversal runs tasks unprompted ### Summary In copier `>= 9.5.0, str: if url.startswith("~"): # Only expand on str to avoid messing with URLs url = expanduser(url) # noqa: PTH111 return url ``` This decision gates code execution — `copier/_main.py:293` (tag `v9.15.1`): ```python if self.unsafe or is_trusted_repository(self.settings.trust, self.template.url): return # skip the unsafe-feature check entirely ``` The chain: the trust comparison sees the **raw** URL, so `"https://github.com/safeorg/../evilorg/t.git".startswith("https://github.com/safeorg/")` is `True`; but the value copier hands to `git`/`pathlib` is **normalized**, so the template actually loaded is `evilorg/t` (a different, attacker-owned org). Trust is granted to a location the user never trusted, and `_check_unsafe` returns early, so the malicious template's tasks execute with no prompt. This is most acute on `copier update`, which reads `_src_path` from the project's `.copier-answers.yml` (`copier/_subproject.py`) — i.e. an attacker who hands you a project controls the URL that the trust check is applied to. In-repo asymmetry that confirms the omission: copier consistently resolves paths *everywhere else* it makes a security decision — `Path.resolve()` plus `is_relative_to(...)` guards in `_render_template`, `template_copy_root`, and `_external_data` — but not in the trust comparison. ### PoC Self-contained standalone script; runs against a clean, pinned PyPI install via the real `copier` CLI only. **Static by default** (`copier copy --pretend` reaches the trust decision but does not execute tasks); `--prove-exec` is an opt-in supplementary run that fires an inert marker (`echo` + `touch`). The full `poc.py` accompanies this report. Build and run: ```bash python -m venv venv && . venv/bin/activate pip install "copier==9.15.1" python poc.py # static proof (default) python poc.py --prove-exec # also fire the inert marker ``` Observed output (`copier 9.15.1`): ``` == version proof == copier == 9.15.1 module : .../site-packages/copier/__init__.py == inputs == trusted prefix (settings.yml): /tmp/copier_trust_poc_XXXX/trusted_templates/ control src (canonical) : /tmp/copier_trust_poc_XXXX/attacker/evil_template exploit src (traversal) : /tmp/copier_trust_poc_XXXX/trusted_templates/../attacker/evil_template both resolve to the SAME dir : True exploit startswith trusted/ : True minimal delta

Affected packages

EcosystemPackageAffected versionsFixed versions
PyPIcopier9.15.2

Remediation: Upgrade to 9.15.2 or later.

References

Includes data from the GitHub Advisory Database, licensed under CC-BY 4.0.

CVE® is a registered trademark of The MITRE Corporation. CVE content reproduced under the CVE Terms of Use; copyright designation © MITRE.

EPSS scores provided by the FIRST.org Exploit Prediction Scoring System.