agentsec-firewall 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,5 @@
1
+ """agentfirewall: Policy-enforced firewall for AI agent tool calls."""
2
+
3
+ __version__ = "0.1.0"
4
+
5
+ from .interceptor import Interceptor, PolicyViolationError
@@ -0,0 +1,5 @@
1
+ """Allow running as python3 -m agentfirewall."""
2
+
3
+ from .cli import main
4
+
5
+ main()
agentfirewall/cli.py ADDED
@@ -0,0 +1,236 @@
1
+ """Click-based CLI for agentfirewall."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import logging
7
+ from pathlib import Path
8
+
9
+ import click
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+ DEFAULT_POLICY_DIR = ".agentfirewall"
14
+ DEFAULT_POLICY_PATH = f"{DEFAULT_POLICY_DIR}/policy.yaml"
15
+ DEFAULT_LOG_PATH = f"{DEFAULT_POLICY_DIR}/audit.jsonl"
16
+
17
+ DEFAULT_POLICY_YAML = """\
18
+ version: "1.0"
19
+ name: "default-agent-policy"
20
+ description: "Default security policy -- blocks dangerous operations, logs everything"
21
+ default_action: log
22
+ rules:
23
+ - name: allow-read-operations
24
+ tools: ["Read", "Glob", "Grep"]
25
+ action: allow
26
+ reason: "Read operations are safe"
27
+ - name: block-dangerous-bash
28
+ tools: ["Bash"]
29
+ resources: ["rm -rf *", "sudo *", "chmod 777 *", "curl * | sh", "wget * | sh"]
30
+ action: deny
31
+ reason: "Dangerous shell commands blocked"
32
+ - name: alert-mcp-tools
33
+ tools: ["mcp__*"]
34
+ action: alert
35
+ reason: "MCP tool calls flagged for review"
36
+ - name: log-all-writes
37
+ tools: ["Write", "Edit"]
38
+ action: log
39
+ reason: "File modifications logged"
40
+ """
41
+
42
+ HOOK_PRETOOL = {
43
+ "type": "command",
44
+ "event": "PreToolUse",
45
+ "command": "python3 -c 'from agentfirewall.hooks import pretool_hook; pretool_hook()'",
46
+ }
47
+ HOOK_POSTTOOL = {
48
+ "type": "command",
49
+ "event": "PostToolUse",
50
+ "command": "python3 -c 'from agentfirewall.hooks import posttool_hook; posttool_hook()'",
51
+ }
52
+
53
+ SETTINGS_PATH = ".claude/settings.local.json"
54
+
55
+
56
+ @click.group()
57
+ @click.version_option(package_name="agentfirewall")
58
+ def main() -> None:
59
+ """agentfirewall -- Policy-enforced firewall for AI agent tool calls."""
60
+
61
+
62
+ @main.command()
63
+ def init() -> None:
64
+ """Initialize .agentfirewall/ with default policy and audit log."""
65
+ policy_dir = Path(DEFAULT_POLICY_DIR)
66
+ policy_dir.mkdir(parents=True, exist_ok=True)
67
+
68
+ policy_file = Path(DEFAULT_POLICY_PATH)
69
+ if policy_file.exists():
70
+ click.echo(f"Policy already exists: {policy_file}")
71
+ else:
72
+ policy_file.write_text(DEFAULT_POLICY_YAML, encoding="utf-8")
73
+ click.echo(f"Created default policy: {policy_file}")
74
+
75
+ log_file = Path(DEFAULT_LOG_PATH)
76
+ if not log_file.exists():
77
+ log_file.touch()
78
+ click.echo(f"Created audit log: {log_file}")
79
+
80
+ click.echo("Initialization complete. Run `agentfirewall install` to add hooks.")
81
+
82
+
83
+ @main.command()
84
+ @click.option("--policy", default=DEFAULT_POLICY_PATH, help="Path to policy YAML file.")
85
+ def validate(policy: str) -> None:
86
+ """Validate a policy YAML file."""
87
+ from agentsec_core.policy import load_policy
88
+
89
+ try:
90
+ loaded = load_policy(policy)
91
+ except FileNotFoundError:
92
+ click.echo(f"Error: Policy file not found: {policy}", err=True)
93
+ raise SystemExit(1)
94
+ except ValueError as exc:
95
+ click.echo(f"Error: Invalid policy: {exc}", err=True)
96
+ raise SystemExit(1)
97
+
98
+ click.echo(f"Policy: {loaded.name} (v{loaded.version})")
99
+ click.echo(f"Description: {loaded.description}")
100
+ click.echo(f"Default action: {loaded.default_action.value}")
101
+ click.echo(f"Rules: {len(loaded.rules)}")
102
+ for rule in loaded.rules:
103
+ tools = ", ".join(rule.tools) if rule.tools else "*"
104
+ click.echo(f" - {rule.name}: {rule.action.value} [{tools}]")
105
+ click.echo("Policy is valid.")
106
+
107
+
108
+ @main.command()
109
+ @click.option("--tail", "tail_n", default=20, type=int, help="Number of recent events.")
110
+ @click.option("--tool", "tool_name", default=None, help="Filter by tool name.")
111
+ @click.option("--action", "action_filter", default=None, help="Filter by action (allow/deny/log/alert).")
112
+ def audit(tail_n: int, tool_name: str | None, action_filter: str | None) -> None:
113
+ """Query the audit log."""
114
+ from agentsec_core.logger import AuditLogger
115
+ from agentsec_core.schemas import PolicyAction
116
+
117
+ log_path = Path(DEFAULT_LOG_PATH)
118
+ if not log_path.exists():
119
+ click.echo("No audit log found. Run `agentfirewall init` first.", err=True)
120
+ raise SystemExit(1)
121
+
122
+ audit_logger = AuditLogger(log_path)
123
+
124
+ action_enum = None
125
+ if action_filter:
126
+ try:
127
+ action_enum = PolicyAction(action_filter.lower())
128
+ except ValueError:
129
+ click.echo(f"Error: Invalid action '{action_filter}'. Use: allow/deny/log/alert", err=True)
130
+ raise SystemExit(1)
131
+
132
+ events = audit_logger.query(tool_name=tool_name, action=action_enum)
133
+ recent = events[-tail_n:]
134
+
135
+ if not recent:
136
+ click.echo("No matching events found.")
137
+ return
138
+
139
+ click.echo(f"Showing {len(recent)} of {len(events)} events:")
140
+ for event in recent:
141
+ ts = event.timestamp.strftime("%Y-%m-%d %H:%M:%S") if event.timestamp else "N/A"
142
+ violations = f" [{len(event.violations)} violations]" if event.violations else ""
143
+ click.echo(f" {ts} | {event.action_taken.value:5s} | {event.tool_name}{violations}")
144
+
145
+
146
+ @main.command()
147
+ @click.argument("path", default=".")
148
+ def scan(path: str) -> None:
149
+ """Run security scanner on project files."""
150
+ from agentsec_core.scanner import scan_file
151
+
152
+ target = Path(path)
153
+ if not target.exists():
154
+ click.echo(f"Error: Path not found: {path}", err=True)
155
+ raise SystemExit(1)
156
+
157
+ files = [target] if target.is_file() else sorted(target.rglob("*"))
158
+ total_violations = 0
159
+
160
+ for filepath in files:
161
+ if not filepath.is_file():
162
+ continue
163
+ violations = scan_file(str(filepath))
164
+ for v in violations:
165
+ total_violations += 1
166
+ click.echo(f" [{v.severity}] {v.source}:{v.line_number} - {v.message}")
167
+
168
+ if total_violations == 0:
169
+ click.echo("No security issues found.")
170
+ else:
171
+ click.echo(f"\nFound {total_violations} issue(s).")
172
+ raise SystemExit(1)
173
+
174
+
175
+ @main.command()
176
+ def install() -> None:
177
+ """Install agentfirewall hooks into Claude Code settings."""
178
+ settings_path = Path(SETTINGS_PATH)
179
+ settings_path.parent.mkdir(parents=True, exist_ok=True)
180
+
181
+ if settings_path.exists():
182
+ settings = json.loads(settings_path.read_text(encoding="utf-8"))
183
+ else:
184
+ settings = {}
185
+
186
+ hooks = settings.get("hooks", [])
187
+
188
+ # Check for existing agentfirewall hooks
189
+ existing_commands = {h.get("command", "") for h in hooks}
190
+ added = 0
191
+
192
+ if HOOK_PRETOOL["command"] not in existing_commands:
193
+ hooks.append(HOOK_PRETOOL)
194
+ added += 1
195
+
196
+ if HOOK_POSTTOOL["command"] not in existing_commands:
197
+ hooks.append(HOOK_POSTTOOL)
198
+ added += 1
199
+
200
+ settings["hooks"] = hooks
201
+ settings_path.write_text(
202
+ json.dumps(settings, indent=2) + "\n", encoding="utf-8"
203
+ )
204
+
205
+ if added > 0:
206
+ click.echo(f"Installed {added} hook(s) into {settings_path}")
207
+ else:
208
+ click.echo("Hooks already installed.")
209
+
210
+
211
+ @main.command()
212
+ def uninstall() -> None:
213
+ """Remove agentfirewall hooks from Claude Code settings."""
214
+ settings_path = Path(SETTINGS_PATH)
215
+
216
+ if not settings_path.exists():
217
+ click.echo("No settings file found. Nothing to uninstall.")
218
+ return
219
+
220
+ settings = json.loads(settings_path.read_text(encoding="utf-8"))
221
+ hooks = settings.get("hooks", [])
222
+
223
+ firewall_commands = {HOOK_PRETOOL["command"], HOOK_POSTTOOL["command"]}
224
+ original_count = len(hooks)
225
+ hooks = [h for h in hooks if h.get("command", "") not in firewall_commands]
226
+
227
+ removed = original_count - len(hooks)
228
+ settings["hooks"] = hooks
229
+ settings_path.write_text(
230
+ json.dumps(settings, indent=2) + "\n", encoding="utf-8"
231
+ )
232
+
233
+ if removed > 0:
234
+ click.echo(f"Removed {removed} hook(s) from {settings_path}")
235
+ else:
236
+ click.echo("No agentfirewall hooks found to remove.")
agentfirewall/hooks.py ADDED
@@ -0,0 +1,106 @@
1
+ """Claude Code hook integration for agentfirewall.
2
+
3
+ PreToolUse hook: reads tool call from stdin, checks policy, exits 2 to block.
4
+ PostToolUse hook: reads tool result from stdin, logs to audit trail.
5
+
6
+ Install via: agentfirewall install
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import json
12
+ import logging
13
+ import sys
14
+ from pathlib import Path
15
+
16
+ from agentsec_core.schemas import AuditEvent, PolicyAction
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+ # Default paths (created by `agentfirewall init`)
21
+ DEFAULT_POLICY_PATH = ".agentfirewall/policy.yaml"
22
+ DEFAULT_LOG_PATH = ".agentfirewall/audit.jsonl"
23
+
24
+
25
+ def _read_hook_input() -> dict:
26
+ """Read JSON hook input from stdin. Returns empty dict on failure."""
27
+ try:
28
+ raw = sys.stdin.read()
29
+ return json.loads(raw) if raw.strip() else {}
30
+ except (json.JSONDecodeError, OSError):
31
+ return {}
32
+
33
+
34
+ def _parse_json_field(data: dict, field: str) -> dict:
35
+ """Parse a possibly-stringified JSON field."""
36
+ value = data.get(field, "{}")
37
+ if isinstance(value, dict):
38
+ return value
39
+ try:
40
+ return json.loads(value)
41
+ except (json.JSONDecodeError, TypeError):
42
+ return {}
43
+
44
+
45
+ def pretool_hook() -> None:
46
+ """PreToolUse hook entry point.
47
+
48
+ Reads stdin JSON with tool_name and tool_input.
49
+ Checks against policy. Exits 2 to block, 0 to allow.
50
+ Outputs JSON reason on block.
51
+ """
52
+ if not Path(DEFAULT_POLICY_PATH).exists():
53
+ sys.exit(0) # No policy = allow all
54
+
55
+ from .interceptor import Interceptor
56
+
57
+ hook_data = _read_hook_input()
58
+ tool_name = hook_data.get("tool_name", "")
59
+ tool_input = _parse_json_field(hook_data, "tool_input")
60
+
61
+ try:
62
+ interceptor = Interceptor(
63
+ policy_path=DEFAULT_POLICY_PATH,
64
+ log_path=DEFAULT_LOG_PATH,
65
+ )
66
+ decision = interceptor.check(tool_name, tool_input)
67
+ except Exception as exc:
68
+ # Fail-open: don't break Claude Code if firewall crashes
69
+ sys.stderr.write(f"agentfirewall: error checking policy: {exc}\n")
70
+ sys.exit(0)
71
+
72
+ if decision.action == PolicyAction.DENY:
73
+ reason = decision.reason or "Blocked by agentfirewall policy"
74
+ output = json.dumps({"decision": "block", "reason": reason})
75
+ print(output) # noqa: T201 — CLI output
76
+ sys.exit(2)
77
+
78
+ sys.exit(0)
79
+
80
+
81
+ def posttool_hook() -> None:
82
+ """PostToolUse hook entry point.
83
+
84
+ Reads stdin JSON with tool result. Logs to audit trail.
85
+ Always exits 0 (never blocks post-execution).
86
+ """
87
+ if not Path(DEFAULT_LOG_PATH).parent.exists():
88
+ sys.exit(0)
89
+
90
+ from agentsec_core.logger import AuditLogger
91
+
92
+ hook_data = _read_hook_input()
93
+ tool_name = hook_data.get("tool_name", "")
94
+
95
+ try:
96
+ audit_logger = AuditLogger(DEFAULT_LOG_PATH)
97
+ event = AuditEvent(
98
+ event_type="tool_execution",
99
+ tool_name=tool_name,
100
+ action_taken=PolicyAction.LOG,
101
+ )
102
+ audit_logger.log(event)
103
+ except Exception as exc:
104
+ sys.stderr.write(f"agentfirewall: error logging: {exc}\n")
105
+
106
+ sys.exit(0)
@@ -0,0 +1,113 @@
1
+ """Tool call interceptor with policy enforcement."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import functools
6
+ import logging
7
+ from pathlib import Path
8
+ from typing import Any, Callable
9
+
10
+ from agentsec_core.alerts import create_alerter
11
+ from agentsec_core.logger import AuditLogger
12
+ from agentsec_core.policy import evaluate, load_policy
13
+ from agentsec_core.schemas import AuditEvent, Policy, PolicyAction, PolicyDecision
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ class PolicyViolationError(Exception):
19
+ """Raised when a tool call violates the security policy."""
20
+
21
+ def __init__(self, decision: PolicyDecision) -> None:
22
+ self.decision = decision
23
+ super().__init__(f"Policy violation: {decision.reason}")
24
+
25
+
26
+ class Interceptor:
27
+ """Intercepts tool calls and enforces security policies.
28
+
29
+ Usage:
30
+ interceptor = Interceptor(policy_path="policy.yaml", log_path="audit.jsonl")
31
+ decision = interceptor.check("Bash", {"command": "rm -rf /"})
32
+ # decision.action == PolicyAction.DENY
33
+
34
+ # Or as a decorator:
35
+ @interceptor.wrap("my_tool")
36
+ def dangerous_function(params):
37
+ ...
38
+ """
39
+
40
+ def __init__(
41
+ self,
42
+ policy_path: str | Path | None = None,
43
+ policy: Policy | None = None,
44
+ log_path: str | Path = ".agentfirewall/audit.jsonl",
45
+ webhook_url: str | None = None,
46
+ ) -> None:
47
+ """Initialize with either a policy file path or a Policy object."""
48
+ if policy is not None:
49
+ self._policy = policy
50
+ elif policy_path is not None:
51
+ self._policy = load_policy(policy_path)
52
+ else:
53
+ raise ValueError("Either policy_path or policy must be provided")
54
+
55
+ self._logger = AuditLogger(log_path)
56
+ self._alerter = create_alerter(webhook_url) if webhook_url else None
57
+
58
+ def check(self, tool_name: str, parameters: dict | None = None) -> PolicyDecision:
59
+ """Check a tool call against the policy.
60
+
61
+ Returns PolicyDecision. Logs the event. Fires alert on DENY/ALERT.
62
+ Does NOT raise -- caller decides what to do.
63
+ """
64
+ decision = evaluate(self._policy, tool_name, parameters)
65
+
66
+ event = AuditEvent(
67
+ event_type="policy_check",
68
+ tool_name=tool_name,
69
+ action_taken=decision.action,
70
+ violations=list(decision.violations) if decision.violations else [],
71
+ parameters=parameters or {},
72
+ )
73
+ self._logger.log(event)
74
+
75
+ if decision.action in (PolicyAction.DENY, PolicyAction.ALERT) and self._alerter:
76
+ try:
77
+ self._alerter.send(event)
78
+ except Exception as exc:
79
+ logger.warning("Failed to send alert: %s", exc)
80
+
81
+ return decision
82
+
83
+ def check_or_raise(
84
+ self, tool_name: str, parameters: dict | None = None
85
+ ) -> PolicyDecision:
86
+ """Like check(), but raises PolicyViolationError on DENY."""
87
+ decision = self.check(tool_name, parameters)
88
+ if decision.action == PolicyAction.DENY:
89
+ raise PolicyViolationError(decision)
90
+ return decision
91
+
92
+ def wrap(self, tool_name: str) -> Callable:
93
+ """Decorator that checks policy before executing a function.
94
+
95
+ Usage:
96
+ @interceptor.wrap("data_export")
97
+ def export_data(params):
98
+ ...
99
+
100
+ Raises PolicyViolationError if denied.
101
+ """
102
+ def decorator(func: Callable) -> Callable:
103
+ @functools.wraps(func)
104
+ def wrapper(*args: Any, **kwargs: Any) -> Any:
105
+ self.check_or_raise(tool_name, kwargs or {})
106
+ return func(*args, **kwargs)
107
+ return wrapper
108
+ return decorator
109
+
110
+ @property
111
+ def audit_logger(self) -> AuditLogger:
112
+ """Access the underlying audit logger."""
113
+ return self._logger
@@ -0,0 +1,152 @@
1
+ Metadata-Version: 2.4
2
+ Name: agentsec-firewall
3
+ Version: 0.1.0
4
+ Summary: Policy-enforced firewall for AI agent tool calls
5
+ License-Expression: MIT
6
+ Requires-Python: >=3.11
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: agentsec-core>=0.1.0
9
+ Requires-Dist: click>=8.0
10
+ Requires-Dist: pyyaml>=6.0
11
+ Provides-Extra: dev
12
+ Requires-Dist: pytest>=8.0; extra == "dev"
13
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
14
+ Requires-Dist: ruff>=0.3.0; extra == "dev"
15
+
16
+ # agentfirewall
17
+
18
+ Policy-enforced firewall for AI agent tool calls. Intercepts, evaluates, and audits every tool invocation against a YAML security policy.
19
+
20
+ ## Install
21
+
22
+ ```bash
23
+ pip install agentfirewall
24
+ ```
25
+
26
+ ## Quick Start
27
+
28
+ ```bash
29
+ # 1. Initialize policy and audit log
30
+ agentfirewall init
31
+
32
+ # 2. Install hooks into Claude Code
33
+ agentfirewall install
34
+
35
+ # 3. Done -- every tool call is now checked against .agentfirewall/policy.yaml
36
+ ```
37
+
38
+ ## How It Works
39
+
40
+ ```
41
+ Tool Call (e.g. Bash "rm -rf /")
42
+ |
43
+ v
44
+ +-----------+
45
+ | PreToolUse | <-- Claude Code hook reads stdin JSON
46
+ | Hook |
47
+ +-----+-----+
48
+ |
49
+ v
50
+ +-----------+
51
+ | Interceptor| <-- Evaluates against policy.yaml
52
+ | .check() | Scans params for secrets
53
+ +-----+-----+
54
+ |
55
+ +----+----+
56
+ | |
57
+ ALLOW DENY ---------> exit 2 (blocks tool call)
58
+ | + JSON reason to stdout
59
+ v
60
+ Tool Executes
61
+ |
62
+ v
63
+ +-----------+
64
+ | PostToolUse| <-- Logs execution to audit.jsonl
65
+ | Hook |
66
+ +-----------+
67
+ ```
68
+
69
+ ## Policy Reference
70
+
71
+ Policies are YAML files at `.agentfirewall/policy.yaml`:
72
+
73
+ ```yaml
74
+ version: "1.0"
75
+ name: "my-policy"
76
+ description: "Custom security policy"
77
+ default_action: log # allow | deny | log | alert
78
+
79
+ rules:
80
+ - name: allow-read-operations
81
+ tools: ["Read", "Glob", "Grep"] # fnmatch patterns
82
+ action: allow
83
+ reason: "Read operations are safe"
84
+
85
+ - name: block-dangerous-bash
86
+ tools: ["Bash"]
87
+ resources: ["rm -rf *", "sudo *"] # resource patterns
88
+ action: deny
89
+ reason: "Dangerous shell commands blocked"
90
+
91
+ - name: alert-mcp-tools
92
+ tools: ["mcp__*"] # wildcards supported
93
+ action: alert
94
+ reason: "MCP tool calls flagged for review"
95
+
96
+ - name: log-all-writes
97
+ tools: ["Write", "Edit"]
98
+ action: log
99
+ reason: "File modifications logged"
100
+ ```
101
+
102
+ **Actions:**
103
+ - `allow` -- permit the tool call
104
+ - `deny` -- block the tool call (exit code 2)
105
+ - `log` -- permit but log to audit trail
106
+ - `alert` -- permit, log, and fire webhook alert
107
+
108
+ ## CLI Reference
109
+
110
+ | Command | Description |
111
+ |---------|-------------|
112
+ | `agentfirewall init` | Create `.agentfirewall/` with default policy and audit log |
113
+ | `agentfirewall install` | Add PreToolUse/PostToolUse hooks to `.claude/settings.local.json` |
114
+ | `agentfirewall uninstall` | Remove agentfirewall hooks from settings |
115
+ | `agentfirewall validate` | Validate policy YAML and print rule summary |
116
+ | `agentfirewall audit` | Query audit log (supports `--tail`, `--tool`, `--action` filters) |
117
+ | `agentfirewall scan [PATH]` | Run security scanner on project files |
118
+
119
+ ## Python API
120
+
121
+ ```python
122
+ from agentfirewall import Interceptor, PolicyViolationError
123
+ from agentsec_core.schemas import PolicyAction
124
+
125
+ # From a policy file
126
+ interceptor = Interceptor(policy_path=".agentfirewall/policy.yaml")
127
+
128
+ # Check a tool call (returns PolicyDecision, never raises)
129
+ decision = interceptor.check("Bash", {"command": "rm -rf /"})
130
+ if decision.action == PolicyAction.DENY:
131
+ print(f"Blocked: {decision.reason}")
132
+
133
+ # Check and raise on deny
134
+ try:
135
+ interceptor.check_or_raise("Bash", {"command": "rm -rf /"})
136
+ except PolicyViolationError as e:
137
+ print(f"Violation: {e.decision.reason}")
138
+
139
+ # Decorator pattern
140
+ @interceptor.wrap("data_export")
141
+ def export_data(**kwargs):
142
+ ... # Only runs if policy allows
143
+ ```
144
+
145
+ ## Requirements
146
+
147
+ - Python 3.11+
148
+ - [agentsec-core](../agentsec-core/) >= 0.1.0
149
+
150
+ ## License
151
+
152
+ MIT
@@ -0,0 +1,10 @@
1
+ agentfirewall/__init__.py,sha256=XkIemylFB6ZRbPA1h7S1_zrePYUm1ywqAvJDMwPnoHM,154
2
+ agentfirewall/__main__.py,sha256=wQ9HRnZeqyRwycfm0EyqjfSr59OfTfF_jCFCpDJh5Sw,80
3
+ agentfirewall/cli.py,sha256=T86DLuP94OhPkH7FhdCDEYQtR9Ljhr7rzN6oQch3aSE,7619
4
+ agentfirewall/hooks.py,sha256=P0uTDI_5u_hOxBhUK17gc9Op-QpTW216J3hiKTEhP4I,3062
5
+ agentfirewall/interceptor.py,sha256=wOFV-skuC_hnnbGAWHL_Xsq9V1TEcK8EH4XKi0Jb41I,3834
6
+ agentsec_firewall-0.1.0.dist-info/METADATA,sha256=p5vh0fOlESt5YNdaUQ1PRBisvKbuWG231scDXSpVJ6o,3878
7
+ agentsec_firewall-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
8
+ agentsec_firewall-0.1.0.dist-info/entry_points.txt,sha256=cXKWk_r1Xc7f9VLM8NzS4WugZ2G5KI3WEeWHOmkW3oc,57
9
+ agentsec_firewall-0.1.0.dist-info/top_level.txt,sha256=T8cdn-in91cGXsuGMoyw88WfGD0pauNiBT9BNdOnCpc,14
10
+ agentsec_firewall-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ agentfirewall = agentfirewall.cli:main
@@ -0,0 +1 @@
1
+ agentfirewall