From 4d35517d692897aa8b4c901677a0545dcaf7c53e Mon Sep 17 00:00:00 2001 From: Zayadul-huq-afnan Date: Thu, 5 Feb 2026 15:09:41 +0600 Subject: [PATCH 1/5] Changed the accessibility test action to save the report in the zeuz download folder. Added an endpoint to server the html report --- .../Sequential_Actions/common_functions.py | 31 +++++- server/serve_accessibility_report.py | 102 ++++++++++++++++++ 2 files changed, 128 insertions(+), 5 deletions(-) create mode 100644 server/serve_accessibility_report.py 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/serve_accessibility_report.py b/server/serve_accessibility_report.py new file mode 100644 index 00000000..2f276d04 --- /dev/null +++ b/server/serve_accessibility_report.py @@ -0,0 +1,102 @@ +""" +API endpoint for serving accessibility HTML reports from the node's filesystem. + +The node server runs on the same machine where test execution occurs, so it can directly +access and serve files from the local filesystem where accessibility reports are saved. +""" +from pathlib import Path +from fastapi import APIRouter, HTTPException +from fastapi.responses import FileResponse +from urllib.parse import unquote + +router = APIRouter(prefix="/debug/reports", tags=["debug-reports"]) + + +@router.get("/accessibility") +def serve_accessibility_report(file_path: str): + """ + Serve accessibility HTML report by full file path. + + This endpoint serves the HTML file directly from the node's filesystem where the + accessibility test was executed. The node server runs on the same machine as the + test execution, allowing direct file system access. + + **Endpoint:** + ``` + GET /api/v1/debug/reports/accessibility?file_path={full_path} + ``` + + **Example Request:** + ``` + GET http://127.0.0.1:18100/api/v1/debug/reports/accessibility?file_path=C%3A%2FUsers%2Fuser%2FZeuz_Python_Node%2FAutomationLog%2Fdebug_abc123%2Fsession_1%2FTEST-123%2Fzeuz_download_folder%2Faccessibility_report_example_com_20250120_143022.html + ``` + + **Example with decoded path:** + ``` + GET http://127.0.0.1:18100/api/v1/debug/reports/accessibility?file_path=C:/Users/user/Zeuz_Python_Node/AutomationLog/debug_abc123/session_1/TEST-123/zeuz_download_folder/accessibility_report_example_com_20250120_143022.html + ``` + + **Parameter:** + - `file_path` (query parameter, required): The full absolute path to the accessibility HTML report file + - Can be Windows path: `C:/Users/.../zeuz_download_folder/accessibility_report_*.html` + - Can be Unix path: `/home/user/.../zeuz_download_folder/accessibility_report_*.html` + - Should be URL-encoded if containing special characters + - The path is extracted from the log message: `"HTML report saved to zeuz_download_folder: {html_path}"` + + **File Location:** + The file is served from the machine where the ZeuZ node is running. The node server + has direct filesystem access to the AutomationLog directory where test artifacts + (including accessibility reports) are stored during test execution. + + **Returns:** + - `200 OK`: FileResponse with the HTML report content + - `400 Bad Request`: If path is not a file or not an HTML file + - `404 Not Found`: If the file doesn't exist + - `500 Internal Server Error`: If there's an error reading the file + + **Example Response:** + ``` + Content-Type: text/html + Content-Disposition: attachment; filename="accessibility_report_example_com_20250120_143022.html" + + + ... + ``` + """ + try: + # Decode URL-encoded path + decoded_path = unquote(file_path) + + # Convert to Path object (handles both Windows and Unix paths) + html_file = Path(decoded_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)}") From 8c39dec9c699b4eaa1fff81213116c2d4a61a240 Mon Sep 17 00:00:00 2001 From: Zayadul-huq-afnan Date: Tue, 17 Feb 2026 12:21:51 +0600 Subject: [PATCH 2/5] Added endpoint to serve the accessibility test report --- server/main.py | 2 ++ server/serve_accessibility_report.py | 53 ++-------------------------- 2 files changed, 4 insertions(+), 51 deletions(-) diff --git a/server/main.py b/server/main.py index 96213cfd..36be49ab 100644 --- a/server/main.py +++ b/server/main.py @@ -11,6 +11,7 @@ from server.mobile import router as mobile_router, upload_android_ui_dump 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 import asyncio class EndpointFilter(logging.Filter): @@ -42,6 +43,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) app = FastAPI() app.include_router(v1router) diff --git a/server/serve_accessibility_report.py b/server/serve_accessibility_report.py index 2f276d04..25908e5b 100644 --- a/server/serve_accessibility_report.py +++ b/server/serve_accessibility_report.py @@ -1,9 +1,7 @@ """ API endpoint for serving accessibility HTML reports from the node's filesystem. - -The node server runs on the same machine where test execution occurs, so it can directly -access and serve files from the local filesystem where accessibility reports are saved. """ + from pathlib import Path from fastapi import APIRouter, HTTPException from fastapi.responses import FileResponse @@ -14,57 +12,10 @@ @router.get("/accessibility") def serve_accessibility_report(file_path: str): - """ - Serve accessibility HTML report by full file path. - - This endpoint serves the HTML file directly from the node's filesystem where the - accessibility test was executed. The node server runs on the same machine as the - test execution, allowing direct file system access. - - **Endpoint:** - ``` - GET /api/v1/debug/reports/accessibility?file_path={full_path} - ``` - - **Example Request:** - ``` - GET http://127.0.0.1:18100/api/v1/debug/reports/accessibility?file_path=C%3A%2FUsers%2Fuser%2FZeuz_Python_Node%2FAutomationLog%2Fdebug_abc123%2Fsession_1%2FTEST-123%2Fzeuz_download_folder%2Faccessibility_report_example_com_20250120_143022.html - ``` - - **Example with decoded path:** - ``` - GET http://127.0.0.1:18100/api/v1/debug/reports/accessibility?file_path=C:/Users/user/Zeuz_Python_Node/AutomationLog/debug_abc123/session_1/TEST-123/zeuz_download_folder/accessibility_report_example_com_20250120_143022.html - ``` - - **Parameter:** - - `file_path` (query parameter, required): The full absolute path to the accessibility HTML report file - - Can be Windows path: `C:/Users/.../zeuz_download_folder/accessibility_report_*.html` - - Can be Unix path: `/home/user/.../zeuz_download_folder/accessibility_report_*.html` - - Should be URL-encoded if containing special characters - - The path is extracted from the log message: `"HTML report saved to zeuz_download_folder: {html_path}"` - - **File Location:** - The file is served from the machine where the ZeuZ node is running. The node server - has direct filesystem access to the AutomationLog directory where test artifacts - (including accessibility reports) are stored during test execution. - - **Returns:** - - `200 OK`: FileResponse with the HTML report content - - `400 Bad Request`: If path is not a file or not an HTML file - - `404 Not Found`: If the file doesn't exist - - `500 Internal Server Error`: If there's an error reading the file - - **Example Response:** - ``` - Content-Type: text/html - Content-Disposition: attachment; filename="accessibility_report_example_com_20250120_143022.html" - - ... - ``` - """ try: # Decode URL-encoded path + print("Serving accessi file") decoded_path = unquote(file_path) # Convert to Path object (handles both Windows and Unix paths) From 28a283490ecc34335abc9b90fb568666d7737a0a Mon Sep 17 00:00:00 2001 From: Zayadul-huq-afnan <76451807+Zayadul-huq-afnan@users.noreply.github.com> Date: Thu, 30 Apr 2026 13:53:46 +0600 Subject: [PATCH 3/5] Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- server/serve_accessibility_report.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/server/serve_accessibility_report.py b/server/serve_accessibility_report.py index 25908e5b..afbbd7fe 100644 --- a/server/serve_accessibility_report.py +++ b/server/serve_accessibility_report.py @@ -8,6 +8,7 @@ from urllib.parse import unquote router = APIRouter(prefix="/debug/reports", tags=["debug-reports"]) +REPORTS_BASE_DIR = Path("reports/accessibility").resolve() @router.get("/accessibility") @@ -17,9 +18,16 @@ def serve_accessibility_report(file_path: str): # Decode URL-encoded path print("Serving accessi file") decoded_path = unquote(file_path) - - # Convert to Path object (handles both Windows and Unix paths) - html_file = Path(decoded_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(): From 2c5eb335eedfacd9230b7c22b3f9242c2785d006 Mon Sep 17 00:00:00 2001 From: Zayadul-huq-afnan <76451807+Zayadul-huq-afnan@users.noreply.github.com> Date: Thu, 30 Apr 2026 13:58:21 +0600 Subject: [PATCH 4/5] Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- server/serve_accessibility_report.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/server/serve_accessibility_report.py b/server/serve_accessibility_report.py index afbbd7fe..1ee87991 100644 --- a/server/serve_accessibility_report.py +++ b/server/serve_accessibility_report.py @@ -18,9 +18,17 @@ def serve_accessibility_report(file_path: str): # Decode URL-encoded path print("Serving accessi file") decoded_path = unquote(file_path) + requested_path = Path(decoded_path) + + # Reject absolute paths and traversal attempts before joining + if requested_path.is_absolute() or ".." in requested_path.parts: + 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() + html_file = (REPORTS_BASE_DIR / requested_path).resolve() try: html_file.relative_to(REPORTS_BASE_DIR) except ValueError: From 0c9238df4be700422c2c7c19dcc44c4ece90344b Mon Sep 17 00:00:00 2001 From: Zayadul-huq-afnan <76451807+Zayadul-huq-afnan@users.noreply.github.com> Date: Thu, 30 Apr 2026 14:01:18 +0600 Subject: [PATCH 5/5] Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- server/serve_accessibility_report.py | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/server/serve_accessibility_report.py b/server/serve_accessibility_report.py index 1ee87991..59da8d86 100644 --- a/server/serve_accessibility_report.py +++ b/server/serve_accessibility_report.py @@ -3,12 +3,14 @@ """ 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") @@ -17,18 +19,31 @@ def serve_accessibility_report(file_path: str): try: # Decode URL-encoded path print("Serving accessi file") - decoded_path = unquote(file_path) - requested_path = Path(decoded_path) + decoded_path = unquote(file_path).strip() - # Reject absolute paths and traversal attempts before joining - if requested_path.is_absolute() or ".." in requested_path.parts: + # 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 / requested_path).resolve() + html_file = (REPORTS_BASE_DIR / decoded_path).resolve() try: html_file.relative_to(REPORTS_BASE_DIR) except ValueError: