mirror of
https://github.com/shadowsocks/ShadowsocksX-NG.git
synced 2026-11-08 10:34:51 +00:00
95 lines
3.5 KiB
Bash
Executable File
95 lines
3.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Build-time offline verification of pinned rule snapshots.
|
|
# Ordinary builds never fetch or convert; they only verify the local snapshots
|
|
# are present, intact, and match their manifests. Missing, corrupt, or
|
|
# version-mismatched snapshots fail the build.
|
|
set -euo pipefail
|
|
|
|
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
|
|
fail() {
|
|
echo "error: $*" >&2
|
|
echo " (run the matching Scripts/update-*.sh after reviewing upstream changes)" >&2
|
|
exit 1
|
|
}
|
|
|
|
verify_one() {
|
|
local name="$1"
|
|
local min_count="$2"
|
|
local max_count="$3"
|
|
local rules_dir="${ROOT}/Vendor/rules/${name}"
|
|
local snapshot="${rules_dir}/snapshot.json"
|
|
local manifest="${rules_dir}/manifest.json"
|
|
local notice="${rules_dir}/NOTICE"
|
|
|
|
[[ -f "${snapshot}" ]] || fail "missing ${snapshot}"
|
|
[[ -f "${manifest}" ]] || fail "missing ${manifest}"
|
|
[[ -f "${notice}" ]] || fail "missing ${notice} (license/attribution must ship with the snapshot)"
|
|
|
|
python3 - "${snapshot}" "${manifest}" "${min_count}" "${max_count}" "${name}" <<'PY' || fail "${name} rule snapshot verification failed"
|
|
import hashlib, json, sys
|
|
from pathlib import Path
|
|
|
|
snapshot_path, manifest_path, min_count, max_count, name = sys.argv[1:6]
|
|
min_count, max_count = int(min_count), int(max_count)
|
|
snapshot_bytes = Path(snapshot_path).read_bytes()
|
|
try:
|
|
snapshot = json.loads(snapshot_bytes)
|
|
except Exception as exc: # noqa: BLE001
|
|
raise SystemExit(f"snapshot is not valid JSON: {exc}")
|
|
|
|
if snapshot.get("schemaVersion") != 2:
|
|
raise SystemExit(f"schemaVersion mismatch: {snapshot.get('schemaVersion')!r} != 2")
|
|
|
|
meta = snapshot.get("metadata") or {}
|
|
for key in ("source", "upstreamReference", "inputDigest", "fetchedAt", "converterVersion", "license", "attribution"):
|
|
if key not in meta:
|
|
raise SystemExit(f"snapshot metadata missing {key!r}")
|
|
|
|
if meta.get("converterVersion") != "2.0.0":
|
|
raise SystemExit(
|
|
f"converterVersion mismatch: {meta.get('converterVersion')!r} != '2.0.0' "
|
|
"(re-run the explicit update or bump both sides together)"
|
|
)
|
|
|
|
rules = snapshot.get("rules")
|
|
if not isinstance(rules, list) or not rules:
|
|
raise SystemExit("snapshot has no rules")
|
|
if len(rules) < min_count or len(rules) > max_count:
|
|
raise SystemExit(f"abnormal rule count {len(rules)} (allowed {min_count}..{max_count})")
|
|
|
|
try:
|
|
manifest = json.loads(Path(manifest_path).read_text(encoding="utf-8"))
|
|
except Exception as exc: # noqa: BLE001
|
|
raise SystemExit(f"manifest is not valid JSON: {exc}")
|
|
|
|
for key in ("upstreamVersion", "snapshotSHA256", "converterVersion"):
|
|
if key not in manifest:
|
|
raise SystemExit(f"manifest missing {key!r}")
|
|
|
|
if manifest.get("converterVersion") != meta.get("converterVersion"):
|
|
raise SystemExit("manifest converterVersion does not match snapshot metadata")
|
|
|
|
digest = hashlib.sha256(snapshot_bytes).hexdigest()
|
|
if digest != manifest.get("snapshotSHA256"):
|
|
raise SystemExit(
|
|
"snapshot SHA-256 does not match manifest "
|
|
f"({digest} != {manifest.get('snapshotSHA256')})"
|
|
)
|
|
|
|
if manifest.get("upstreamVersion") != meta.get("source", {}).get("upstreamVersion"):
|
|
raise SystemExit("manifest upstreamVersion does not match snapshot metadata")
|
|
|
|
print(f"{name} snapshot ok: {len(rules)} rules, upstream {meta.get('source', {}).get('upstreamVersion')}")
|
|
PY
|
|
}
|
|
|
|
# geolocation-cn: domain rules.
|
|
verify_one "geolocation-cn" 100 200000
|
|
# china-ipv4: China mainland IPv4 CIDR.
|
|
verify_one "china-ipv4" 100 50000
|
|
# gfwlist: GFWList AutoProxy proxy candidates.
|
|
verify_one "gfwlist" 100 50000
|
|
|
|
echo "verify-rule-snapshots: ok"
|