@miller-tech/uap 1.179.6 → 1.179.8
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.
- package/dist/.tsbuildinfo +1 -1
- package/dist/delivery/agentic-executor.d.ts +33 -0
- package/dist/delivery/agentic-executor.d.ts.map +1 -1
- package/dist/delivery/agentic-executor.js +100 -3
- package/dist/delivery/agentic-executor.js.map +1 -1
- package/docs/guides/PROXY.md +17 -1
- package/package.json +2 -2
- package/src/policies/enforcers/__pycache__/_common.cpython-312.pyc +0 -0
- package/templates/hooks/__pycache__/deliver_autoroute.cpython-312.pyc +0 -0
- package/tools/agents/scripts/__pycache__/toolcall_path_normalizer.cpython-312.pyc +0 -0
- package/tools/agents/scripts/anthropic_proxy.py +150 -4
- package/tools/agents/tests/__init__.py +27 -0
- package/tools/agents/tests/conftest.py +27 -0
- package/tools/agents/tests/test_proxy_env_loader.py +140 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Test package init — keeps importing the proxy from mutating this process.
|
|
2
|
+
|
|
3
|
+
`anthropic_proxy` loads `.uap/proxy.env` into the real `os.environ` at import.
|
|
4
|
+
That is right for a server run and wrong for a test run: it makes tests assert
|
|
5
|
+
the developer's proxy config instead of the shipped defaults, and it leaks
|
|
6
|
+
UAP_DELIVER_* settings into every enforcer subprocess the suite spawns, so
|
|
7
|
+
results depend on module import order.
|
|
8
|
+
|
|
9
|
+
Setting the opt-out here rather than only in `conftest.py` covers every entry
|
|
10
|
+
path, because all of them import this package first:
|
|
11
|
+
|
|
12
|
+
- `python -m unittest tools.agents.tests.test_x` (what CI runs)
|
|
13
|
+
- `python -m unittest discover -s tools/agents/tests`
|
|
14
|
+
- `loadTestsFromNames(...)` in test_enforcer_suite_coverage
|
|
15
|
+
- pytest (which also reads conftest.py)
|
|
16
|
+
|
|
17
|
+
`python3 tools/agents/tests/test_x.py` — running a file directly by path — is
|
|
18
|
+
the one shape that does NOT import the package, which is why conftest.py and
|
|
19
|
+
the `test:enforcers` script both still set it too.
|
|
20
|
+
|
|
21
|
+
`setdefault`, so an explicit `UAP_PROXY_ENV_AUTOLOAD=1` in the environment
|
|
22
|
+
still wins for anyone who deliberately wants the real file loaded.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
import os
|
|
26
|
+
|
|
27
|
+
os.environ.setdefault("UAP_PROXY_ENV_AUTOLOAD", "0")
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Keep importing the proxy from mutating the test process's environment.
|
|
2
|
+
|
|
3
|
+
`anthropic_proxy` loads `.uap/proxy.env` into the real `os.environ` at import
|
|
4
|
+
so a server run gets the operator's recipe/delivery/auth settings without
|
|
5
|
+
hand-exported env. Under test that is a liability, and it produced two real
|
|
6
|
+
failures:
|
|
7
|
+
|
|
8
|
+
1. Tests that assert shipped defaults (timeouts, retry budgets) instead
|
|
9
|
+
asserted whatever the developer's `.uap/proxy.env` happened to contain —
|
|
10
|
+
green in CI, red locally, for no code reason.
|
|
11
|
+
2. `UAP_DELIVER_LOCAL_MODE` from that file reached every enforcer subprocess
|
|
12
|
+
the suite spawns, so `test_delivery_enforcement_worktree` passed or failed
|
|
13
|
+
depending on whether a proxy-importing module had run before it.
|
|
14
|
+
|
|
15
|
+
Setting the opt-out here covers every pytest run from one place instead of
|
|
16
|
+
each module defending itself. `npm run test:enforcers` sets the same variable
|
|
17
|
+
for the unittest path, which does not read conftest.py.
|
|
18
|
+
|
|
19
|
+
This must run before any test module imports the proxy — conftest is imported
|
|
20
|
+
at collection time, ahead of the test modules, which is exactly that point.
|
|
21
|
+
Tests that specifically exercise the loader call `_load_proxy_env_file()`
|
|
22
|
+
directly and are unaffected.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
import os
|
|
26
|
+
|
|
27
|
+
os.environ.setdefault("UAP_PROXY_ENV_AUTOLOAD", "0")
|
|
@@ -3,6 +3,7 @@ import importlib.util
|
|
|
3
3
|
import os
|
|
4
4
|
import tempfile
|
|
5
5
|
import unittest
|
|
6
|
+
import unittest.mock
|
|
6
7
|
from pathlib import Path
|
|
7
8
|
|
|
8
9
|
|
|
@@ -55,5 +56,144 @@ class ProxyEnvLoaderTest(unittest.TestCase):
|
|
|
55
56
|
proxy._load_proxy_env_file() # must not raise
|
|
56
57
|
|
|
57
58
|
|
|
59
|
+
class ProxyEnvWalkStopsAtRepoRootTest(unittest.TestCase):
|
|
60
|
+
"""The upward walk must stop at the repository, not the filesystem root.
|
|
61
|
+
|
|
62
|
+
It used to run to "/", so a checkout nested under an unrelated checkout
|
|
63
|
+
loaded the outer repo's proxy.env, PROXY_AUTH_TOKEN included. A WORKTREE is
|
|
64
|
+
not that case — its .git file names the same repository — so it must still
|
|
65
|
+
reach its own repo's config rather than start tokenless.
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
KEY = "UAP_WALK_PROBE_KEY"
|
|
69
|
+
|
|
70
|
+
def tearDown(self):
|
|
71
|
+
os.environ.pop(self.KEY, None)
|
|
72
|
+
os.environ.pop("UAP_PROXY_ENV_FILE", None)
|
|
73
|
+
|
|
74
|
+
def _tree(self, td):
|
|
75
|
+
# outer/ is the "parent checkout" and holds the env file; outer/inner/
|
|
76
|
+
# is the "worktree" — it has a .git FILE (as real worktrees do) and no
|
|
77
|
+
# .uap/ of its own.
|
|
78
|
+
outer = Path(td) / "outer"
|
|
79
|
+
(outer / ".uap").mkdir(parents=True)
|
|
80
|
+
(outer / ".git").mkdir()
|
|
81
|
+
(outer / ".uap" / "proxy.env").write_text(f"{self.KEY}=from_the_repo\n")
|
|
82
|
+
inner = outer / "inner"
|
|
83
|
+
inner.mkdir()
|
|
84
|
+
(inner / ".git").write_text("gitdir: ../.git/worktrees/inner\n")
|
|
85
|
+
return outer, inner
|
|
86
|
+
|
|
87
|
+
def _load_from(self, path):
|
|
88
|
+
# cwd is process-global; restore it even if the loader raises. Safe
|
|
89
|
+
# under unittest (serial) and pytest-xdist (separate processes); would
|
|
90
|
+
# not be under a thread-parallel runner, and nothing here runs threaded.
|
|
91
|
+
cwd = os.getcwd()
|
|
92
|
+
os.chdir(path)
|
|
93
|
+
try:
|
|
94
|
+
os.environ.pop(self.KEY, None)
|
|
95
|
+
proxy._load_proxy_env_file()
|
|
96
|
+
finally:
|
|
97
|
+
os.chdir(cwd)
|
|
98
|
+
return os.environ.get(self.KEY)
|
|
99
|
+
|
|
100
|
+
def test_a_worktree_still_reaches_its_own_repository_config(self):
|
|
101
|
+
# A worktree's .git is a FILE naming the same repository. Stopping there
|
|
102
|
+
# would leave the proxy with an empty PROXY_AUTH_TOKEN, and since the
|
|
103
|
+
# auth middleware treats empty as "no auth configured", a non-loopback
|
|
104
|
+
# bind would then be wide open. Same repo => follow the pointer home.
|
|
105
|
+
with tempfile.TemporaryDirectory() as td:
|
|
106
|
+
_outer, inner = self._tree(td)
|
|
107
|
+
self.assertEqual(self._load_from(inner), "from_the_repo")
|
|
108
|
+
|
|
109
|
+
def test_still_finds_the_env_file_at_the_repo_root_itself(self):
|
|
110
|
+
# The bound is inclusive — stopping AT the repo root must not stop
|
|
111
|
+
# BEFORE reading it, or the proxy loses its own config.
|
|
112
|
+
with tempfile.TemporaryDirectory() as td:
|
|
113
|
+
outer, _inner = self._tree(td)
|
|
114
|
+
self.assertEqual(self._load_from(outer), "from_the_repo")
|
|
115
|
+
|
|
116
|
+
def test_the_walk_actually_ascends(self):
|
|
117
|
+
# Both cases above sit ON a boundary directory, so an implementation
|
|
118
|
+
# that only ever looked at Path.cwd() would pass them. Start well below
|
|
119
|
+
# the root, in a dir with neither .git nor .uap.
|
|
120
|
+
with tempfile.TemporaryDirectory() as td:
|
|
121
|
+
outer, _inner = self._tree(td)
|
|
122
|
+
deep = outer / "a" / "b" / "c"
|
|
123
|
+
deep.mkdir(parents=True)
|
|
124
|
+
self.assertEqual(self._load_from(deep), "from_the_repo")
|
|
125
|
+
|
|
126
|
+
def test_does_not_climb_out_of_one_repository_into_another(self):
|
|
127
|
+
# The genuine cross-repo case the bound exists for: a checkout nested
|
|
128
|
+
# under an unrelated checkout must not inherit the outer repo's secret.
|
|
129
|
+
with tempfile.TemporaryDirectory() as td:
|
|
130
|
+
outer, _inner = self._tree(td)
|
|
131
|
+
nested = outer / "vendored"
|
|
132
|
+
(nested / ".git").mkdir(parents=True) # a real repo root, not a worktree
|
|
133
|
+
self.assertIsNone(
|
|
134
|
+
self._load_from(nested),
|
|
135
|
+
"walk escaped a nested repository into the enclosing checkout",
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class ProxyBindAuthGuardTest(unittest.TestCase):
|
|
140
|
+
"""A non-loopback bind with no token must fail closed.
|
|
141
|
+
|
|
142
|
+
PROXY_AUTH_TOKEN comes from .uap/proxy.env; PROXY_HOST commonly comes from
|
|
143
|
+
the systemd EnvironmentFile. So any failure to find the former leaves the
|
|
144
|
+
latter intact — bind-all survives, require-a-credential does not — and the
|
|
145
|
+
middleware reads an empty token as "no auth configured". Without this guard
|
|
146
|
+
a config-discovery bug silently produces an open LLM proxy on the LAN.
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
def test_loopback_without_a_token_is_allowed(self):
|
|
150
|
+
for host in ("127.0.0.1", "::1", "localhost"):
|
|
151
|
+
with self.subTest(host=host):
|
|
152
|
+
proxy._assert_bind_is_authenticated(host, "") # must not raise
|
|
153
|
+
|
|
154
|
+
def test_any_bind_with_a_token_is_allowed(self):
|
|
155
|
+
proxy._assert_bind_is_authenticated("0.0.0.0", "a-real-token")
|
|
156
|
+
|
|
157
|
+
def test_non_loopback_without_a_token_refuses_to_start(self):
|
|
158
|
+
for host in ("0.0.0.0", "192.168.1.50", "::"):
|
|
159
|
+
with self.subTest(host=host):
|
|
160
|
+
with unittest.mock.patch.dict(os.environ, {}, clear=False):
|
|
161
|
+
os.environ.pop("PROXY_ALLOW_UNAUTHENTICATED_BIND", None)
|
|
162
|
+
with self.assertRaises(SystemExit) as ctx:
|
|
163
|
+
proxy._assert_bind_is_authenticated(host, "")
|
|
164
|
+
self.assertIn("unauthenticated", str(ctx.exception))
|
|
165
|
+
|
|
166
|
+
def test_explicit_override_permits_an_open_listener(self):
|
|
167
|
+
with unittest.mock.patch.dict(
|
|
168
|
+
os.environ, {"PROXY_ALLOW_UNAUTHENTICATED_BIND": "1"}
|
|
169
|
+
):
|
|
170
|
+
proxy._assert_bind_is_authenticated("0.0.0.0", "") # must not raise
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class ProxyEnvAutoloadOptOutTest(unittest.TestCase):
|
|
174
|
+
"""Importing the module must be inert when the opt-out is set."""
|
|
175
|
+
|
|
176
|
+
def test_autoload_enabled_by_default(self):
|
|
177
|
+
with unittest.mock.patch.dict(os.environ, {}, clear=False):
|
|
178
|
+
os.environ.pop("UAP_PROXY_ENV_AUTOLOAD", None)
|
|
179
|
+
self.assertTrue(proxy._proxy_env_autoload_enabled())
|
|
180
|
+
|
|
181
|
+
def test_opt_out_values_disable_autoload(self):
|
|
182
|
+
for val in ("0", "off", "false", "no", "OFF", " 0 "):
|
|
183
|
+
with self.subTest(val=val):
|
|
184
|
+
with unittest.mock.patch.dict(
|
|
185
|
+
os.environ, {"UAP_PROXY_ENV_AUTOLOAD": val}
|
|
186
|
+
):
|
|
187
|
+
self.assertFalse(proxy._proxy_env_autoload_enabled())
|
|
188
|
+
|
|
189
|
+
def test_other_values_leave_autoload_on(self):
|
|
190
|
+
for val in ("1", "on", "true", "yes"):
|
|
191
|
+
with self.subTest(val=val):
|
|
192
|
+
with unittest.mock.patch.dict(
|
|
193
|
+
os.environ, {"UAP_PROXY_ENV_AUTOLOAD": val}
|
|
194
|
+
):
|
|
195
|
+
self.assertTrue(proxy._proxy_env_autoload_enabled())
|
|
196
|
+
|
|
197
|
+
|
|
58
198
|
if __name__ == "__main__":
|
|
59
199
|
unittest.main()
|