GHSA-8P4X-WR7X-3788
Vulnerability from github – Published: 2026-05-11 14:57 – Updated: 2026-06-09 02:00Impact
The built-in FileSystemLoader and CachingFileSystemLoader do not guard against reading files outside their search paths when given an absolute path to resolve. This allows malicious template authors to load and render arbitrary files via the {% include %} and {% render %} tags. Targeted files would need to contain valid Liquid markup and be readable by the application process.
Patches
The issue is fixed in version 2.2.0 with the inclusion of a template_path.is_absolute() condition in liquid/builtin/loaders/file_system_loader.py.
if os.path.pardir in template_path.parts or template_path.is_absolute():
raise TemplateNotFoundError(template_name)
Workarounds
Create a custom template loader by inheriting from FileSystemLoader and overriding resolve_path(). Use an instance of the custom loader as the loader argument when instantiating your Liquid environment.
import os
from pathlib import Path
from liquid import Environment
from liquid import FileSystemLoader
from liquid.exceptions import TemplateNotFoundError
class MyFileSystemLoader(FileSystemLoader):
def resolve_path(self, template_name: str) -> Path:
template_path = Path(template_name)
if self.ext and not template_path.suffix:
template_path = template_path.with_suffix(self.ext)
if os.path.pardir in template_path.parts or template_path.is_absolute():
raise TemplateNotFoundError(template_name)
for path in self.search_path:
source_path = path.joinpath(template_path)
if not source_path.exists():
continue
return source_path
raise TemplateNotFoundError(template_name)
env = Environment(loader=MyFileSystemLoader("path/to/templates/"))
{
"affected": [
{
"package": {
"ecosystem": "PyPI",
"name": "python-liquid"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "2.2.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-45017"
],
"database_specific": {
"cwe_ids": [
"CWE-22"
],
"github_reviewed": true,
"github_reviewed_at": "2026-05-11T14:57:43Z",
"nvd_published_at": "2026-05-28T16:16:25Z",
"severity": "HIGH"
},
"details": "### Impact\nThe built-in `FileSystemLoader` and `CachingFileSystemLoader` do not guard against reading files outside their search paths when given an absolute path to resolve. This allows malicious template authors to load and render arbitrary files via the `{% include %}` and `{% render %}` tags. Targeted files would need to contain valid Liquid markup and be readable by the application process.\n\n### Patches\nThe issue is fixed in version 2.2.0 with the inclusion of a `template_path.is_absolute()` condition in `liquid/builtin/loaders/file_system_loader.py`.\n\n```python\n if os.path.pardir in template_path.parts or template_path.is_absolute():\n raise TemplateNotFoundError(template_name)\n```\n\n### Workarounds\n\nCreate a custom template loader by inheriting from `FileSystemLoader` and overriding `resolve_path()`. Use an instance of the custom loader as the `loader` argument when instantiating your Liquid environment. \n\n```python\nimport os\nfrom pathlib import Path\n\nfrom liquid import Environment\nfrom liquid import FileSystemLoader\nfrom liquid.exceptions import TemplateNotFoundError\n\n\nclass MyFileSystemLoader(FileSystemLoader):\n def resolve_path(self, template_name: str) -\u003e Path:\n template_path = Path(template_name)\n\n if self.ext and not template_path.suffix:\n template_path = template_path.with_suffix(self.ext)\n\n if os.path.pardir in template_path.parts or template_path.is_absolute():\n raise TemplateNotFoundError(template_name)\n\n for path in self.search_path:\n source_path = path.joinpath(template_path)\n if not source_path.exists():\n continue\n return source_path\n\n raise TemplateNotFoundError(template_name)\n\n\nenv = Environment(loader=MyFileSystemLoader(\"path/to/templates/\"))\n```",
"id": "GHSA-8p4x-wr7x-3788",
"modified": "2026-06-09T02:00:21Z",
"published": "2026-05-11T14:57:43Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/jg-rp/liquid/security/advisories/GHSA-8p4x-wr7x-3788"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-45017"
},
{
"type": "PACKAGE",
"url": "https://github.com/jg-rp/liquid"
},
{
"type": "WEB",
"url": "https://github.com/pypa/advisory-database/tree/main/vulns/python-liquid/PYSEC-2026-192.yaml"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "python-liquid: Absolute paths escape filesystem loader search path"
}
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.
The approach is described in our paper Mapping CVEs to MITRE ATT&CK Techniques: A Curated Gold-Set Classifier and the Limits of LLM-Assisted Label Expansion.
Browse all ATT&CK techniques and the vulnerabilities related to each.
Related by attack behaviour
Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.