"""agent-egress-guard command line. agent-egress-guard check URL [-X METHOD] verdict for one request agent-egress-guard replay run the incident-derived test requests against your policy agent-egress-guard mitm-script path of the mitmproxy addon (mitmdump -s "$(agent-egress-guard mitm-script)") agent-egress-guard policy init [--agent NAME] [--owner NAME] [--tier N] print a starting per-agent policy agent-egress-guard policy validate FILE errors and warnings for a per-agent policy file agent-egress-guard policy check FILE URL [-X METHOD] verdict for one request under that agent's policy Common options: --rules FILE --hosts FILE (licensed full rule set), --api-key KEY (page-type database), --strict, --json """ import argparse import json import os import sys from .engine import DATA, Guard from .policy import AgentPolicy, PolicyError, policy_template UPGRADE = "https://www.aiagentallowlist.com/egress-guard/#full" def _guard(a): return Guard(rules_path=a.rules, hosts_path=a.hosts, api_key=a.api_key or os.environ.get("EGRESS_GUARD_API_KEY"), strict=a.strict) def cmd_check(a): v = _guard(a).check(a.url, a.method) if a.json: print(json.dumps(v.as_dict(), indent=1)) else: print("%-5s %s %s\n layer: %s rule: %s%s" % (v.decision.upper(), v.method, v.url, v.layer, v.rule, ("\n " + v.note) if v.note else "")) return 0 if v.decision != "deny" else 2 def cmd_replay(a): g = _guard(a) with open(os.path.join(DATA, "replay.json"), encoding="utf-8") as fh: data = json.load(fh) steps = data["steps"] rows, stopped = [], 0 target = [s for s in steps if s["full_policy"] == "deny"] for s in steps: v = g.check(s["url"], s["method"]) if s["full_policy"] == "deny" and v.decision == "deny": stopped += 1 rows.append((s, v)) if a.json: print(json.dumps({"edition": g.edition, "denied": stopped, "of": len(target), "steps": [dict(s, verdict=v.as_dict()) for s, v in rows]}, indent=1)) return 0 print("Policy: %s (%d rules, %d hosts)\n" % (g.edition, len(g.rules), len(g.hosts_exact) + len(g.hosts_wild))) scen = None for s, v in rows: if s["scenario"] != scen: scen = s["scenario"] print(scen) mark = "x" if v.decision == "deny" else ("!" if v.decision == "flag" else ".") print(" %s %-5s %-8s %-60s %s" % (mark, v.decision, s["method"], s["url"][:60], v.rule)) print("\n%d of the %d steps that the full policy denies are denied here." % (stopped, len(target))) missed = [s for s, v in rows if s["full_policy"] == "deny" and v.decision != "deny"] if missed: print("Allowed here, denied by the full rule set:") for s in missed: print(" - %s %s (%s)" % (s["method"], s["url"][:70], s["step"])) print("Full rule set and page-type database: " + UPGRADE) print("\n" + data["source"]) return 0 def cmd_policy_init(a): print(json.dumps(policy_template(a.agent, a.owner, a.tier), indent=2)) return 0 def cmd_policy_validate(a): try: pol = AgentPolicy.load(a.file) except PolicyError as e: print("error: %s" % e) return 2 issues = pol.validate() for i in issues: print(i) if not issues: print("ok: %s (owner %s, tier %s)" % (pol.agent or "unnamed agent", pol.owner or "none", pol.tier)) return 0 def cmd_policy_check(a): try: pol = AgentPolicy.load(a.file) except PolicyError as e: print("error: %s" % e) return 2 v = pol.check(_guard(a), a.url, a.method) if a.json: print(json.dumps(v.as_dict(), indent=1)) else: print("%-5s %s %s\n agent: %s layer: %s rule: %s%s" % ( v.decision.upper(), v.method, v.url, pol.agent or "-", v.layer, v.rule, ("\n " + v.note) if v.note else "")) return 0 if v.decision == "allow" else 2 def cmd_mitm(a): print(os.path.join(os.path.dirname(os.path.abspath(__file__)), "mitm.py")) return 0 def main(argv=None): p = argparse.ArgumentParser(prog="agent-egress-guard", description="Page-level egress policy for AI agents.") common = argparse.ArgumentParser(add_help=False) common.add_argument("--rules", help="rules file (JSON lines); default: the 3 free rules") common.add_argument("--hosts", help="host list (CSV); default: the 2 free hosts") common.add_argument("--api-key", help="AI Agent Allowlist API key: adds the page-type database") common.add_argument("--strict", action="store_true", help="with the database: deny reads to unknown domains") common.add_argument("--json", action="store_true") sub = p.add_subparsers(dest="cmd", required=True) c = sub.add_parser("check", parents=[common], help="verdict for one request") c.add_argument("url") c.add_argument("-X", "--method", default="GET") c.set_defaults(fn=cmd_check) r = sub.add_parser("replay", parents=[common], help="run the incident-derived test requests") r.set_defaults(fn=cmd_replay) m = sub.add_parser("mitm-script", help="print the path of the mitmproxy addon") m.set_defaults(fn=cmd_mitm) pol = sub.add_parser("policy", help="per-agent policy files") psub = pol.add_subparsers(dest="pcmd", required=True) pi = psub.add_parser("init", help="print a starting policy") pi.add_argument("--agent", default="my-agent") pi.add_argument("--owner", default="") pi.add_argument("--tier", type=int, default=3, choices=[1, 2, 3, 4]) pi.set_defaults(fn=cmd_policy_init) pv = psub.add_parser("validate", help="check a policy file") pv.add_argument("file") pv.set_defaults(fn=cmd_policy_validate) pc = psub.add_parser("check", parents=[common], help="verdict for one request under a policy") pc.add_argument("file") pc.add_argument("url") pc.add_argument("-X", "--method", default="GET") pc.set_defaults(fn=cmd_policy_check) a = p.parse_args(argv) return a.fn(a) if __name__ == "__main__": sys.exit(main())