Skip to content

Commit 9d95d13

Browse files
committed
fix: hash-only cache naming (security fix) (#40)
- Remove original filename (prevent injection) - Format: .imports/<hash>.workflow.list - Tested: local + remote imports REFERENCE: #40
1 parent 47deb9c commit 9d95d13

File tree

6 files changed

+24
-20
lines changed

6 files changed

+24
-20
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
<!-- CHANGELOG.md -->
12
# CHANGELOG
23

34

src/workflow_as_list/executor/loader.py

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -172,26 +172,21 @@ def _fetch_remote(self, url: str) -> str:
172172
raise RuntimeError(f"Failed to fetch {url}: {e}") from e
173173

174174
def _get_import_cache_path(self, import_path: str, base_path: Path) -> Path:
175-
"""Get cache file path using hash-based naming (flat structure).
175+
"""Get cache file path using hash-only naming (security + flat structure).
176176
177177
Design:
178-
- Cache files named by content hash prefix + original filename
178+
- Cache files named by content hash only (no original filename)
179+
- Prevents path traversal and injection attacks
179180
- Flat structure (no deep directories)
180181
- Deduplication: same content = same cache file
181-
- User reads import line for source, cache is internal storage
182+
- User reads import line for source, cache filename is internal
182183
"""
183184
# Fetch content to compute hash
184185
imported_content = self._fetch_import(import_path, base_path)
185186
content_hash = hashlib.sha256(imported_content.encode("utf-8")).hexdigest()[:16]
186187

187-
# Extract original filename for readability
188-
if import_path.startswith(("http://", "https://")):
189-
original_name = import_path.split("/")[-1]
190-
else:
191-
original_name = Path(import_path).name
192-
193-
# Format: <hash-prefix>-<original-name>
194-
cache_filename = f"{content_hash}-{original_name}"
188+
# Format: <hash-prefix>.workflow.list (no original filename for security)
189+
cache_filename = f"{content_hash}.workflow.list"
195190
cache_path = self.imports_dir / cache_filename
196191

197192
cache_path.parent.mkdir(parents=True, exist_ok=True)

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

workflow/test-import.workflow.list

Lines changed: 0 additions & 8 deletions
This file was deleted.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# test-local-import.workflow.list
2+
# Purpose: Test local import caching
3+
4+
- (start) Test Local Import
5+
# you see: <project root:.imports/6b30743e0e2cbec3.workflow.list> <sha256:6b30743e0e2cbec36c0c98c0fd27814a8ba6fd4a73aed0e40d020111cc199994>
6+
import: ./main.workflow.list
7+
8+
- End
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# test-remote-import.workflow.list
2+
# Purpose: Test remote URL import caching
3+
4+
- (start) Test Remote Import
5+
# you see: <project root:.imports/61726152b038af77.workflow.list> <sha256:61726152b038af779e07b68db164187111b6d02a6427db25c03fd987f4ac0c30>
6+
import: https://raw.githubusercontent.com/tracer-mohist/workflow-as-list/refs/heads/main/examples/git/commit.workflow.list
7+
8+
- End

0 commit comments

Comments
 (0)