", _html(), re.S)
+ assert scripts, "no inline ",
+ "vbscript:msgbox(1)",
+ ],
+)
+def test_safe_url_blocks_dangerous_schemes(payload):
+ """safeUrl() must fall back instead of letting a non-http(s) scheme through.
+
+ This is the gap the issue's collaborator explicitly flagged: entity
+ escaping alone does not stop a `javascript:` URL from reaching
+ setAttribute for an href/src sink.
+ """
+ node_src = (
+ _extract_escape_html_fn()
+ + "\n"
+ + _extract_safe_url_fn()
+ + f"\nconsole.log(JSON.stringify(safeUrl({payload!r})));\n"
+ )
+ result = subprocess.run(
+ ["node", "-e", node_src], capture_output=True, text=True, timeout=30
+ )
+ assert result.returncode == 0, result.stderr
+ returned = result.stdout.strip()
+ assert payload not in returned
+ assert returned == '"#"'
+
+
+@pytest.mark.skipif(not _has_node(), reason="node is required to execute the template's JS")
+@pytest.mark.parametrize(
+ "payload",
+ [
+ "https://www.linkedin.com/in/example",
+ "http://www.linkedin.com/in/example",
+ ],
+)
+def test_safe_url_allows_http_and_https(payload):
+ node_src = (
+ _extract_escape_html_fn()
+ + "\n"
+ + _extract_safe_url_fn()
+ + f"\nconsole.log(JSON.stringify(safeUrl({payload!r})));\n"
+ )
+ result = subprocess.run(
+ ["node", "-e", node_src], capture_output=True, text=True, timeout=30
+ )
+ assert result.returncode == 0, result.stderr
+ assert result.stdout.strip() == f'"{payload}"'
+
+
+# Each entry is a snippet that must be present in the fixed template, taken
+# from one of the five sinks the issue reported. Checking out the pre-fix
+# source (HEAD~1) for this file makes every one of these assertions fail,
+# since escapeHtml() did not exist and these fields were interpolated raw.
+EXPECTED_ESCAPED_SNIPPETS = [
+ # 1. Company list (was: ${n.name}, ${n.industry ...}, ${n.about ...}, ${n.handle})
+ '