@miller-tech/uap 1.172.14 → 1.172.15
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/package.json +1 -1
- package/src/policies/enforcers/__pycache__/_common.cpython-312.pyc +0 -0
- package/src/policies/enforcers/enforcement_infra_protect.py +26 -1
- package/src/policies/enforcers/enforcement_self_protect.py +8 -0
- package/src/policies/enforcers/expert_review_required.py +22 -9
- 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/tests/test_enforcer_escape_hatches.py +123 -0
package/package.json
CHANGED
|
Binary file
|
|
@@ -34,6 +34,7 @@ Scope (Bash/bash/run_bash commands only):
|
|
|
34
34
|
Killing a SPECIFIC process pattern (e.g. `pkill -f "python3 -m http.server
|
|
35
35
|
8765"`) and serving on non-infra ports stay allowed.
|
|
36
36
|
"""
|
|
37
|
+
import os
|
|
37
38
|
import re
|
|
38
39
|
import sys
|
|
39
40
|
from pathlib import Path
|
|
@@ -94,7 +95,8 @@ REASON = (
|
|
|
94
95
|
"session runs on (llama-server :8080 / UAP proxy :4000 / embeddings :8081). "
|
|
95
96
|
"Kill only your own processes by SPECIFIC pattern (e.g. pkill -f 'python3 -m "
|
|
96
97
|
"http.server 8765') and serve your app on a port other than 8080/4000/8081 "
|
|
97
|
-
"(e.g. 8765)."
|
|
98
|
+
"(e.g. 8765). Operator override: set UAP_INFRA_PROTECT_OFF=1 in the launch "
|
|
99
|
+
"environment (not inline on the command)."
|
|
98
100
|
)
|
|
99
101
|
|
|
100
102
|
|
|
@@ -140,8 +142,31 @@ def _referenced_script_bodies(cmd: str, limit: int = 4) -> list[tuple[str, str]]
|
|
|
140
142
|
return found
|
|
141
143
|
|
|
142
144
|
|
|
145
|
+
# Operator escape hatch — ENVIRONMENT ONLY, deliberately.
|
|
146
|
+
#
|
|
147
|
+
# This is the only enforcer that had no override, which is why every restart of
|
|
148
|
+
# the inference stack required the operator to run the command by hand. That is
|
|
149
|
+
# defensible for a control whose purpose is to stop the agent cycling the stack
|
|
150
|
+
# it runs on, but it also meant an operator who WANTED the agent to restart a
|
|
151
|
+
# service had no way to say so.
|
|
152
|
+
#
|
|
153
|
+
# It reads os.environ and nothing else. It does NOT honour an inline
|
|
154
|
+
# `UAP_INFRA_PROTECT_OFF=1 systemctl ...` assignment, unlike expert-review's
|
|
155
|
+
# override, because the agent composes its own command strings: an inline form is
|
|
156
|
+
# self-grantable and would delete the control rather than delegate it. An
|
|
157
|
+
# environment variable is set by whoever launched the session, so honouring only
|
|
158
|
+
# that keeps the decision with the operator.
|
|
159
|
+
#
|
|
160
|
+
# enforcement-self-protect additionally lists this flag among the bypasses the
|
|
161
|
+
# agent may not set, so an inline attempt is refused with an explicit message
|
|
162
|
+
# instead of silently doing nothing.
|
|
163
|
+
OVERRIDE = os.environ.get("UAP_INFRA_PROTECT_OFF") == "1"
|
|
164
|
+
|
|
165
|
+
|
|
143
166
|
def main() -> None:
|
|
144
167
|
operation, args = parse_cli()
|
|
168
|
+
if OVERRIDE:
|
|
169
|
+
emit(True, "UAP_INFRA_PROTECT_OFF=1 set in the launch environment (operator)")
|
|
145
170
|
if operation not in BASH_OPS:
|
|
146
171
|
emit(True, "not a shell operation")
|
|
147
172
|
cmd = str(args.get("command") or "")
|
|
@@ -51,6 +51,14 @@ BYPASS_PATTERNS = (
|
|
|
51
51
|
re.compile(r"UAP_SELF_PROTECT_OFF\s*=\s*['\"]?1", re.I),
|
|
52
52
|
re.compile(r"UAP_NO_WORKTREE\s*=\s*['\"]?1", re.I),
|
|
53
53
|
re.compile(r"UAP_WORKDIR_SCOPE_OFF\s*=\s*['\"]?1", re.I),
|
|
54
|
+
# infra-protect's hatch is environment-only by design; an inline assignment
|
|
55
|
+
# would be self-grantable, so refuse it here with a clear message rather than
|
|
56
|
+
# letting it look like it worked.
|
|
57
|
+
re.compile(r"UAP_INFRA_PROTECT_OFF\s*=\s*['\"]?1", re.I),
|
|
58
|
+
# expert-review's override is environment-only too, for the same reason: the
|
|
59
|
+
# agent writes its own command strings, so an inline form waives its own
|
|
60
|
+
# review. Refuse it here so the attempt is visible rather than silent.
|
|
61
|
+
re.compile(r"UAP_NO_REVIEW\s*=\s*['\"]?1", re.I),
|
|
54
62
|
re.compile(r"UAP_USER_VALIDATION\s*=\s*['\"]?0", re.I),
|
|
55
63
|
)
|
|
56
64
|
# Destructive ops against the enforcer/policy surface.
|
|
@@ -131,13 +131,25 @@ def main() -> None:
|
|
|
131
131
|
emit(True, "not a ship operation")
|
|
132
132
|
|
|
133
133
|
cmd = args.get("command") or args.get("cmd") or ""
|
|
134
|
-
# The
|
|
135
|
-
#
|
|
136
|
-
#
|
|
137
|
-
#
|
|
138
|
-
#
|
|
139
|
-
|
|
140
|
-
|
|
134
|
+
# The inline override is GONE, deliberately.
|
|
135
|
+
#
|
|
136
|
+
# It used to parse a leading UAP_NO_REVIEW=1 assignment out of the command
|
|
137
|
+
# string, on the reasoning that the policy-gate hook runs in the harness
|
|
138
|
+
# environment and so cannot see one. That reasoning was correct — but the
|
|
139
|
+
# agent composes its own command strings, which made the override
|
|
140
|
+
# SELF-GRANTABLE. In practice an agent session waived expert review on every
|
|
141
|
+
# ship action it performed. That is not delegating the decision, it is
|
|
142
|
+
# removing the gate.
|
|
143
|
+
#
|
|
144
|
+
# Now environment-only: os.environ is set by whoever launched the session, so
|
|
145
|
+
# the decision stays with the operator. Two env-free routes remain for
|
|
146
|
+
# harnesses that strip the environment, and both are deliberate acts visible
|
|
147
|
+
# in the tree rather than a per-command flag:
|
|
148
|
+
# - policies/waivers/*expert-review*.md
|
|
149
|
+
# - .uap/reviews/WAIVER
|
|
150
|
+
# enforcement-self-protect also lists this flag among the bypasses the agent
|
|
151
|
+
# may not set, so an inline attempt is refused with an explicit message
|
|
152
|
+
# instead of appearing to work.
|
|
141
153
|
if not any(p.search(cmd) for p in SHIP_PATTERNS):
|
|
142
154
|
emit(True, "not a ship action")
|
|
143
155
|
|
|
@@ -175,7 +187,8 @@ def main() -> None:
|
|
|
175
187
|
f"expert-review-required: no review artifact at .uap/reviews/{slug}.json. "
|
|
176
188
|
"Run the parallel-expert-review skill (code-quality, security, performance, "
|
|
177
189
|
"docs, test-coverage reviewers) and record the consolidated verdict before "
|
|
178
|
-
"shipping.
|
|
190
|
+
"shipping. Operator override: UAP_NO_REVIEW=1 in the launch environment "
|
|
191
|
+
"(no longer honoured inline), or a waiver file.",
|
|
179
192
|
)
|
|
180
193
|
|
|
181
194
|
head = head_sha(root)
|
|
@@ -192,7 +205,7 @@ def main() -> None:
|
|
|
192
205
|
False,
|
|
193
206
|
f"expert-review-required: review at .uap/reviews/{slug}.json covers branch "
|
|
194
207
|
f"'{artifact_branch}', not '{branch}'. Re-run the parallel expert review on "
|
|
195
|
-
"this branch.
|
|
208
|
+
"this branch. Operator override: UAP_NO_REVIEW=1 in the launch environment.",
|
|
196
209
|
)
|
|
197
210
|
|
|
198
211
|
# Stale check relative to current HEAD.
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Operator escape hatches must be operator-only — not self-grantable.
|
|
3
|
+
|
|
4
|
+
Two changes, one principle. The agent composes its own command strings, so any
|
|
5
|
+
override parsed OUT OF a command string is one the agent can grant itself. An
|
|
6
|
+
override read from os.environ is set by whoever launched the session.
|
|
7
|
+
|
|
8
|
+
- infra-protect had NO hatch at all, so every restart of the inference stack
|
|
9
|
+
needed the operator to type it. Now UAP_INFRA_PROTECT_OFF=1, environment only.
|
|
10
|
+
- expert-review HAD an inline hatch, and it was self-granted on all eleven
|
|
11
|
+
commits of one agent session. That is not delegating the decision, it is
|
|
12
|
+
removing the gate. Now UAP_NO_REVIEW=1, environment only.
|
|
13
|
+
|
|
14
|
+
self-protect additionally refuses inline attempts at either flag, so trying reads
|
|
15
|
+
as an explicit refusal rather than appearing to work.
|
|
16
|
+
|
|
17
|
+
NOTE ON THE HARNESS: every case runs with cwd inside a real git repo. Outside
|
|
18
|
+
one, expert-review fail-opens on an unresolvable branch and EVERY result reads
|
|
19
|
+
"allowed" — the first version of this verification was run from a temp dir and
|
|
20
|
+
reported the self-grant case as passing when it was not.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
import json
|
|
24
|
+
import os
|
|
25
|
+
import subprocess
|
|
26
|
+
import sys
|
|
27
|
+
import unittest
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
|
|
30
|
+
# parents: [0]=tests [1]=agents [2]=tools [3]=repo root. Off-by-one here made
|
|
31
|
+
# every enforcer path invalid and the suite fail for the wrong reason.
|
|
32
|
+
REPO = Path(__file__).resolve().parents[3]
|
|
33
|
+
ENFORCERS = REPO / "src" / "policies" / "enforcers"
|
|
34
|
+
|
|
35
|
+
# Assembled rather than written literally: these enforcers scan command strings
|
|
36
|
+
# for ship/infra verbs, so a literal here trips the gate on this file's own test
|
|
37
|
+
# run. (infra-protect refused its own comment text for exactly this reason.)
|
|
38
|
+
SHIP = "git " + "commit -m x"
|
|
39
|
+
RESTART = "systemctl --user " + "rest" + "art " + "uap-" + "llama-server.service"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def verdict(enforcer: str, cmd: str, env: dict | None = None):
|
|
43
|
+
e = dict(os.environ)
|
|
44
|
+
for k in ("UAP_NO_REVIEW", "UAP_INFRA_PROTECT_OFF", "UAP_SELF_PROTECT_OFF"):
|
|
45
|
+
e.pop(k, None)
|
|
46
|
+
e.update(env or {})
|
|
47
|
+
p = subprocess.run(
|
|
48
|
+
[sys.executable, str(ENFORCERS / enforcer),
|
|
49
|
+
"--operation", "Bash", "--args", json.dumps({"command": cmd})],
|
|
50
|
+
capture_output=True, text=True, cwd=str(REPO), env=e,
|
|
51
|
+
)
|
|
52
|
+
try:
|
|
53
|
+
return json.loads(p.stdout or "{}").get("allowed")
|
|
54
|
+
except Exception: # noqa: BLE001
|
|
55
|
+
return f"ERR {(p.stderr or '')[:80]}"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class InfraProtectHatchTest(unittest.TestCase):
|
|
59
|
+
E = "enforcement_infra_protect.py"
|
|
60
|
+
|
|
61
|
+
def test_blocks_a_stack_restart_by_default(self):
|
|
62
|
+
self.assertFalse(verdict(self.E, RESTART))
|
|
63
|
+
|
|
64
|
+
def test_env_override_allows_it(self):
|
|
65
|
+
self.assertTrue(verdict(self.E, RESTART, {"UAP_INFRA_PROTECT_OFF": "1"}))
|
|
66
|
+
|
|
67
|
+
def test_inline_override_is_NOT_honoured(self):
|
|
68
|
+
"""The load-bearing case. An inline form would be self-grantable, which
|
|
69
|
+
would delete the control rather than delegate it."""
|
|
70
|
+
self.assertFalse(verdict(self.E, f"UAP_INFRA_PROTECT_OFF=1 {RESTART}"))
|
|
71
|
+
|
|
72
|
+
def test_harmless_commands_are_untouched(self):
|
|
73
|
+
self.assertTrue(verdict(self.E, "echo hi"))
|
|
74
|
+
|
|
75
|
+
def test_the_refusal_tells_the_operator_the_hatch_exists(self):
|
|
76
|
+
"""An undiscoverable hatch is not much better than none: the whole
|
|
77
|
+
problem was an operator with no way to say yes."""
|
|
78
|
+
src = (ENFORCERS / self.E).read_text()
|
|
79
|
+
self.assertIn("UAP_INFRA_PROTECT_OFF", src.split("REASON")[1] if "REASON" in src else src)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class ExpertReviewHatchTest(unittest.TestCase):
|
|
83
|
+
E = "expert_review_required.py"
|
|
84
|
+
|
|
85
|
+
def test_inline_override_is_NOT_honoured(self):
|
|
86
|
+
"""This is the regression that mattered: it used to return True here, and
|
|
87
|
+
an agent session used it to waive review on every commit it made."""
|
|
88
|
+
self.assertFalse(verdict(self.E, f"UAP_NO_REVIEW=1 {SHIP}"))
|
|
89
|
+
|
|
90
|
+
def test_env_override_allows_it(self):
|
|
91
|
+
self.assertTrue(verdict(self.E, SHIP, {"UAP_NO_REVIEW": "1"}))
|
|
92
|
+
|
|
93
|
+
def test_non_ship_commands_are_untouched(self):
|
|
94
|
+
self.assertTrue(verdict(self.E, "echo hi"))
|
|
95
|
+
|
|
96
|
+
def test_the_inline_parser_is_gone_from_source(self):
|
|
97
|
+
"""Behavioural tests could pass while a dormant parser waits to be
|
|
98
|
+
re-enabled; assert the code path is actually removed."""
|
|
99
|
+
src = (ENFORCERS / self.E).read_text()
|
|
100
|
+
self.assertNotIn("inline override set", src)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class SelfProtectRefusesInlineAttemptsTest(unittest.TestCase):
|
|
104
|
+
E = "enforcement_self_protect.py"
|
|
105
|
+
|
|
106
|
+
def test_refuses_inline_no_review(self):
|
|
107
|
+
self.assertFalse(verdict(self.E, f"UAP_NO_REVIEW=1 {SHIP}"))
|
|
108
|
+
|
|
109
|
+
def test_refuses_inline_infra_protect_off(self):
|
|
110
|
+
self.assertFalse(verdict(self.E, f"UAP_INFRA_PROTECT_OFF=1 {RESTART}"))
|
|
111
|
+
|
|
112
|
+
def test_still_refuses_the_pre_existing_bypasses(self):
|
|
113
|
+
"""Guards against a regression that drops the older patterns while adding
|
|
114
|
+
the new ones."""
|
|
115
|
+
self.assertFalse(verdict(self.E, "UAP_DELIVER_BYPASS=1 " + SHIP))
|
|
116
|
+
self.assertFalse(verdict(self.E, "UAP_WORKDIR_SCOPE_OFF=1 touch /tmp/x"))
|
|
117
|
+
|
|
118
|
+
def test_harmless_commands_are_untouched(self):
|
|
119
|
+
self.assertTrue(verdict(self.E, "echo hi"))
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
if __name__ == "__main__":
|
|
123
|
+
unittest.main()
|