Resolve file_path against root_dir_abs. - if file_path starts with 'projects/', resolve from ms-agent base dir - if file_path is absolute, use as-is - if relative, join(root_dir_abs, file_path)
(root_dir_abs: str, file_path: str)
| 590 | |
| 591 | |
| 592 | def resolve_file_path(root_dir_abs: str, file_path: str) -> str: |
| 593 | """ |
| 594 | Resolve file_path against root_dir_abs. |
| 595 | - if file_path starts with 'projects/', resolve from ms-agent base dir |
| 596 | - if file_path is absolute, use as-is |
| 597 | - if relative, join(root_dir_abs, file_path) |
| 598 | """ |
| 599 | root_dir_abs = os.path.normpath(os.path.abspath(root_dir_abs)) |
| 600 | |
| 601 | if os.path.isabs(file_path): |
| 602 | full_path = os.path.normpath(os.path.abspath(file_path)) |
| 603 | elif file_path.startswith('projects/'): |
| 604 | # Special case: if path starts with 'projects/', resolve from base_dir |
| 605 | # This handles: projects/code_genesis/output/config.js |
| 606 | base_dir = os.path.dirname( |
| 607 | os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) |
| 608 | full_path = os.path.normpath( |
| 609 | os.path.abspath(os.path.join(base_dir, file_path))) |
| 610 | else: |
| 611 | # Try multiple locations |
| 612 | base_dir = os.path.dirname( |
| 613 | os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) |
| 614 | |
| 615 | candidates = [ |
| 616 | # First try with root_dir_abs (for session-based access) |
| 617 | os.path.join(root_dir_abs, file_path), |
| 618 | # Then try in each project's output directory |
| 619 | ] |
| 620 | |
| 621 | # Search in project output directories |
| 622 | projects_dir = os.path.join(base_dir, 'projects') |
| 623 | if os.path.exists(projects_dir): |
| 624 | try: |
| 625 | for project_name in os.listdir(projects_dir): |
| 626 | project_path = os.path.join(projects_dir, project_name) |
| 627 | if os.path.isdir(project_path): |
| 628 | candidates.append( |
| 629 | os.path.join(project_path, 'output', file_path)) |
| 630 | except (OSError, PermissionError): |
| 631 | pass |
| 632 | |
| 633 | # Find first existing file |
| 634 | full_path = None |
| 635 | for candidate in candidates: |
| 636 | candidate = os.path.normpath(candidate) |
| 637 | if os.path.exists(candidate) and os.path.isfile(candidate): |
| 638 | full_path = candidate |
| 639 | break |
| 640 | |
| 641 | if not full_path: |
| 642 | # Default to first candidate if none found |
| 643 | full_path = os.path.normpath(candidates[0]) |
| 644 | |
| 645 | # Warning: Web UI is for local-only convenience (frontend/backend assumed localhost). |
| 646 | # For production, enforce strict backend file-access validation and authorization |
| 647 | # to prevent arbitrary path read/write (e.g., path traversal). |
| 648 | # TODO: Security check: ensure `full_path` is within configured allowed roots. |
| 649 |
no outgoing calls
no test coverage detected