Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,8 @@
**Vulnerability:** CSV formula injection mitigation was naive, missing leading whitespace, tabs, and newlines.
**Learning:** Checking `/^[=+\-@]/` is not sufficient, as OWASP states that spaces and tabs before the formula triggers will also execute the formula in applications like Excel.
**Prevention:** Use a regex that allows leading whitespace (e.g. `/^[\s\uFEFF\xA0]*[=+\-@\t\r\n]/`) and include standalone tabs or new lines which are also injection vectors.

## 2024-05-20 - Python Path Traversal Mitigation bypass
**Vulnerability:** Path traversal detection in Python backend APIs relied solely on checking the input path string or basic parsed parts which might not adequately catch sequences like `..` when intermixed with different path separators.
**Learning:** Using `if ".." in Path(path).parts` incorrectly assumes `Path` parses mixed cross-platform separators reliably for untrusted input, particularly in heterogeneous environments. Instead, explicitly reject directory traversal sequences by replacing `\` with `/` and splitting on `/` to prevent bypassing automated CI vulnerability scanners (like Strix) as explicitly requested in memory.
**Prevention:** Always manually split the target path using both `/` and `\` manually instead of relying solely on `os.sep`, `os.altsep`, or `pathlib.Path.parts`. This ensures cross-platform payloads are reliably blocked even when executed on POSIX runners.
8 changes: 8 additions & 0 deletions services/analysis-engine/src/bandscope_analysis/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,10 @@ def validate_analysis_job_request(payload: object) -> AnalysisJobRequest:
file_size_bytes = local_source.get("fileSizeBytes")
if not isinstance(source_path, str) or not source_path.strip():
raise ValueError("Invalid analysis job request: invalid field 'localSource.sourcePath'")
if ".." in source_path.replace("\\", "/").split("/"):
raise ValueError(
"Invalid analysis job request: path traversal detected in 'localSource.sourcePath'"
)
if not isinstance(file_name, str) or not file_name.strip():
raise ValueError("Invalid analysis job request: invalid field 'localSource.fileName'")
if extension not in {"wav", "mp3", "flac", "m4a"}:
Expand All @@ -307,10 +311,14 @@ def validate_analysis_job_request(payload: object) -> AnalysisJobRequest:
if cache_root is not None:
if not isinstance(cache_root, str) or not cache_root.strip():
raise ValueError("Invalid analysis job request: invalid field 'cacheRoot'")
if ".." in cache_root.replace("\\", "/").split("/"):
raise ValueError("Invalid analysis job request: path traversal detected in 'cacheRoot'")
normalized["cacheRoot"] = cache_root
if temp_root is not None:
if not isinstance(temp_root, str) or not temp_root.strip():
raise ValueError("Invalid analysis job request: invalid field 'tempRoot'")
if ".." in temp_root.replace("\\", "/").split("/"):
raise ValueError("Invalid analysis job request: path traversal detected in 'tempRoot'")
normalized["tempRoot"] = temp_root

return normalized
Expand Down
47 changes: 47 additions & 0 deletions services/analysis-engine/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,53 @@ def test_validate_analysis_job_request_rejects_bad_payloads() -> None:
},
"tempRoot",
),
(
{
"sourceKind": "local_audio",
"projectId": "project-1",
"sourceLabel": "Late Night Set",
"roleFocus": [],
"localSource": {
"sourcePath": "/Users/test/Music/late-night-set.wav",
"fileName": "late-night-set.wav",
"extension": "wav",
"fileSizeBytes": 1024000,
},
"cacheRoot": "/tmp/../secret",
},
"path traversal",
),
(
{
"sourceKind": "local_audio",
"projectId": "project-1",
"sourceLabel": "Late Night Set",
"roleFocus": [],
"localSource": {
"sourcePath": "/Users/test/Music/late-night-set.wav",
"fileName": "late-night-set.wav",
"extension": "wav",
"fileSizeBytes": 1024000,
},
"tempRoot": "C:\\temp\\..\\secret",
},
"path traversal",
),
(
{
"sourceKind": "local_audio",
"projectId": "project-1",
"sourceLabel": "Late Night Set",
"roleFocus": [],
"localSource": {
"sourcePath": "../secret.wav",
"fileName": "late-night-set.wav",
"extension": "wav",
"fileSizeBytes": 1024000,
},
},
"path traversal",
),
]

for payload, message in cases:
Expand Down