Skip to content
1 change: 1 addition & 0 deletions services/analysis-engine/src/bandscope_analysis/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@ def validate_analysis_job_request(payload: object) -> AnalysisJobRequest:
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("/"):
logger.warning("Security: path traversal detected in localSource.sourcePath")
raise ValueError(
"Invalid analysis job request: path traversal detected in 'localSource.sourcePath'"
)
Expand Down
25 changes: 25 additions & 0 deletions services/analysis-engine/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -1419,3 +1419,28 @@ def _slow_separate(_source_path: str) -> dict[str, object]:
update.get("progressLabel") == "Stem separation timed out; continuing with fallback cues"
for update in updates
)


def test_validate_analysis_job_request_logs_traversal_attempts_source_path() -> None:
"""Ensure path traversal attempts are logged securely, avoiding log forging."""
with patch("bandscope_analysis.api.logger.warning") as mock_logger:
malicious_payload = {
"sourceKind": "local_audio",
"projectId": "my-project",
"sourceLabel": "Late Night Set",
"roleFocus": [],
"localSource": {
"sourcePath": "/Users/test/../Music/late-night-set.wav",
"fileName": "late-night-set.wav",
"extension": "wav",
"fileSizeBytes": 1024000,
},
}
try:
validate_analysis_job_request(malicious_payload)
except ValueError:
pass

mock_logger.assert_called_once_with(
"Security: path traversal detected in localSource.sourcePath"
)
Loading