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

235 lines
8.6 KiB
Python
Executable File

#!/usr/bin/env python3
"""Pre-commit guard: Bearer ``bearer:disable`` directives must be well-formed.
Bearer SILENTLY ignores a suppression directive (the finding stays open) unless
it is written exactly as:
* a line comment (``#`` in Python, ``//`` in JavaScript) on its OWN line,
directly above the statement — a *same-line* trailing directive
(``code() # bearer:disable rule``) is ignored; and
* the bare rule id(s) only, with NO trailing prose — ``# bearer:disable rule
-- why`` is ignored. Put the rationale on a separate comment line above the
bare directive; and
* NOT inside a block comment — Bearer ignores ``/* bearer:disable rule */``
and JSDoc ``* bearer:disable rule`` too.
Each failure mode is silent, so a malformed directive looks like protection
while suppressing nothing. This hook fails the commit when it finds one.
Implementation notes:
* Python uses ``tokenize`` — directives mentioned inside docstrings or string
literals are not flagged, only real ``#`` comments.
* JavaScript uses a small char scanner that tracks strings/templates and
block comments, so a ``//`` inside a string (e.g. a URL) is not mistaken
for a comment. Not special-cased (rare edges, accepted): regex literals,
and code inside template ``${...}`` interpolations (a directive written
inside ``${...}`` is treated as template text and would be missed) — no
real directive is written in either place.
"""
from __future__ import annotations
import io
import re
import sys
import tokenize
# A Bearer rule id is always namespaced with underscores, e.g.
# python_lang_sql_injection / javascript_lang_dangerous_insert_html — never a
# bare English word. Requiring an underscore stops lowercase prose
# ("because reasons") from masquerading as a rule id.
_RULE = r"[a-z][a-z0-9]*(?:_[a-z0-9]+)+"
# After `bearer:disable`: one or more rule ids, comma-separated only (Bearer's
# documented multi-rule syntax), and nothing else.
_VALID_AFTER = re.compile(rf"^[ \t]+{_RULE}(?:[ \t]*,[ \t]*{_RULE})*[ \t]*$")
# A Python `#` comment whose content begins with the directive.
_DIRECTIVE_START = re.compile(r"^#[ \t]*bearer:disable\b(.*)$")
# An embedded `# bearer:disable` (a directive trailing another comment).
_EMBEDDED = re.compile(r"#[ \t]*bearer:disable\b")
# A `// bearer:disable ...` line comment (used to read the bit after the rule).
_JS_LINE_DIRECTIVE = re.compile(r"^//[ \t]*bearer:disable\b(.*)$")
# A block-comment line that STARTS with the directive (after `/*` / JSDoc `*`),
# vs. one that merely mentions it in prose.
_BLOCK_DIRECTIVE_LINE = re.compile(
r"^[ \t]*(?:/\*+|\*+)?[ \t]*bearer:disable\b"
)
_SAME_LINE_MSG = (
"same-line `bearer:disable` is silently ignored by Bearer — put the bare "
"directive on its own line directly above the statement"
)
_TRAILING_MSG = (
"trailing text after the rule id is silently ignored by Bearer — keep the "
"directive line as the bare rule id and move the rationale to a separate "
"comment line above it"
)
_BLOCK_MSG = (
"`bearer:disable` in a block comment is silently ignored by Bearer — use a "
"line comment (// or #) on its own line directly above the statement"
)
_MISSING_MSG = "`bearer:disable` is missing a rule id"
def _after_violation(after: str) -> str | None:
"""Validate the text that follows ``bearer:disable``."""
if not after.strip():
return _MISSING_MSG
if not _VALID_AFTER.match(after):
return _TRAILING_MSG
return None
def _check_python(content: str) -> list[tuple[int, str]]:
errors: list[tuple[int, str]] = []
lines = content.splitlines()
try:
tokens = list(tokenize.generate_tokens(io.StringIO(content).readline))
except (tokenize.TokenError, IndentationError, SyntaxError):
return errors # malformed files are caught by other tools
for tok in tokens:
if tok.type != tokenize.COMMENT or "bearer:disable" not in tok.string:
continue
row, col = tok.start
code_before = (
lines[row - 1][:col].strip() if row - 1 < len(lines) else ""
)
m = _DIRECTIVE_START.match(tok.string)
if m:
if code_before:
errors.append((row, _SAME_LINE_MSG))
else:
msg = _after_violation(m.group(1))
if msg:
errors.append((row, msg))
elif code_before and _EMBEDDED.search(tok.string):
# A real directive trailing another comment on a code line, e.g.
# `run(q) # noqa: S608 # bearer:disable rule`.
errors.append((row, _SAME_LINE_MSG))
# else: a comment that only mentions the text in prose — not a directive.
return errors
def _classify_js_line_comment(
lineno: int, code_before: str, comment: str, errors: list[tuple[int, str]]
) -> None:
m = _JS_LINE_DIRECTIVE.match(comment)
if not m:
return # `// other prose ... bearer:disable ...` — not a directive
if code_before.strip():
errors.append((lineno, _SAME_LINE_MSG))
else:
msg = _after_violation(m.group(1))
if msg:
errors.append((lineno, msg))
def _check_js(content: str) -> list[tuple[int, str]]:
"""Char scanner: only `bearer:disable` reached as a real comment counts."""
errors: list[tuple[int, str]] = []
n = len(content)
i = 0
line = 1
line_start = 0
state = "code" # code | block | sq | dq | tmpl
block_start_i = 0
block_start_line = 0
def flag_block(text: str, start_line: int) -> None:
# Flag only a block line that STARTS with the directive (a real
# block-comment suppression), not prose that mentions it.
for off, ln in enumerate(text.splitlines()):
if _BLOCK_DIRECTIVE_LINE.match(ln):
errors.append((start_line + off, _BLOCK_MSG))
return
while i < n:
ch = content[i]
nxt = content[i + 1] if i + 1 < n else ""
if ch == "\n":
line += 1
line_start = i + 1
i += 1
continue
if state == "code":
if ch == "/" and nxt == "/":
eol = content.find("\n", i)
if eol == -1:
eol = n
_classify_js_line_comment(
line, content[line_start:i], content[i:eol], errors
)
i = eol
elif ch == "/" and nxt == "*":
state, block_start_i, block_start_line = "block", i, line
i += 2
elif ch == '"':
state = "dq"
i += 1
elif ch == "'":
state = "sq"
i += 1
elif ch == "`":
state = "tmpl"
i += 1
else:
i += 1
elif state == "block":
if ch == "*" and nxt == "/":
flag_block(content[block_start_i : i + 2], block_start_line)
state = "code"
i += 2
else:
i += 1
else: # sq | dq | tmpl
quote = {"sq": "'", "dq": '"', "tmpl": "`"}[state]
if ch == "\\":
i += 2
elif ch == quote:
state = "code"
i += 1
else:
i += 1
if state == "block": # unterminated block comment
flag_block(content[block_start_i:n], block_start_line)
return errors
def check_file(filename: str) -> list[tuple[int, str]]:
try:
# utf-8-sig strips a leading BOM so it is not mistaken for code.
with open(filename, "r", encoding="utf-8-sig") as fh:
content = fh.read()
except (UnicodeDecodeError, OSError):
return []
if filename.endswith(".py"):
return _check_python(content)
if filename.endswith(".js"):
return _check_js(content)
return []
def main(argv: list[str]) -> int:
failed = False
for filename in argv:
errors = check_file(filename)
if errors:
failed = True
print(f"\n{filename}:")
for line_num, msg in sorted(errors):
print(f" Line {line_num}: {msg}")
if failed:
print(
"\n❌ Malformed `bearer:disable` directive(s). Bearer only honors a "
"directive that is\n the bare rule id on its own line directly "
"above the statement:\n"
" # bearer:disable python_lang_sql_injection\n"
" <statement>\n"
" Put any rationale on separate comment line(s) above it."
)
return 1
return 0
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))