diff --git a/.jules/sentinel.md b/.jules/sentinel.md new file mode 100644 index 0000000000..06defc0f25 --- /dev/null +++ b/.jules/sentinel.md @@ -0,0 +1,4 @@ +## 2025-02-14 - Prevent Zip Slip Vulnerability during Extraction +**Vulnerability:** Zip Slip (path traversal) risk when extracting `zipfile.ZipFile` archives using `extractall()` without verifying individual member paths in `helpers/skills_import.py`. +**Learning:** Malicious zip files can contain relative paths (e.g., `../../etc/passwd`) or absolute paths that extract files outside the intended target directory. Using `extractall()` blindly trusts the paths within the zip archive, bypassing directory containment. +**Prevention:** Explicitly iterate through every member in `z.namelist()`, construct the member's resolved absolute path, and verify it falls within the intended target directory using a safe path containment method like `Path.resolve().is_relative_to()` before proceeding with extraction. diff --git a/helpers/skills_import.py b/helpers/skills_import.py index c73ffa55de..b10150f67f 100644 --- a/helpers/skills_import.py +++ b/helpers/skills_import.py @@ -94,6 +94,10 @@ def _unzip_to_temp_dir(zip_path: Path) -> Path: target.mkdir(parents=True, exist_ok=True) with zipfile.ZipFile(zip_path, "r") as z: + for member in z.namelist(): + member_path = (target / member).resolve() + if not member_path.is_relative_to(target.resolve()): + raise ValueError(f"Unsafe path in archive: {member}") z.extractall(target) # If zip contains a single top-level folder, treat that as the root