"""decide.py - the two checks a sandbox makes, as a small model in Python.

    openshell policy get demo --base -o json > policy.json
    python3 decide.py policy.json

It reads the sandbox's real rules and says what should happen to four
requests. It is a model for learning, not OpenShell's own code: it knows exact
addresses and the three access words, and nothing else.
"""
import json
import sys

METHODS = {
    "read-only": {"GET", "HEAD", "OPTIONS"},
    "read-write": {"GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH"},
}
REQUESTS = [
    ("/usr/bin/curl", "api.github.com", 443, "GET"),
    ("/usr/bin/curl", "api.github.com", 443, "POST"),
    ("/usr/bin/curl", "example.com", 443, "GET"),
    ("/usr/bin/python3.12", "api.github.com", 443, "GET"),
]


def endpoints_for(rules, program, host, port):
    """Check 1, the connection: the endpoints that let this program reach this address."""
    found = []
    for rule in rules.values():
        programs = [binary["path"] for binary in rule["binaries"]]
        if program not in programs:
            continue
        for endpoint in rule["endpoints"]:
            if endpoint["host"] == host and endpoint["port"] == port:
                found.append(endpoint)
    return found


def lets_through(endpoint, method):
    """Check 2, the request: does this endpoint let this method through?"""
    if "protocol" not in endpoint:
        return True              # nobody reads the requests on this endpoint
    if endpoint.get("enforcement") != "enforce":
        return True              # "audit" writes a bad request down, and lets it pass
    access = endpoint.get("access")
    return access == "full" or method in METHODS.get(access, set())


def decide(rules, program, host, port, method):
    """What happens to one request: the verdict, and the check that gave it."""
    endpoints = endpoints_for(rules, program, host, port)
    if not endpoints:
        return "DENIED", "check 1: no rule lists this program with this address"
    if any(lets_through(endpoint, method) for endpoint in endpoints):
        return "ALLOWED", "both checks passed"
    return "DENIED", f"check 2: {method} is not in this endpoint's access"


def main():
    with open(sys.argv[1]) as file:
        rules = json.load(file)["policy"].get("network_policies", {})
    print(len(rules), "rules:", ", ".join(rules))
    for program, host, port, method in REQUESTS:
        verdict, why = decide(rules, program, host, port, method)
        print(f"{verdict:8}{method:5}{program} -> {host}:{port}   {why}")


if __name__ == "__main__":
    main()
