CVE-2024-9701
Description
A Remote Code Execution (RCE) vulnerability has been identified in the Kedro ShelveStore class (version 0.19.8). This vulnerability allows an attacker to execute arbitrary Python code via deserialization of malicious payloads, potentially leading to a full system compromise. The ShelveStore class uses Python's shelve module to manage session data, which relies on pickle for serialization. Crafting a malicious payload and storing it in the shelve file can lead to RCE when the payload is deserialized.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
kedroPyPI | < 0.19.9 | 0.19.9 |
Patches
2d79fa51de55a66e5e074b278Remove `ShelveStore` (#4148)
6 files changed · +4 −136
docs/source/api/kedro.framework.session.shelvestore.ShelveStore.rst+0 −6 removed@@ -1,6 +0,0 @@ -kedro.framework.session.shelvestore.ShelveStore -================================================ - -.. currentmodule:: kedro.framework.session.shelvestore - -.. autoclass:: ShelveStore
kedro/framework/session/shelvestore.py+0 −46 removed@@ -1,46 +0,0 @@ -"""This module implements a dict-like store object used to persist Kedro sessions. -This module is separated from store.py to ensure it's only imported when exported explicitly. -""" - -from __future__ import annotations - -import dbm -import shelve -from multiprocessing import Lock -from pathlib import Path -from typing import Any - -from .store import BaseSessionStore - - -class ShelveStore(BaseSessionStore): - """Stores the session data on disk using `shelve` package. - This is an example of how to persist data on disk.""" - - _lock = Lock() - - @property - def _location(self) -> Path: - return Path(self._path).expanduser().resolve() / self._session_id / "store" - - def read(self) -> dict[str, Any]: - """Read the data from disk using `shelve` package.""" - data: dict[str, Any] = {} - try: - with shelve.open(str(self._location), flag="r") as _sh: # noqa: S301 - data = dict(_sh) - except dbm.error: - pass - return data - - def save(self) -> None: - """Save the data on disk using `shelve` package.""" - location = self._location - location.parent.mkdir(parents=True, exist_ok=True) - - with self._lock, shelve.open(str(location)) as _sh: # noqa: S301 - keys_to_del = _sh.keys() - self.data.keys() - for key in keys_to_del: - del _sh[key] - - _sh.update(self.data)
RELEASE.md+1 −0 modified@@ -7,6 +7,7 @@ * Fixed bug where using dataset factories breaks with `ThreadRunner`. ## Breaking changes to the API +* Removed `ShelveStore` to address a security vulnerability. ## Documentation changes * Fix logo on PyPI page.
tests/framework/project/test_settings.py+3 −4 modified@@ -7,7 +7,6 @@ from kedro.config import OmegaConfigLoader from kedro.framework.context.context import KedroContext from kedro.framework.project import configure_project, settings, validate_settings -from kedro.framework.session.shelvestore import ShelveStore from kedro.framework.session.store import BaseSessionStore from kedro.io import DataCatalog @@ -40,8 +39,8 @@ def mock_package_name_with_settings_file(tmpdir): DISABLE_HOOKS_FOR_PLUGINS = ("kedro-viz",) - from kedro.framework.session.shelvestore import ShelveStore - SESSION_STORE_CLASS = ShelveStore + from kedro.framework.session.store import BaseSessionStore + SESSION_STORE_CLASS = BaseSessionStore SESSION_STORE_ARGS = {{ "path": "./sessions" }} @@ -103,7 +102,7 @@ def test_settings_after_configuring_project_shows_updated_values( configure_project(mock_package_name_with_settings_file) assert len(settings.HOOKS) == 1 and isinstance(settings.HOOKS[0], ProjectHooks) assert settings.DISABLE_HOOKS_FOR_PLUGINS.to_list() == ["kedro-viz"] - assert settings.SESSION_STORE_CLASS is ShelveStore + assert settings.SESSION_STORE_CLASS is BaseSessionStore assert settings.SESSION_STORE_ARGS == {"path": "./sessions"} assert settings.CONTEXT_CLASS is MyContext assert settings.CONF_SOURCE == "test_conf"
tests/framework/session/test_session.py+0 −37 modified@@ -22,12 +22,10 @@ ValidationError, Validator, _HasSharedParentClassValidator, - _IsSubclassValidator, _ProjectSettings, ) from kedro.framework.session import KedroSession from kedro.framework.session.session import KedroSessionError -from kedro.framework.session.shelvestore import ShelveStore from kedro.framework.session.store import BaseSessionStore from kedro.utils import _has_rich_handler @@ -235,21 +233,6 @@ class MockSettings(_ProjectSettings): ) -@pytest.fixture -def mock_settings_shelve_session_store(mocker, fake_project): - shelve_location = fake_project / "nested" / "sessions" - - class MockSettings(_ProjectSettings): - _SESSION_STORE_CLASS = _IsSubclassValidator( - "SESSION_STORE_CLASS", default=lambda *_: ShelveStore - ) - _SESSION_STORE_ARGS = Validator( - "SESSION_STORE_ARGS", default={"path": shelve_location.as_posix()} - ) - - return _mock_imported_settings_paths(mocker, MockSettings()) - - @pytest.fixture def fake_session_id(mocker): session_id = "fake_session_id" @@ -502,26 +485,6 @@ def test_default_store(self, fake_project, fake_session_id, caplog): ] assert actual_log_messages == expected_log_messages - @pytest.mark.usefixtures("mock_settings_shelve_session_store") - def test_shelve_store(self, fake_project, fake_session_id, caplog, mocker): - mocker.patch("pathlib.Path.is_file", return_value=True) - shelve_location = fake_project / "nested" / "sessions" - other = KedroSession.create(fake_project) - assert other._store.__class__ is ShelveStore - assert other._store._path == shelve_location.as_posix() - assert other._store._location == shelve_location / fake_session_id / "store" - assert other._store._session_id == fake_session_id - assert not shelve_location.is_dir() - - other.close() # session data persisted - assert shelve_location.is_dir() - actual_log_messages = [ - rec.getMessage() - for rec in caplog.records - if rec.name == STORE_LOGGER_NAME and rec.levelno == logging.DEBUG - ] - assert not actual_log_messages - def test_wrong_store_type(self, mock_settings_file_bad_session_store_class): pattern = ( "Invalid value 'tests.framework.session.test_session.BadStore' received "
tests/framework/session/test_store.py+0 −43 modified@@ -1,9 +1,5 @@ import logging -from pathlib import Path -import pytest - -from kedro.framework.session.shelvestore import ShelveStore from kedro.framework.session.store import BaseSessionStore FAKE_SESSION_ID = "fake_session_id" @@ -48,42 +44,3 @@ def test_save(self, caplog): if rec.name == STORE_LOGGER_NAME and rec.levelno == logging.DEBUG ] assert actual_debug_messages == expected_debug_messages - - -@pytest.fixture -def shelve_path(tmp_path): - return Path(tmp_path / "path" / "to" / "sessions") - - -class TestShelveStore: - def test_empty(self, shelve_path): - shelve = ShelveStore(str(shelve_path), FAKE_SESSION_ID) - assert shelve == {} - assert shelve._location == shelve_path / FAKE_SESSION_ID / "store" - assert not shelve_path.exists() - - def test_save(self, shelve_path): - assert not shelve_path.exists() - - shelve = ShelveStore(str(shelve_path), FAKE_SESSION_ID) - shelve["shelve_path"] = shelve_path - shelve.save() - - assert (shelve_path / FAKE_SESSION_ID).is_dir() - - reloaded = ShelveStore(str(shelve_path), FAKE_SESSION_ID) - assert reloaded == {"shelve_path": shelve_path} - - def test_update(self, shelve_path): - shelve = ShelveStore(str(shelve_path), FAKE_SESSION_ID) - shelve["shelve_path"] = shelve_path - shelve.save() - - shelve.update(new_key="new_value") - del shelve["shelve_path"] - reloaded = ShelveStore(str(shelve_path), FAKE_SESSION_ID) - assert reloaded == {"shelve_path": shelve_path} # changes not saved yet - - shelve.save() - reloaded = ShelveStore(str(shelve_path), FAKE_SESSION_ID) - assert reloaded == {"new_key": "new_value"}
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-747f-ww56-4q4hghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-9701ghsaADVISORY
- github.com/kedro-org/kedro/commit/66e5e074b2789469550370f370c8b486f638d975ghsaWEB
- huntr.com/bounties/96c77fef-93b2-4d4d-8cbe-57a718d8eea5nvdWEB
- github.com/kedro-org/kedro/commit/d79fa51de55ac0ccb58cce1a482df1b445f0fe7cnvd
News mentions
0No linked articles in our index yet.