diff --git a/Framework/Built_In_Automation/Sequential_Actions/common_functions.py b/Framework/Built_In_Automation/Sequential_Actions/common_functions.py index 347af46e..ed616398 100755 --- a/Framework/Built_In_Automation/Sequential_Actions/common_functions.py +++ b/Framework/Built_In_Automation/Sequential_Actions/common_functions.py @@ -7436,7 +7436,27 @@ def accessibility_test(data_set): - reports_dir = "Accessibility Test Report" + # Get zeuz_download_folder for saving reports (will be uploaded to server) + try: + if sr.Test_Shared_Variables("zeuz_download_folder"): + zeuz_download_folder = sr.Get_Shared_Variables("zeuz_download_folder") + reports_dir = str(Path(zeuz_download_folder)) + CommonUtil.ExecLog(sModuleInfo, f"Saving accessibility reports to zeuz_download_folder: {reports_dir}", 1) + else: + CommonUtil.ExecLog( + sModuleInfo, + "zeuz_download_folder not found. Using current directory for reports.", + 2, + ) + reports_dir = "Accessibility Test Report" + except Exception as e: + CommonUtil.ExecLog( + sModuleInfo, + f"Failed to get zeuz_download_folder: {str(e)}. Using current directory.", + 2, + ) + reports_dir = "Accessibility Test Report" + try: os.makedirs(reports_dir, exist_ok=True) CommonUtil.ExecLog(sModuleInfo, f"Reports directory created/verified: {reports_dir}", 1) @@ -7458,7 +7478,7 @@ def accessibility_test(data_set): json_path = os.path.join(reports_dir, json_filename) with open(json_path, 'w', encoding='utf-8') as f: json.dump(result, f, indent=2, ensure_ascii=False) - CommonUtil.ExecLog(sModuleInfo, f"Raw JSON report saved successfully to: {json_path}", 1) + CommonUtil.ExecLog(sModuleInfo, f"Raw JSON report saved to zeuz_download_folder: {json_path}", 1) except Exception as e: CommonUtil.ExecLog(sModuleInfo, f"Failed to save raw JSON report: {str(e)}", 3) return "zeuz_failed" @@ -7470,7 +7490,7 @@ def accessibility_test(data_set): summary_path = os.path.join(reports_dir, summary_filename) with open(summary_path, 'w', encoding='utf-8') as f: json.dump(summary, f, indent=2, ensure_ascii=False) - CommonUtil.ExecLog(sModuleInfo, f"Summary JSON report saved successfully to: {summary_path}", 1) + CommonUtil.ExecLog(sModuleInfo, f"Summary JSON report saved to zeuz_download_folder: {summary_path}", 1) except Exception as e: CommonUtil.ExecLog(sModuleInfo, f"Failed to create summary report: {str(e)}", 3) return "zeuz_failed" @@ -7482,7 +7502,7 @@ def accessibility_test(data_set): html_path = os.path.join(reports_dir, html_filename) with open(html_path, 'w', encoding='utf-8') as f: f.write(html_report) - CommonUtil.ExecLog(sModuleInfo, f"HTML report saved successfully to: {html_path}", 1) + CommonUtil.ExecLog(sModuleInfo, f"HTML report saved to zeuz_download_folder: {html_path}", 1) except Exception as e: CommonUtil.ExecLog(sModuleInfo, f"Failed to create HTML report: {str(e)}", 3) return "zeuz_failed" @@ -7494,11 +7514,12 @@ def accessibility_test(data_set): [V] Tests Passed: {summary['summary']['passes_count']} [!] Inapplicable: {summary['summary']['inapplicable_count']} [~] Incomplete: {summary['summary']['incomplete_count']} -Reports generated: +Reports generated and saved to zeuz_download_folder: - {json_filename} (raw data) - {summary_filename} (summary) - {html_filename} (visual report) Location: {reports_dir} + """ CommonUtil.ExecLog(sModuleInfo, summary_message, 5) diff --git a/server/main.py b/server/main.py index d0ccb2cf..561f40bd 100644 --- a/server/main.py +++ b/server/main.py @@ -11,6 +11,7 @@ from server.mobile import router as mobile_router, start_ui_dump_uploads from server.mac import router as mac_router from server.linux import router as linux_router +from server.serve_accessibility_report import router as debug_reports_router from server.windows import router as windows_router, upload_windows_ui_dump from server.installers import router as installers_router import asyncio @@ -44,6 +45,7 @@ def main() -> FastAPI: v1router.include_router(mobile_router) v1router.include_router(mac_router) v1router.include_router(linux_router) + v1router.include_router(debug_reports_router) v1router.include_router(windows_router) v1router.include_router(installers_router) app = FastAPI() diff --git a/server/serve_accessibility_report.py b/server/serve_accessibility_report.py new file mode 100644 index 00000000..59da8d86 --- /dev/null +++ b/server/serve_accessibility_report.py @@ -0,0 +1,84 @@ +""" +API endpoint for serving accessibility HTML reports from the node's filesystem. +""" + +from pathlib import Path +import re +from fastapi import APIRouter, HTTPException +from fastapi.responses import FileResponse +from urllib.parse import unquote + +router = APIRouter(prefix="/debug/reports", tags=["debug-reports"]) +REPORTS_BASE_DIR = Path("reports/accessibility").resolve() +SAFE_REPORT_FILENAME_RE = re.compile(r"^[A-Za-z0-9._-]+\.html$") + + +@router.get("/accessibility") +def serve_accessibility_report(file_path: str): + + try: + # Decode URL-encoded path + print("Serving accessi file") + decoded_path = unquote(file_path).strip() + + # Accept only a safe filename (no directory components) + if not decoded_path: + raise HTTPException( + status_code=400, + detail="Invalid report path" + ) + + # Reject any path separators or dot-directory tokens + if "/" in decoded_path or "\\" in decoded_path or decoded_path in {".", ".."}: + raise HTTPException( + status_code=400, + detail="Invalid report path" + ) + + # Ensure it is a basename and matches allowed characters + .html extension + if Path(decoded_path).name != decoded_path or not SAFE_REPORT_FILENAME_RE.match(decoded_path): + raise HTTPException( + status_code=400, + detail="Invalid report path" + ) + + # Resolve path under a fixed reports directory to prevent traversal + html_file = (REPORTS_BASE_DIR / decoded_path).resolve() + try: + html_file.relative_to(REPORTS_BASE_DIR) + except ValueError: + raise HTTPException( + status_code=400, + detail="Invalid report path" + ) + + # Security check: ensure file exists and is a file (not a directory) + if not html_file.exists(): + raise HTTPException( + status_code=404, + detail=f"Accessibility report file not found: {file_path}" + ) + + if not html_file.is_file(): + raise HTTPException( + status_code=400, + detail=f"Path is not a file: {file_path}" + ) + + # Verify it's an HTML file + if html_file.suffix.lower() != ".html": + raise HTTPException( + status_code=400, + detail=f"File is not an HTML file: {file_path}" + ) + + return FileResponse( + str(html_file), + media_type="text/html", + filename=html_file.name + ) + + except HTTPException: + raise + except Exception as e: + raise HTTPException(status_code=500, detail=f"Failed to retrieve accessibility report: {str(e)}")