From 821594f617227e490fd0fb7673f0bde40c22722e Mon Sep 17 00:00:00 2001 From: "Jokob @NetAlertX" <96159884+jokob-sk@users.noreply.github.com> Date: Thu, 21 May 2026 00:36:59 +0000 Subject: [PATCH] Refine documentation and enhance XSS prevention measures in device details and test scripts --- docs/ADVISORY_MULTI_NETWORK.md | 4 +-- front/deviceDetailsEdit.php | 2 +- test/ui/test_ui_xss_devname.py | 55 +++++++++++++++++++++------------- 3 files changed, 37 insertions(+), 24 deletions(-) diff --git a/docs/ADVISORY_MULTI_NETWORK.md b/docs/ADVISORY_MULTI_NETWORK.md index c26a4f6e..b56f58ae 100644 --- a/docs/ADVISORY_MULTI_NETWORK.md +++ b/docs/ADVISORY_MULTI_NETWORK.md @@ -18,7 +18,7 @@ Effective multi-network monitoring starts with understanding how NetAlertX "sees ### 2. Automating IT Asset Inventory with Workflows -[Workflows](./WORKFLOWS.md) are the "engine" of NetAlertX, reducing manual overhead as your device list grows. See some examples below.: +[Workflows](./WORKFLOWS.md) are the "engine" of NetAlertX, reducing manual overhead as your device list grows. See some examples below: #### A. Logical Ownership & VLAN Tagging @@ -63,7 +63,7 @@ Use conditional logic to categorize devices. ``` #### C. Sync Node Tracking -When using multiple instances, ensure all synchub nodes have a descriptive `SYNC_node_name` name to distinguish between sites. +When using multiple instances, ensure all sync hub nodes have a descriptive `SYNC_node_name` name to distinguish between sites. > [!TIP] > Always test new workflows in a "Staging" instance. A misconfigured workflow can trigger thousands of unintended updates across your database. diff --git a/front/deviceDetailsEdit.php b/front/deviceDetailsEdit.php index 99f22bf6..6c70ac4b 100755 --- a/front/deviceDetailsEdit.php +++ b/front/deviceDetailsEdit.php @@ -333,7 +333,7 @@ function getDeviceData() { // XSS prevention - encode special characters for string fields, but not for arrays (like children dynamic) // Don't move above the handle devChildrenDynamic block because it relies on the original fieldData to generate options fieldData = encodeSpecialChars(fieldData); - console.log(fieldData); + // console.log(fieldData); // Generate the input field HTML const inputFormHtml = `
diff --git a/test/ui/test_ui_xss_devname.py b/test/ui/test_ui_xss_devname.py index ca7a4953..0832c2c7 100644 --- a/test/ui/test_ui_xss_devname.py +++ b/test/ui/test_ui_xss_devname.py @@ -126,18 +126,37 @@ def _delete_xss_device(token: str): ) -def _reset_canary(driver): - """Inject canary reset so we start each navigation clean.""" - driver.execute_script("window.__xss_canary = false;") - - def _read_canary(driver) -> bool: """Return True if the XSS canary was tripped.""" return bool(driver.execute_script("return window.__xss_canary || false;")) +def _navigate_with_canary(driver, url, timeout=15): + """Navigate to *url* with window.__xss_canary pre-initialised to false. + + Uses CDP Page.addScriptToEvaluateOnNewDocument so the canary is set + *before* any page script runs. If we reset it after driver.get() instead, + a payload that fires during page load would set the canary and we would + immediately clear the evidence. + """ + result = driver.execute_cdp_cmd( + "Page.addScriptToEvaluateOnNewDocument", + {"source": "window.__xss_canary = false;"}, + ) + script_id = result.get("identifier") + try: + driver.get(url) + wait_for_page_load(driver, timeout=timeout) + finally: + if script_id: + driver.execute_cdp_cmd( + "Page.removeScriptToEvaluateOnNewDocument", + {"identifier": script_id}, + ) + + def _force_cache_refresh(driver): - """Navigate to /devices.php once to warm the localStorage cache, then go back.""" + """Clear localStorage to force a fresh device-list fetch on next page load.""" driver.execute_script("if(window.localStorage){ localStorage.clear(); }") @@ -198,10 +217,9 @@ def test_devname_xss_not_executed(xss_driver, path, description): """ driver = xss_driver - # Navigate and reset canary before the page runs any JS - driver.get(f"{BASE_URL}{path}") - _reset_canary(driver) # reset AFTER page load (page may have set it) - wait_for_page_load(driver, timeout=15) + # Pre-initialise canary via CDP so it is false before any page JS runs. + # Resetting after driver.get() would erase evidence of early-firing payloads. + _navigate_with_canary(driver, f"{BASE_URL}{path}") # Give async JS (DataTables, FullCalendar, network tree) time to render time.sleep(ASYNC_WAIT_S) @@ -261,10 +279,11 @@ def test_devname_xss_device_shows_as_escaped_text(xss_driver): # The body text should contain the literal payload characters as plain text body_text = driver.find_element(By.TAG_NAME, "body").text - # If the device name is visible at all, it must show as text, not fire XSS if "onerror" in body_text: - # Good: the word 'onerror' appears as visible text, not as an attribute - assert "window.__xss_canary" not in driver.execute_script("return ''") or True + # Payload is visible as literal text — confirm it was not also executed + assert not driver.execute_script("return window.__xss_canary || false"), ( + "XSS canary fired even though payload appeared as visible text on devices.php" + ) # No with src=x should have been injected by the XSS payload injected_imgs = driver.find_elements(By.CSS_SELECTOR, "img[src='x']") @@ -297,14 +316,8 @@ def test_devname_attribute_break_xss_not_executed(xss_driver, xss_token): if resp.status_code not in (200, 201): pytest.skip(f"Could not create attribute-break XSS device (HTTP {resp.status_code})") - # Warm cache - xss_driver.get(f"{BASE_URL}/devices.php") - wait_for_page_load(driver=xss_driver, timeout=15) - time.sleep(ASYNC_WAIT_S) - - _reset_canary(xss_driver) - xss_driver.get(f"{BASE_URL}/devices.php") - wait_for_page_load(driver=xss_driver, timeout=15) + # Pre-set canary via CDP, then navigate — same pattern as main tests. + _navigate_with_canary(xss_driver, f"{BASE_URL}/devices.php") time.sleep(ASYNC_WAIT_S) fired = _read_canary(xss_driver)