agentguardproxy 0.9.0__tar.gz → 1.0.0__tar.gz
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.
- {agentguardproxy-0.9.0 → agentguardproxy-1.0.0}/PKG-INFO +1 -1
- {agentguardproxy-0.9.0 → agentguardproxy-1.0.0}/agentguard/adapters/_common.py +31 -5
- {agentguardproxy-0.9.0 → agentguardproxy-1.0.0}/agentguard/adapters/mcp.py +1 -1
- {agentguardproxy-0.9.0 → agentguardproxy-1.0.0}/agentguardproxy.egg-info/PKG-INFO +1 -1
- {agentguardproxy-0.9.0 → agentguardproxy-1.0.0}/agentguardproxy.egg-info/SOURCES.txt +1 -0
- {agentguardproxy-0.9.0 → agentguardproxy-1.0.0}/pyproject.toml +1 -1
- agentguardproxy-1.0.0/tests/test_action_parity.py +113 -0
- {agentguardproxy-0.9.0 → agentguardproxy-1.0.0}/README.md +0 -0
- {agentguardproxy-0.9.0 → agentguardproxy-1.0.0}/agentguard/__init__.py +0 -0
- {agentguardproxy-0.9.0 → agentguardproxy-1.0.0}/agentguard/adapters/__init__.py +0 -0
- {agentguardproxy-0.9.0 → agentguardproxy-1.0.0}/agentguard/adapters/browseruse.py +0 -0
- {agentguardproxy-0.9.0 → agentguardproxy-1.0.0}/agentguard/adapters/crewai.py +0 -0
- {agentguardproxy-0.9.0 → agentguardproxy-1.0.0}/agentguard/adapters/langchain.py +0 -0
- {agentguardproxy-0.9.0 → agentguardproxy-1.0.0}/agentguard/core.py +0 -0
- {agentguardproxy-0.9.0 → agentguardproxy-1.0.0}/agentguard/decorators.py +0 -0
- {agentguardproxy-0.9.0 → agentguardproxy-1.0.0}/agentguardproxy.egg-info/dependency_links.txt +0 -0
- {agentguardproxy-0.9.0 → agentguardproxy-1.0.0}/agentguardproxy.egg-info/requires.txt +0 -0
- {agentguardproxy-0.9.0 → agentguardproxy-1.0.0}/agentguardproxy.egg-info/top_level.txt +0 -0
- {agentguardproxy-0.9.0 → agentguardproxy-1.0.0}/setup.cfg +0 -0
- {agentguardproxy-0.9.0 → agentguardproxy-1.0.0}/tests/test_adapters.py +0 -0
- {agentguardproxy-0.9.0 → agentguardproxy-1.0.0}/tests/test_adapters_extended.py +0 -0
- {agentguardproxy-0.9.0 → agentguardproxy-1.0.0}/tests/test_browseruse.py +0 -0
- {agentguardproxy-0.9.0 → agentguardproxy-1.0.0}/tests/test_crewai.py +0 -0
- {agentguardproxy-0.9.0 → agentguardproxy-1.0.0}/tests/test_decorator.py +0 -0
- {agentguardproxy-0.9.0 → agentguardproxy-1.0.0}/tests/test_end_to_end_real_server.py +0 -0
- {agentguardproxy-0.9.0 → agentguardproxy-1.0.0}/tests/test_guard.py +0 -0
- {agentguardproxy-0.9.0 → agentguardproxy-1.0.0}/tests/test_integration.py +0 -0
- {agentguardproxy-0.9.0 → agentguardproxy-1.0.0}/tests/test_langchain.py +0 -0
- {agentguardproxy-0.9.0 → agentguardproxy-1.0.0}/tests/test_mcp.py +0 -0
- {agentguardproxy-0.9.0 → agentguardproxy-1.0.0}/tests/test_mcp_fuzz.py +0 -0
- {agentguardproxy-0.9.0 → agentguardproxy-1.0.0}/tests/test_mcp_gateway.py +0 -0
- {agentguardproxy-0.9.0 → agentguardproxy-1.0.0}/tests/test_polling_jitter.py +0 -0
- {agentguardproxy-0.9.0 → agentguardproxy-1.0.0}/tests/test_redactor_property.py +0 -0
- {agentguardproxy-0.9.0 → agentguardproxy-1.0.0}/tests/test_sdk_integration.py +0 -0
- {agentguardproxy-0.9.0 → agentguardproxy-1.0.0}/tests/test_sdk_polish.py +0 -0
- {agentguardproxy-0.9.0 → agentguardproxy-1.0.0}/tests/test_tenant_routing.py +0 -0
- {agentguardproxy-0.9.0 → agentguardproxy-1.0.0}/tests/test_wire_format.py +0 -0
|
@@ -21,19 +21,45 @@ def domain_from_url(url: Any) -> str:
|
|
|
21
21
|
return ""
|
|
22
22
|
|
|
23
23
|
|
|
24
|
+
# Filesystem-action verb groups. These MUST stay byte-identical to the Go
|
|
25
|
+
# transports' gateclient.InferFilesystemAction (pkg/internal/gateclient/
|
|
26
|
+
# gateclient.go): same verbs, same groups, same precedence (read, then
|
|
27
|
+
# write, then delete). Parity is pinned by two mirrored test tables —
|
|
28
|
+
# plugins/python/tests/test_action_parity.py and
|
|
29
|
+
# pkg/internal/gateclient/gateclient_test.go. Edit both sides together.
|
|
30
|
+
_READ_PREFIXES = ("read", "list", "get", "stat", "cat", "find", "glob")
|
|
31
|
+
_WRITE_PREFIXES = ("write", "edit", "create", "append", "save", "copy", "move")
|
|
32
|
+
_DELETE_PREFIXES = ("delete", "remove", "unlink", "rm")
|
|
33
|
+
|
|
34
|
+
|
|
24
35
|
def infer_path_action(tool_name: Any) -> str:
|
|
25
36
|
"""Map a tool-name verb to the canonical filesystem action
|
|
26
|
-
("read"/"write"/"delete"), or "" when no verb matches.
|
|
27
|
-
|
|
37
|
+
("read"/"write"/"delete"), or "" when no verb matches.
|
|
38
|
+
|
|
39
|
+
Mirrors the Go transports' gateclient.InferFilesystemAction exactly:
|
|
40
|
+
a case-insensitive ``strings.HasPrefix`` (Python ``str.startswith``)
|
|
41
|
+
against these verb groups, checked in this order —
|
|
42
|
+
|
|
43
|
+
read = read, list, get, stat, cat, find, glob
|
|
44
|
+
write = write, edit, create, append, save, copy, move
|
|
45
|
+
delete = delete, remove, unlink, rm
|
|
46
|
+
|
|
47
|
+
Prefix (not substring) matching is deliberate: it avoids the false
|
|
48
|
+
positives substring matching produces that Go never makes — e.g.
|
|
49
|
+
"set_target" contains "get" but does not START with it, and
|
|
50
|
+
"undelete_x" contains "delete" but does not start with it. Both
|
|
51
|
+
correctly yield "" here, matching Go. Parity is pinned by
|
|
52
|
+
plugins/python/tests/test_action_parity.py, whose table mirrors
|
|
53
|
+
pkg/internal/gateclient/gateclient_test.go.
|
|
28
54
|
"""
|
|
29
55
|
if not isinstance(tool_name, str):
|
|
30
56
|
return ""
|
|
31
57
|
name_lower = tool_name.lower()
|
|
32
|
-
if
|
|
58
|
+
if name_lower.startswith(_READ_PREFIXES):
|
|
33
59
|
return "read"
|
|
34
|
-
if
|
|
60
|
+
if name_lower.startswith(_WRITE_PREFIXES):
|
|
35
61
|
return "write"
|
|
36
|
-
if
|
|
62
|
+
if name_lower.startswith(_DELETE_PREFIXES):
|
|
37
63
|
return "delete"
|
|
38
64
|
return ""
|
|
39
65
|
|
|
@@ -41,7 +41,7 @@ from agentguard.adapters._common import extract_check_params
|
|
|
41
41
|
|
|
42
42
|
# MCP protocol constants
|
|
43
43
|
MCP_PROTOCOL_VERSION = "2024-11-05"
|
|
44
|
-
SDK_VERSION = "0.
|
|
44
|
+
SDK_VERSION = "1.0.0"
|
|
45
45
|
|
|
46
46
|
# Secret patterns mirrored from pkg/notify/notify.go's DefaultRedactor. The
|
|
47
47
|
# MCP adapter forwards handler exception text back to the client as a
|
|
@@ -14,6 +14,7 @@ agentguardproxy.egg-info/SOURCES.txt
|
|
|
14
14
|
agentguardproxy.egg-info/dependency_links.txt
|
|
15
15
|
agentguardproxy.egg-info/requires.txt
|
|
16
16
|
agentguardproxy.egg-info/top_level.txt
|
|
17
|
+
tests/test_action_parity.py
|
|
17
18
|
tests/test_adapters.py
|
|
18
19
|
tests/test_adapters_extended.py
|
|
19
20
|
tests/test_browseruse.py
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""Filesystem-action inference parity: Python SDK <-> Go transports.
|
|
2
|
+
|
|
3
|
+
The Python adapters (infer_path_action in agentguard.adapters._common) and
|
|
4
|
+
the Go transports (gateclient.InferFilesystemAction) must map a tool name to
|
|
5
|
+
the SAME filesystem `action` ("read"/"write"/"delete"/""), otherwise an
|
|
6
|
+
`action:`-keyed filesystem policy rule fires on one transport and not the
|
|
7
|
+
other for the same tool call.
|
|
8
|
+
|
|
9
|
+
The table below is the SAME (tool_name -> action) table pinned on the Go
|
|
10
|
+
side in pkg/internal/gateclient/gateclient_test.go (TestInferFilesystemAction).
|
|
11
|
+
When you add or change a verb in one place, change it in the other and update
|
|
12
|
+
BOTH tables — this test and that Go test are the tripwire.
|
|
13
|
+
|
|
14
|
+
This module imports ONLY agentguard.adapters._common, so it runs without
|
|
15
|
+
langchain / crewai / mcp (or any framework dependency) installed.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from agentguard.adapters._common import infer_path_action
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# Mirror of gateclient_test.go's TestInferFilesystemAction table. Keep in sync.
|
|
22
|
+
GO_PARITY_TABLE = {
|
|
23
|
+
"read_file": "read",
|
|
24
|
+
"list_files": "read",
|
|
25
|
+
"get_file": "read",
|
|
26
|
+
"stat_file": "read",
|
|
27
|
+
"cat": "read",
|
|
28
|
+
"find_files": "read",
|
|
29
|
+
"glob": "read",
|
|
30
|
+
"write_file": "write",
|
|
31
|
+
"edit_file": "write",
|
|
32
|
+
"create_dir": "write",
|
|
33
|
+
"append_file": "write",
|
|
34
|
+
"delete_file": "delete",
|
|
35
|
+
"remove_dir": "delete",
|
|
36
|
+
"unlink": "delete",
|
|
37
|
+
"unknown": "",
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
# Extra regression cases for verbs the substring implementation used to miss
|
|
41
|
+
# (edit/append/copy/move/unlink/rm*/list/stat/cat/find/glob). These are the
|
|
42
|
+
# tool names that previously sent NO action through the Python SDK while the
|
|
43
|
+
# Go transports DID send one.
|
|
44
|
+
REGRESSION_TABLE = {
|
|
45
|
+
"edit_file": "write",
|
|
46
|
+
"append_file": "write",
|
|
47
|
+
"copy_file": "write",
|
|
48
|
+
"move_file": "write",
|
|
49
|
+
"unlink": "delete",
|
|
50
|
+
"rmdir": "delete", # "rm" prefix
|
|
51
|
+
"rm": "delete",
|
|
52
|
+
"list_files": "read",
|
|
53
|
+
"stat_file": "read",
|
|
54
|
+
"cat": "read",
|
|
55
|
+
"find_files": "read",
|
|
56
|
+
"glob": "read",
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
# False-positive guards: substring matching (the old behavior) wrongly mapped
|
|
60
|
+
# these; prefix matching (Go's HasPrefix, our str.startswith) does not.
|
|
61
|
+
FALSE_POSITIVE_TABLE = {
|
|
62
|
+
"set_target": "", # contains "get" but does not START with it
|
|
63
|
+
"undelete_x": "", # contains "delete" but does not START with it
|
|
64
|
+
"forget": "", # contains "get"
|
|
65
|
+
"overwrite": "", # contains "write"
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def test_go_parity_table():
|
|
70
|
+
"""Every pinned Go case maps identically in Python."""
|
|
71
|
+
for tool_name, expected in GO_PARITY_TABLE.items():
|
|
72
|
+
assert infer_path_action(tool_name) == expected, (
|
|
73
|
+
f"infer_path_action({tool_name!r}) diverged from Go parity table"
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_regression_cases_previously_missed_by_substring():
|
|
78
|
+
for tool_name, expected in REGRESSION_TABLE.items():
|
|
79
|
+
assert infer_path_action(tool_name) == expected, (
|
|
80
|
+
f"infer_path_action({tool_name!r}) regressed"
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_false_positive_guards():
|
|
85
|
+
for tool_name, expected in FALSE_POSITIVE_TABLE.items():
|
|
86
|
+
assert infer_path_action(tool_name) == expected, (
|
|
87
|
+
f"infer_path_action({tool_name!r}) should be {expected!r} "
|
|
88
|
+
f"(prefix, not substring, matching)"
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def test_non_string_input_is_empty():
|
|
93
|
+
for bad in (None, 123, ["read_file"], {"name": "read_file"}, object()):
|
|
94
|
+
assert infer_path_action(bad) == ""
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def test_case_insensitive():
|
|
98
|
+
# HasPrefix is applied to strings.ToLower(toolName) on the Go side; we
|
|
99
|
+
# lowercase too, so mixed case still resolves.
|
|
100
|
+
assert infer_path_action("READ_File") == "read"
|
|
101
|
+
assert infer_path_action("Write_Thing") == "write"
|
|
102
|
+
assert infer_path_action("DELETE_it") == "delete"
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
if __name__ == "__main__":
|
|
106
|
+
# Allow a framework-free smoke run without pytest installed.
|
|
107
|
+
for table in (GO_PARITY_TABLE, REGRESSION_TABLE, FALSE_POSITIVE_TABLE):
|
|
108
|
+
for name, want in table.items():
|
|
109
|
+
got = infer_path_action(name)
|
|
110
|
+
assert got == want, f"{name!r}: got {got!r}, want {want!r}"
|
|
111
|
+
for bad in (None, 123, ["x"], {"a": 1}):
|
|
112
|
+
assert infer_path_action(bad) == ""
|
|
113
|
+
print("action-parity smoke OK")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentguardproxy-0.9.0 → agentguardproxy-1.0.0}/agentguardproxy.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|