130 lines
4.0 KiB
Python
Executable File
130 lines
4.0 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
import argparse
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
|
|
DEFAULT_EXCLUDED_PATHS = {
|
|
"LICENSE",
|
|
"Cargo.lock",
|
|
"apps/ui/package-lock.json",
|
|
"scripts/check-community-scope.py",
|
|
"tests/unit/test_check_community_scope.py",
|
|
}
|
|
|
|
DEFAULT_EXCLUDED_PREFIXES = {
|
|
"target/",
|
|
"apps/ui/node_modules/",
|
|
"apps/ui/dist/",
|
|
}
|
|
|
|
FORBIDDEN_PATTERNS = [
|
|
("enterprise", re.compile(r"\benterprise\b", re.IGNORECASE)),
|
|
("cloud", re.compile(r"\bcloud\b", re.IGNORECASE)),
|
|
("commercial", re.compile(r"\bcommercial\b|коммер", re.IGNORECASE)),
|
|
("cloud-russian", re.compile(r"облач", re.IGNORECASE)),
|
|
("graphql", re.compile(r"\bgraphql\b", re.IGNORECASE)),
|
|
("grpc", re.compile(r"\bgrpc\b", re.IGNORECASE)),
|
|
("soap", re.compile(r"\bsoap\b", re.IGNORECASE)),
|
|
("websocket", re.compile(r"\bwebsocket\b", re.IGNORECASE)),
|
|
("short-lived-token", re.compile(r"short[-_ ]?lived", re.IGNORECASE)),
|
|
("one-time-token", re.compile(r"one[-_ ]?time", re.IGNORECASE)),
|
|
("sso", re.compile(r"\bsso\b", re.IGNORECASE)),
|
|
("two-factor", re.compile(r"two[-_ ]?factor|\b2fa\b|\bmfa\b", re.IGNORECASE)),
|
|
("billing", re.compile(r"\bbilling\b", re.IGNORECASE)),
|
|
("tenant", re.compile(r"\btenant\b", re.IGNORECASE)),
|
|
("stream-session", re.compile(r"stream session", re.IGNORECASE)),
|
|
("async-job", re.compile(r"async job", re.IGNORECASE)),
|
|
("mcp-auth", re.compile(r"mcp-auth", re.IGNORECASE)),
|
|
("protocol-capabilities", re.compile(r"protocol-capabilities", re.IGNORECASE)),
|
|
("machine-token", re.compile(r"machine token", re.IGNORECASE)),
|
|
("token-issuer", re.compile(r"token issuer", re.IGNORECASE)),
|
|
("request-variables", re.compile(r"request\.variables", re.IGNORECASE)),
|
|
]
|
|
|
|
|
|
def parse_args() -> argparse.Namespace:
|
|
parser = argparse.ArgumentParser(
|
|
description="Fail if Community repository contains out-of-scope product markers."
|
|
)
|
|
parser.add_argument(
|
|
"--root",
|
|
type=Path,
|
|
default=Path(__file__).resolve().parents[1],
|
|
help="Repository root. Defaults to parent of scripts/.",
|
|
)
|
|
parser.add_argument(
|
|
"--files",
|
|
nargs="*",
|
|
help="Optional explicit file list, relative to root. Defaults to git ls-files.",
|
|
)
|
|
return parser.parse_args()
|
|
|
|
|
|
def git_tracked_files(root: Path) -> list[str]:
|
|
result = subprocess.run(
|
|
["git", "ls-files"],
|
|
cwd=root,
|
|
check=True,
|
|
text=True,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
)
|
|
return [line for line in result.stdout.splitlines() if line]
|
|
|
|
|
|
def should_skip_path(path: str) -> bool:
|
|
if path in DEFAULT_EXCLUDED_PATHS:
|
|
return True
|
|
return any(path.startswith(prefix) for prefix in DEFAULT_EXCLUDED_PREFIXES)
|
|
|
|
|
|
def is_binary(data: bytes) -> bool:
|
|
return b"\0" in data[:4096]
|
|
|
|
|
|
def scan_file(root: Path, relative_path: str) -> list[str]:
|
|
if should_skip_path(relative_path):
|
|
return []
|
|
|
|
path = root / relative_path
|
|
if not path.is_file():
|
|
return []
|
|
|
|
data = path.read_bytes()
|
|
if is_binary(data):
|
|
return []
|
|
|
|
text = data.decode("utf-8", errors="replace")
|
|
findings: list[str] = []
|
|
for line_no, line in enumerate(text.splitlines(), start=1):
|
|
for label, pattern in FORBIDDEN_PATTERNS:
|
|
if pattern.search(line):
|
|
findings.append(f"{relative_path}:{line_no}: forbidden marker `{label}`")
|
|
return findings
|
|
|
|
|
|
def main() -> int:
|
|
args = parse_args()
|
|
root = args.root.resolve()
|
|
files = args.files if args.files is not None else git_tracked_files(root)
|
|
findings: list[str] = []
|
|
|
|
for relative_path in sorted(files):
|
|
findings.extend(scan_file(root, relative_path))
|
|
|
|
if findings:
|
|
print("Community scope check failed:", file=sys.stderr)
|
|
for finding in findings:
|
|
print(f"error: {finding}", file=sys.stderr)
|
|
return 1
|
|
|
|
print(f"Community scope check passed ({len(files)} tracked files scanned)")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|