""" Tests for the check-url-security pre-commit hook. Covers regressions for previously-missed patterns: - substring-based skip list (bare "test"/"spec") silently skipped production files such as attestation_service.js, latest_products.js, respectful_handler.js - `has_basic_protection` short-circuit was trivially satisfied by a comment mentioning "javascript:" plus any unrelated .includes()/.startsWith() """ import subprocess import sys import tempfile from pathlib import Path HOOK_SCRIPT = ( Path(__file__).parent.parent.parent / ".pre-commit-hooks" / "check-url-security.py" ) def _run_hook(content: str, filename: str) -> subprocess.CompletedProcess: """Write content to a temp file with a specific path and run the hook.""" with tempfile.TemporaryDirectory() as d: path = Path(d) / filename path.parent.mkdir(parents=True, exist_ok=True) path.write_text(content) return subprocess.run( [sys.executable, str(HOOK_SCRIPT), str(path)], capture_output=True, text=True, ) UNPROTECTED_EXTERNAL = ( 'fetch("https://api.example.com/data");\nelement.href = userProvidedUrl;\n' ) class TestSkipListDoesNotSilentlyExempt: """Files whose names merely contain 'test'/'spec' as substrings must be scanned.""" def test_attestation_service_is_scanned(self): """'attestation_service.js' contains substring 'test' but is not a test file.""" result = _run_hook(UNPROTECTED_EXTERNAL, "attestation_service.js") assert result.returncode == 1 assert "URL Security Issues" in result.stdout def test_latest_products_is_scanned(self): """'latest_products.js' contains substring 'test'.""" result = _run_hook(UNPROTECTED_EXTERNAL, "latest_products.js") assert result.returncode == 1 def test_respectful_is_scanned(self): """'respectful_handler.js' contains substring 'spec'.""" result = _run_hook(UNPROTECTED_EXTERNAL, "respectful_handler.js") assert result.returncode == 1 class TestSkipListStillExemptsRealTestFiles: """Legitimate test / vendor / build files must remain skipped.""" def test_test_prefix_skipped(self): result = _run_hook(UNPROTECTED_EXTERNAL, "test_foo.js") assert result.returncode == 0 def test_tests_directory_skipped(self): result = _run_hook(UNPROTECTED_EXTERNAL, "tests/helpers.js") assert result.returncode == 0 def test_dot_spec_suffix_skipped(self): result = _run_hook(UNPROTECTED_EXTERNAL, "foo.spec.js") assert result.returncode == 0 def test_dot_test_suffix_skipped(self): result = _run_hook(UNPROTECTED_EXTERNAL, "foo.test.js") assert result.returncode == 0 def test_vendor_directory_skipped(self): result = _run_hook(UNPROTECTED_EXTERNAL, "vendor/lib.js") assert result.returncode == 0 def test_node_modules_directory_skipped(self): result = _run_hook(UNPROTECTED_EXTERNAL, "node_modules/lodash/index.js") assert result.returncode == 0 def test_min_suffix_skipped(self): result = _run_hook(UNPROTECTED_EXTERNAL, "app.min.js") assert result.returncode == 0 class TestBasicProtectionShortCircuitRemoved: """The weak has_basic_protection short-circuit should no longer mask gaps.""" def test_comment_mentioning_javascript_and_unrelated_includes_does_not_bypass( self, ): """A comment mentioning 'javascript:' + any unrelated .includes() used to pass.""" content = ( "// Do not use javascript: schemes anywhere in this app.\n" 'if (items.includes("foo")) { doThing(); }\n' 'fetch("https://api.example.com/data");\n' "element.href = userUrl;\n" ) result = _run_hook(content, "app.js") assert result.returncode == 1 assert "URL validation" in result.stdout def test_file_with_real_urlvalidator_passes(self): """Files that actually import URLValidator still pass.""" content = ( 'import { URLValidator } from "./url-validator.js";\n' 'fetch("https://api.example.com/data");\n' 'URLValidator.safeAssign(element, "href", userUrl);\n' ) result = _run_hook(content, "app.js") assert result.returncode == 0 def test_file_with_no_external_urls_passes(self): """Files that don't handle external URLs should pass.""" content = 'element.textContent = "hello";\n' result = _run_hook(content, "app.js") assert result.returncode == 0