docs(web): 鎖住公開前端 env 範例拓樸 [skip ci]
This commit is contained in:
67
scripts/security/public-frontend-env-guard.py
Normal file
67
scripts/security/public-frontend-env-guard.py
Normal file
@@ -0,0 +1,67 @@
|
||||
#!/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()
|
||||
@@ -83,6 +83,10 @@ def validate(root: Path) -> None:
|
||||
str(root / "scripts" / "security" / "package-supply-chain-owner-policy-guard.py")
|
||||
)
|
||||
package_policy_guard["validate"](root)
|
||||
public_frontend_env_guard = runpy.run_path(
|
||||
str(root / "scripts" / "security" / "public-frontend-env-guard.py")
|
||||
)
|
||||
public_frontend_env_guard["validate"](root)
|
||||
|
||||
manifest = load_json(security_dir / "security-supply-chain-contract-manifest.snapshot.json")
|
||||
readiness = load_json(security_dir / "security-mirror-readiness.snapshot.json")
|
||||
|
||||
Reference in New Issue
Block a user