@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miller-tech/uap",
3
- "version": "1.172.14",
3
+ "version": "1.172.15",
4
4
  "description": "Autonomous AI agent memory system with CLAUDE.md protocol enforcement",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -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 policy-gate hook runs in the harness env, not the inline command env,
135
- # so `UAP_NO_REVIEW=1 git commit ...` never reaches os.environ above. Honor
136
- # an inline assignment parsed from the command string too.
137
- # Anchored to a LEADING env-assignment run so an incidental mention in a
138
- # quoted arg / commit message does NOT silently skip review.
139
- if re.search(r"^\s*(?:[A-Za-z_]\w*=\S*\s+)*UAP_NO_REVIEW=['\"]?1\b", cmd):
140
- emit(True, "UAP_NO_REVIEW inline override set")
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. Override for one-off meta-work: UAP_NO_REVIEW=1.",
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. Override: UAP_NO_REVIEW=1.",
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.
@@ -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()