Files
wehub-resource-sync 7a0da7932b
OSV-Scanner (Scheduled) / scan-scheduled (push) Failing after 0s
Create Release / test-gate (push) Has been cancelled
Create Release / release-gate (push) Has been cancelled
Create Release / ci-gate (push) Has been cancelled
Create Release / version-check (push) Has been cancelled
Create Release / e2e-test-gate (push) Has been cancelled
Create Release / responsive-test-gate (push) Has been cancelled
Create Release / compat-test-gate (push) Has been cancelled
Create Release / compose-integration-gate (push) Has been cancelled
Create Release / vulture-gate (push) Has been cancelled
Create Release / build (push) Has been cancelled
Create Release / provenance (push) Has been cancelled
Create Release / prerelease-docker (push) Has been cancelled
Create Release / publish-docker (push) Has been cancelled
Create Release / create-release (push) Has been cancelled
Create Release / cleanup-changelog (push) Has been cancelled
Create Release / trigger-pypi (push) Has been cancelled
Create Release / monitor-pypi (push) Has been cancelled
Create Release / Clean up orphan prerelease tags and signatures (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Has been cancelled
Docker Tests (Consolidated) / Accessibility Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Unit Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Example Tests (push) Has been cancelled
Docker Tests (Consolidated) / Production Image Smoke Test (push) Has been cancelled
Docker Tests (Consolidated) / Infrastructure Tests (push) Has been cancelled
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Has been cancelled
Backwards Compatibility / Verify Encryption Constants (push) Has been cancelled
Backwards Compatibility / PyPI Version Compatibility (push) Has been cancelled
Backwards Compatibility / Database Migration Tests (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Docker Tests (Consolidated) / detect-changes (push) Has been cancelled
Docker Tests (Consolidated) / Build Test Image (push) Has been cancelled
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:08:55 +08:00

149 lines
4.4 KiB
Python
Executable File

#!/usr/bin/env python3
"""
Pre-commit hook to detect double-escaping in JavaScript files.
Catches patterns where escapeHtml() is called on values that are then
passed to functions which already escape internally (showError, showSuccess,
showInfo, showAlert).
Known limitations:
- Multi-line function calls are not detected (line-by-line processing).
- Variable indirection bypasses detection
(e.g., ``const safe = escapeHtml(msg); showError(safe);``).
"""
import re
import sys
# Functions that escape their arguments internally
ESCAPING_FUNCTIONS = ["showError", "showSuccess", "showInfo", "showAlert"]
# Build pattern: showError(...escapeHtml(...)...)
PATTERN = re.compile(
r"\b(" + "|".join(ESCAPING_FUNCTIONS) + r")\s*\("
r".*?"
r"(?:escapeHtml|XSSProtection\.escapeHtml)\s*\("
)
def strip_js_comments(line, in_block_comment):
"""Strip JavaScript comments from a line, respecting string literals.
Handles ``//`` line comments, ``/* */`` block comments (including
multi-line), and correctly ignores comment-like sequences inside
single-quoted, double-quoted, and template-literal strings.
Returns ``(code_without_comments, still_in_block_comment)``.
"""
result = []
i = 0
in_single_quote = False
in_double_quote = False
in_template = False
while i < len(line):
ch = line[i]
# Inside a block comment — scan for */
if in_block_comment:
if ch == "*" and i + 1 < len(line) and line[i + 1] == "/":
in_block_comment = False
i += 2
else:
i += 1
continue
# Handle escape sequences inside strings
if (in_single_quote or in_double_quote or in_template) and ch == "\\":
result.append(ch)
if i + 1 < len(line):
result.append(line[i + 1])
i += 2
else:
i += 1
continue
# Track string state
if ch == "'" and not in_double_quote and not in_template:
in_single_quote = not in_single_quote
elif ch == '"' and not in_single_quote and not in_template:
in_double_quote = not in_double_quote
elif ch == "`" and not in_single_quote and not in_double_quote:
in_template = not in_template
elif not in_single_quote and not in_double_quote and not in_template:
# Outside strings — check for comments
if ch == "/" and i + 1 < len(line):
if line[i + 1] == "/":
break # Line comment — rest of line is comment
if line[i + 1] == "*":
in_block_comment = True
i += 2
continue
result.append(ch)
i += 1
return "".join(result), in_block_comment
def check_file(file_path):
"""Check a JavaScript file for double-escaping patterns."""
issues = []
try:
with open(file_path, "r", encoding="utf-8") as f:
lines = f.readlines()
except (UnicodeDecodeError, IOError):
# Skip binary files or files we can't read
return []
in_block_comment = False
for line_num, line in enumerate(lines, 1):
code, in_block_comment = strip_js_comments(line, in_block_comment)
if PATTERN.search(code):
issues.append(
f"{file_path}:{line_num}: Possible double-escaping — "
f"escapeHtml() inside a function that already escapes: "
f"{line.strip()}"
)
return issues
def main():
"""Main function to check all provided files."""
if len(sys.argv) < 2:
print("No files to check")
return 0
all_issues = []
for file_path in sys.argv[1:]:
if not file_path.endswith(".js"):
continue
try:
issues = check_file(file_path)
all_issues.extend(issues)
except Exception as e:
print(f"Error checking {file_path}: {e}")
continue
if all_issues:
print("Double-escaping detected:")
print("-" * 60)
for issue in all_issues:
print(f" {issue}")
print("-" * 60)
print("\nThese functions already escape their arguments internally.")
print("Pass raw text instead of pre-escaping with escapeHtml().")
return 1
return 0
if __name__ == "__main__":
sys.exit(main())