68 lines
2.1 KiB
Python
68 lines
2.1 KiB
Python
#!/usr/bin/env python3
|
||
"""檢查公開前端 env 範例不可洩漏內網拓樸。"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import argparse
|
||
import re
|
||
from pathlib import Path
|
||
|
||
|
||
PRIVATE_IP_PATTERN = re.compile(
|
||
r"\b(?:10(?:\.\d{1,3}){3}|192\.168(?:\.\d{1,3}){2}|172\.(?:1[6-9]|2\d|3[01])(?:\.\d{1,3}){2})\b"
|
||
)
|
||
|
||
RETIRED_PUBLIC_TOPOLOGY_KEYS = {
|
||
"NEXT_PUBLIC_HOST_IPS",
|
||
"NEXT_PUBLIC_K8S_VIP_INFO",
|
||
}
|
||
|
||
ENV_EXAMPLE_PATHS = (
|
||
Path("apps/web/.env.example"),
|
||
)
|
||
|
||
|
||
def _active_assignment(line: str) -> tuple[str, str] | None:
|
||
stripped = line.strip()
|
||
if not stripped or stripped.startswith("#") or "=" not in stripped:
|
||
return None
|
||
key, value = stripped.split("=", 1)
|
||
return key.strip(), value.strip()
|
||
|
||
|
||
def validate(root: Path) -> None:
|
||
errors: list[str] = []
|
||
|
||
for relative_path in ENV_EXAMPLE_PATHS:
|
||
path = root / relative_path
|
||
if not path.exists():
|
||
continue
|
||
|
||
for line_number, line in enumerate(path.read_text(encoding="utf-8").splitlines(), start=1):
|
||
assignment = _active_assignment(line)
|
||
if assignment is None:
|
||
continue
|
||
|
||
key, value = assignment
|
||
if key in RETIRED_PUBLIC_TOPOLOGY_KEYS:
|
||
errors.append(f"{relative_path}:{line_number}: 已停用公開前端拓樸 env key:{key}")
|
||
if PRIVATE_IP_PATTERN.search(value):
|
||
errors.append(f"{relative_path}:{line_number}: env 範例不得包含內網 IP:{key}")
|
||
if key.startswith("NEXT_PUBLIC_") and not value:
|
||
errors.append(f"{relative_path}:{line_number}: NEXT_PUBLIC_* 範例需明確使用公網入口或安全預設值:{key}")
|
||
|
||
if errors:
|
||
raise SystemExit("BLOCKED public frontend env guard:\n" + "\n".join(f"- {error}" for error in errors))
|
||
|
||
|
||
def main() -> None:
|
||
parser = argparse.ArgumentParser(description="檢查公開前端 env 範例不可洩漏內網拓樸")
|
||
parser.add_argument("--root", default=".", help="repository root")
|
||
args = parser.parse_args()
|
||
validate(Path(args.root).resolve())
|
||
print("OK public frontend env guard")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|