From 57bcec5a2e321110a9c7f76d9ab0b3770de1bed7 Mon Sep 17 00:00:00 2001 From: Adam Moussa <166072409+amoussa1229@users.noreply.github.com> Date: Tue, 12 May 2026 15:03:05 -0400 Subject: [PATCH] Harden WO web UI and fix JS-context XSS in both dashboards - Use json.dumps for onclick URLs to prevent JS string breakout - Add .lower() to WO render_badge color lookup matching PO pattern - Add pagination to get_comments query - Cap get_work_orders to 500 results matching PO pattern --- lambdas/po/web_ui/handler.py | 3 ++- lambdas/wo/web_ui/handler.py | 24 +++++++++++++++--------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/lambdas/po/web_ui/handler.py b/lambdas/po/web_ui/handler.py index a3b3b74..cbbfbaa 100644 --- a/lambdas/po/web_ui/handler.py +++ b/lambdas/po/web_ui/handler.py @@ -5,6 +5,7 @@ Serves a simple HTML dashboard for viewing purchase orders. Accessed via Lambda Function URL. """ +import json import os from decimal import Decimal from html import escape as esc @@ -195,7 +196,7 @@ def render_po_list(purchase_orders): processed = esc((po.get("processed_at") or "")[:16]) rows += f""" - + {po_number} {supplier} {site_code} diff --git a/lambdas/wo/web_ui/handler.py b/lambdas/wo/web_ui/handler.py index f8bb0a5..9ac07c1 100644 --- a/lambdas/wo/web_ui/handler.py +++ b/lambdas/wo/web_ui/handler.py @@ -5,6 +5,7 @@ Serves a simple HTML dashboard for viewing work orders and comments. Accessed via Lambda Function URL. """ +import json import os from html import escape as esc @@ -16,7 +17,7 @@ WORK_ORDERS_TABLE = os.environ.get("WORK_ORDERS_TABLE", "WorkOrders") COMMENTS_TABLE = os.environ.get("COMMENTS_TABLE", "WorkOrderComments") -def get_work_orders(): +def get_work_orders(limit=500): table = dynamodb.Table(WORK_ORDERS_TABLE) items = [] response = table.scan() @@ -25,16 +26,21 @@ def get_work_orders(): response = table.scan(ExclusiveStartKey=response["LastEvaluatedKey"]) items.extend(response.get("Items", [])) items.sort(key=lambda x: x.get("updated_at", ""), reverse=True) - return items + return items[:limit] def get_comments(work_order_id): table = dynamodb.Table(COMMENTS_TABLE) - response = table.query( - KeyConditionExpression="work_order_id = :woid", - ExpressionAttributeValues={":woid": work_order_id}, - ) - items = response.get("Items", []) + items = [] + kwargs = { + "KeyConditionExpression": "work_order_id = :woid", + "ExpressionAttributeValues": {":woid": work_order_id}, + } + response = table.query(**kwargs) + items.extend(response.get("Items", [])) + while "LastEvaluatedKey" in response: + response = table.query(**kwargs, ExclusiveStartKey=response["LastEvaluatedKey"]) + items.extend(response.get("Items", [])) items.sort(key=lambda x: x.get("created_at", ""), reverse=True) return items @@ -42,7 +48,7 @@ def get_comments(work_order_id): def render_badge(value, color_map): if not value: value = "unknown" - color = color_map.get(value, "#9ca3af") + color = color_map.get(value.lower(), "#9ca3af") label = esc(value.replace("_", " ").title()) return f'{label}' @@ -168,7 +174,7 @@ def render_work_orders_list(work_orders): updated = esc((wo.get("updated_at") or "")[:16]) rows += f""" - + {wo_id} {desc} {site}