|
11 | 11 | from basic_memory.mcp.project_session import get_active_project |
12 | 12 | from basic_memory.schemas import EntityResponse |
13 | 13 | from basic_memory.schemas.project_info import ProjectList |
| 14 | +from basic_memory.utils import validate_project_path |
14 | 15 |
|
15 | 16 |
|
16 | 17 | async def _detect_cross_project_move_attempt( |
@@ -393,6 +394,28 @@ async def move_note( |
393 | 394 | active_project = get_active_project(project) |
394 | 395 | project_url = active_project.project_url |
395 | 396 |
|
| 397 | + # Validate destination path to prevent path traversal attacks |
| 398 | + project_path = active_project.home |
| 399 | + if not validate_project_path(destination_path, project_path): |
| 400 | + logger.warning( |
| 401 | + "Attempted path traversal attack blocked", |
| 402 | + destination_path=destination_path, |
| 403 | + project=active_project.name, |
| 404 | + ) |
| 405 | + return f"""# Move Failed - Security Validation Error |
| 406 | +
|
| 407 | +The destination path '{destination_path}' is not allowed - paths must stay within project boundaries. |
| 408 | +
|
| 409 | +## Valid path examples: |
| 410 | +- `notes/my-file.md` |
| 411 | +- `projects/2025/meeting-notes.md` |
| 412 | +- `archive/old-notes.md` |
| 413 | +
|
| 414 | +## Try again with a safe path: |
| 415 | +``` |
| 416 | +move_note("{identifier}", "notes/{destination_path.split("/")[-1] if "/" in destination_path else destination_path}") |
| 417 | +```""" |
| 418 | + |
396 | 419 | # Check for potential cross-project move attempts |
397 | 420 | cross_project_error = await _detect_cross_project_move_attempt( |
398 | 421 | identifier, destination_path, active_project.name |
|
0 commit comments