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

158 lines
4.9 KiB
Python
Executable File

#!/usr/bin/env python3
"""
Pre-commit hook to enforce safe_requests usage for SSRF protection.
This prevents direct usage of requests.get/post/Session which bypasses
SSRF validation. Use safe_get/safe_post/SafeSession from the security module.
See: https://cwe.mitre.org/data/definitions/918.html (CWE-918: SSRF)
"""
import ast
import sys
# Files/patterns where direct requests usage is allowed
ALLOWED_PATTERNS = {
"security/safe_requests.py", # The wrapper itself must use raw requests
"tests/", # Test files may need to mock/test raw requests
"test_", # Test files
"_test.py", # Test files
"examples/", # Example scripts run client-side, not server-side
}
# HTTP methods that should use safe_get/safe_post
UNSAFE_METHODS = {"get", "post", "put", "delete", "patch", "head", "options"}
class RequestsChecker(ast.NodeVisitor):
"""AST visitor to detect unsafe requests usage."""
def __init__(self, filename: str):
self.filename = filename
self.errors = []
def visit_Call(self, node):
# Skip if file is in allowed list
if self._is_file_allowed():
return self.generic_visit(node)
# Pattern 1: requests.get(), requests.post(), etc.
if (
isinstance(node.func, ast.Attribute)
and node.func.attr in UNSAFE_METHODS
and isinstance(node.func.value, ast.Name)
and node.func.value.id == "requests"
):
method = node.func.attr
if method in ("get", "post"):
safe_method = f"safe_{method}"
suggestion = (
f"Use {safe_method}() from security module instead."
)
else:
suggestion = (
"Use SafeSession() from security module for HTTP requests."
)
self.errors.append(
(
node.lineno,
f"Direct requests.{method}() bypasses SSRF protection. "
f"{suggestion}",
)
)
# Pattern 2: requests.Session()
elif (
isinstance(node.func, ast.Attribute)
and node.func.attr == "Session"
and isinstance(node.func.value, ast.Name)
and node.func.value.id == "requests"
):
self.errors.append(
(
node.lineno,
"Direct requests.Session() bypasses SSRF protection. "
"Use SafeSession() from security module instead.",
)
)
self.generic_visit(node)
def _is_file_allowed(self) -> bool:
"""Check if this file is allowed to use requests directly."""
for pattern in ALLOWED_PATTERNS:
if pattern in self.filename:
return True
return False
def check_file(filename: str) -> bool:
"""Check a single Python file for unsafe requests usage."""
if not filename.endswith(".py"):
return True
try:
with open(filename, "r", encoding="utf-8") as f:
content = f.read()
except Exception as e:
print(f"Error reading {filename}: {e}")
return False
try:
tree = ast.parse(content, filename=filename)
checker = RequestsChecker(filename)
checker.visit(tree)
if checker.errors:
print(f"\n{filename}:")
for line_num, error in checker.errors:
print(f" Line {line_num}: {error}")
return False
except SyntaxError:
# Skip files with syntax errors (let other tools handle that)
pass
except Exception as e:
print(f"Error parsing {filename}: {e}")
return False
return True
def main():
"""Main function to check all provided Python files."""
if len(sys.argv) < 2:
print("Usage: check-safe-requests.py <file1> <file2> ...")
sys.exit(1)
files_to_check = sys.argv[1:]
has_errors = False
for filename in files_to_check:
if not check_file(filename):
has_errors = True
if has_errors:
print("\n" + "=" * 70)
print("SSRF Protection: Unsafe requests usage detected!")
print("=" * 70)
print("\nTo fix, replace direct requests calls with safe alternatives:")
print(" - requests.get() -> safe_get()")
print(" - requests.post() -> safe_post()")
print(" - requests.Session() -> SafeSession()")
print("\nImport from security module:")
print(" from ...security import safe_get, safe_post, SafeSession")
print("\nFor localhost/internal services, use:")
print(" safe_get(url, allow_localhost=True)")
print(" safe_get(url, allow_private_ips=True)")
print("\nSee: https://cwe.mitre.org/data/definitions/918.html")
sys.exit(1)
else:
sys.exit(0)
if __name__ == "__main__":
main()