Skip to content
Closed
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
4 changes: 4 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,7 @@
**Vulnerability:** The Rust backend (`apps/desktop/src-tauri/src/main.rs`) did not enforce a maximum URL length limit when processing YouTube URLs via `import_youtube_url`. While the frontend enforced `MAX_YOUTUBE_URL_LENGTH = 2000` via the input element, this could be bypassed by an attacker sending requests directly to the Tauri backend API, potentially causing a Denial of Service (DoS) due to unbounded URL parsing and regex matching.
**Learning:** Input validation must occur at the entry point of untrusted data on the backend, even if it is also validated on the frontend. Relying solely on frontend validation for constraints like string length can expose the backend to resource exhaustion vulnerabilities.
**Prevention:** Always enforce constraints like maximum length, format validation, and sanitization at the earliest possible point on the backend, typically at the API boundary, regardless of frontend safeguards.
## 2025-10-24 - Project ID path traversal guard validation approach
**Vulnerability:** Any project identifier that can reach a filesystem path join must be treated as untrusted.
**Learning:** Checking for substrings like `..` can erroneously block legitimate inputs like `my..id`.
**Prevention:** When validating identifiers like `projectId`, reject exact matches for `.` and `..`, and block any path separators (`/` and `\`), rather than blocking any string containing `..`.
Comment on lines +31 to +34
22 changes: 11 additions & 11 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion services/analysis-engine/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ dependencies = [
"librosa>=0.11.0",
"numba<0.67.0",
"numpy>=1.26",
"setuptools>=83.0.0",
"soundfile>=0.13.1",
"urllib3>=2.7.0",
"urllib3>=2.7.0",
"yt-dlp>=2026.6.9",
Comment on lines 14 to 18
]

Expand Down
3 changes: 3 additions & 0 deletions services/analysis-engine/src/bandscope_analysis/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,9 @@ def validate_analysis_job_request(payload: object) -> AnalysisJobRequest:

if not isinstance(project_id, str) or not project_id.strip():
raise ValueError("Invalid analysis job request: invalid field 'projectId'")
if project_id in {".", ".."} or "/" in project_id or "\\" in project_id:
logger.warning("Security: path traversal detected in projectId")
raise ValueError("Invalid analysis job request: path traversal detected in 'projectId'")
Comment on lines 279 to +283
if local_source is None:
raise ValueError("Invalid analysis job request: invalid field 'localSource'")
if not isinstance(local_source, dict):
Expand Down
49 changes: 47 additions & 2 deletions services/analysis-engine/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def test_validate_analysis_job_request_accepts_local_audio_payload() -> None:
assert validate_analysis_job_request(
{
"sourceKind": "local_audio",
"projectId": "project-1",
"projectId": "my..project",
"sourceLabel": "late-night-set.wav",
"roleFocus": ["bass-guitar", "lead-vocal"],
"localSource": {
Expand All @@ -69,7 +69,7 @@ def test_validate_analysis_job_request_accepts_local_audio_payload() -> None:
}
) == {
"sourceKind": "local_audio",
"projectId": "project-1",
"projectId": "my..project",
"sourceLabel": "late-night-set.wav",
"roleFocus": ["bass-guitar", "lead-vocal"],
"localSource": {
Expand Down Expand Up @@ -308,6 +308,51 @@ def test_validate_analysis_job_request_rejects_bad_payloads() -> None:
},
"path traversal",
),
(
{
"sourceKind": "local_audio",
"projectId": "..",
"sourceLabel": "Late Night Set",
"roleFocus": [],
"localSource": {
"sourcePath": "/tmp/a.wav",
"fileName": "a.wav",
"extension": "wav",
"fileSizeBytes": 1024000,
},
},
"path traversal detected in 'projectId'",
),
Comment on lines +311 to +325
Comment on lines +311 to +325
(
{
"sourceKind": "local_audio",
"projectId": "foo/bar",
"sourceLabel": "Late Night Set",
"roleFocus": [],
"localSource": {
"sourcePath": "/tmp/a.wav",
"fileName": "a.wav",
"extension": "wav",
"fileSizeBytes": 1024000,
},
},
"path traversal detected in 'projectId'",
),
(
{
"sourceKind": "local_audio",
"projectId": "foo\\bar",
"sourceLabel": "Late Night Set",
"roleFocus": [],
"localSource": {
"sourcePath": "/tmp/a.wav",
"fileName": "a.wav",
"extension": "wav",
"fileSizeBytes": 1024000,
},
},
"path traversal detected in 'projectId'",
),
]

for payload, message in cases:
Expand Down
Loading
Loading