#!/usr/bin/env python3
"""Offline, heuristic Docker run exposure policy check. Never executes commands.

It checks only selected known Docker run flags and is NOT a firewall audit.
Run --self-test, or pass a quoted command string with --command.
Never paste commands containing credentials or private mounts into terminal history.
"""
from __future__ import annotations
import argparse
import json
import shlex


def inspect(command: str) -> dict:
    tokens = shlex.split(command)
    if len(tokens) < 3 or tokens[:2] != ["docker", "run"]:
        raise ValueError("Only an argv-style docker run example is supported")
    risks=[]
    def note(code: str) -> None:
        if code not in risks:risks.append(code)
    i=2
    while i<len(tokens):
        arg=tokens[i]
        if arg in ("--privileged",):
            note("PRIVILEGED_CONTAINER")
        if arg=="--ipc=host" or (arg=="--ipc" and i+1<len(tokens) and tokens[i+1]=="host"):
            note("HOST_IPC_SHARED")
        if arg=="--network=host" or (arg=="--network" and i+1<len(tokens) and tokens[i+1]=="host"):
            note("HOST_NETWORK_NAMESPACE")
        if arg=="-P" or arg=="--publish-all":
            note("PUBLISH_ALL_EXPOSES_CONTAINER_PORTS")
        if arg in ("-p","--publish") and i+1<len(tokens):
            value=tokens[i+1];i+=1
        elif arg.startswith("--publish="):
            value=arg.split("=",1)[1]
        elif arg.startswith("-p") and len(arg)>2:
            value=arg[2:]
        else:
            value=None
        if value is not None:
            # IPv4 examples only. Any ambiguity is treated as unresolved.
            if value.startswith("127.0.0.1:") or value.startswith("[::1]:"):
                pass
            elif value.startswith("0.0.0.0:") or value.startswith("[::]:"):
                note("EXPLICIT_ALL_INTERFACE_PORT_PUBLISH")
            elif value.startswith(("localhost:","::1:")):
                note("AMBIGUOUS_PUBLISH_HOST")
            elif value.count(":")<=1:
                note("UNSPECIFIED_HOST_BIND_ALL_INTERFACES")
            else:
                note("NON_LOOPBACK_OR_AMBIGUOUS_PUBLISH_HOST")
        i+=1
    return {
        "status": "STATIC_REVIEW_ONLY",
        "risk_codes":sorted(risks),
        "has_high_risk_flags":bool(risks),
        "commands_executed":0,
        "network_requests":0,
        "cannot_check": [
            "host firewall and reverse proxies",
            "Docker daemon default bind address",
            "Docker Engine version-specific localhost reachability",
            "container image security",
            "Kubernetes or Docker Compose",
            "actual published port reachability",
        ],
    }


def self_test() -> dict:
    cases=[
        ("docker run --gpus all -p 8000:8000 image", {"UNSPECIFIED_HOST_BIND_ALL_INTERFACES"}),
        ("docker run -p 127.0.0.1:8000:8000 image", set()),
        ("docker run -p 0.0.0.0:8000:8000 image", {"EXPLICIT_ALL_INTERFACE_PORT_PUBLISH"}),
        ("docker run --publish=11434:11434 image", {"UNSPECIFIED_HOST_BIND_ALL_INTERFACES"}),
        ("docker run --ipc=host image", {"HOST_IPC_SHARED"}),
        ("docker run --privileged image", {"PRIVILEGED_CONTAINER"}),
        ("docker run -P image", {"PUBLISH_ALL_EXPOSES_CONTAINER_PORTS"}),
        ("docker run --network host image", {"HOST_NETWORK_NAMESPACE"}),
        ("docker run --gpus all --shm-size=1g --ulimit memlock=-1 -p 127.0.0.1:8000:8000 image", set()),
        ("docker run -p 127.0.0.1:11434:11434 --gpus all image", set()),
        ("docker run --ipc host -p 0.0.0.0:8000:8000 image", {"HOST_IPC_SHARED","EXPLICIT_ALL_INTERFACE_PORT_PUBLISH"}),
    ]
    names=[]
    for n,(command,expected) in enumerate(cases,1):
        actual=inspect(command)
        assert set(actual["risk_codes"])==expected,(n,actual)
        names.append(f"synthetic_case_{n}")
    try:
        inspect("ls -la")
        raise AssertionError("Non-Docker example accepted")
    except ValueError:
        names.append("reject_non_docker_input")
    return {"status":"PASS","count":len(names),"synthetic_checks":names,
            "docker_commands_executed":0,"network_requests":0}


def main() -> int:
    parser=argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--self-test",action="store_true")
    parser.add_argument("--command",type=str)
    args=parser.parse_args()
    if args.self_test:
        print(json.dumps(self_test(),indent=2))
        return 0
    if not args.command:
        parser.error("Use --self-test or --command")
    try:
        print(json.dumps(inspect(args.command),indent=2))
        return 0
    except ValueError:
        print(json.dumps({"status":"INVALID_DOCKER_RUN_INPUT"}))
        return 2


if __name__=="__main__":
    raise SystemExit(main())
