@miller-tech/uap 1.170.1 → 1.172.1

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.
Files changed (77) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/bin/cli.js +26 -0
  3. package/dist/bin/cli.js.map +1 -1
  4. package/dist/browser/web-browser.d.ts +17 -0
  5. package/dist/browser/web-browser.d.ts.map +1 -1
  6. package/dist/browser/web-browser.js +36 -0
  7. package/dist/browser/web-browser.js.map +1 -1
  8. package/dist/cli/interaction.d.ts +29 -0
  9. package/dist/cli/interaction.d.ts.map +1 -0
  10. package/dist/cli/interaction.js +217 -0
  11. package/dist/cli/interaction.js.map +1 -0
  12. package/dist/cli/verify.d.ts +6 -0
  13. package/dist/cli/verify.d.ts.map +1 -1
  14. package/dist/cli/verify.js +112 -7
  15. package/dist/cli/verify.js.map +1 -1
  16. package/dist/delivery/execution-gate.d.ts +7 -0
  17. package/dist/delivery/execution-gate.d.ts.map +1 -1
  18. package/dist/delivery/execution-gate.js +42 -1
  19. package/dist/delivery/execution-gate.js.map +1 -1
  20. package/dist/delivery/fidelity.d.ts +16 -0
  21. package/dist/delivery/fidelity.d.ts.map +1 -1
  22. package/dist/delivery/fidelity.js +7 -0
  23. package/dist/delivery/fidelity.js.map +1 -1
  24. package/dist/delivery/interaction/driver.d.ts +60 -0
  25. package/dist/delivery/interaction/driver.d.ts.map +1 -0
  26. package/dist/delivery/interaction/driver.js +18 -0
  27. package/dist/delivery/interaction/driver.js.map +1 -0
  28. package/dist/delivery/interaction/manifest.d.ts +84 -0
  29. package/dist/delivery/interaction/manifest.d.ts.map +1 -0
  30. package/dist/delivery/interaction/manifest.js +241 -0
  31. package/dist/delivery/interaction/manifest.js.map +1 -0
  32. package/dist/delivery/interaction/mine.d.ts +54 -0
  33. package/dist/delivery/interaction/mine.d.ts.map +1 -0
  34. package/dist/delivery/interaction/mine.js +218 -0
  35. package/dist/delivery/interaction/mine.js.map +1 -0
  36. package/dist/delivery/interaction/runner.d.ts +61 -0
  37. package/dist/delivery/interaction/runner.d.ts.map +1 -0
  38. package/dist/delivery/interaction/runner.js +359 -0
  39. package/dist/delivery/interaction/runner.js.map +1 -0
  40. package/dist/delivery/interaction/types.d.ts +223 -0
  41. package/dist/delivery/interaction/types.d.ts.map +1 -0
  42. package/dist/delivery/interaction/types.js +19 -0
  43. package/dist/delivery/interaction/types.js.map +1 -0
  44. package/dist/delivery/interaction/verdict.d.ts +26 -0
  45. package/dist/delivery/interaction/verdict.d.ts.map +1 -0
  46. package/dist/delivery/interaction/verdict.js +139 -0
  47. package/dist/delivery/interaction/verdict.js.map +1 -0
  48. package/dist/delivery/interaction/watchdog.d.ts +82 -0
  49. package/dist/delivery/interaction/watchdog.d.ts.map +1 -0
  50. package/dist/delivery/interaction/watchdog.js +210 -0
  51. package/dist/delivery/interaction/watchdog.js.map +1 -0
  52. package/dist/delivery/interaction/web-driver.d.ts +102 -0
  53. package/dist/delivery/interaction/web-driver.d.ts.map +1 -0
  54. package/dist/delivery/interaction/web-driver.js +306 -0
  55. package/dist/delivery/interaction/web-driver.js.map +1 -0
  56. package/dist/delivery/interaction-gate.d.ts +45 -0
  57. package/dist/delivery/interaction-gate.d.ts.map +1 -0
  58. package/dist/delivery/interaction-gate.js +255 -0
  59. package/dist/delivery/interaction-gate.js.map +1 -0
  60. package/dist/delivery/mission-acceptance.d.ts +2 -0
  61. package/dist/delivery/mission-acceptance.d.ts.map +1 -1
  62. package/dist/delivery/mission-acceptance.js +23 -0
  63. package/dist/delivery/mission-acceptance.js.map +1 -1
  64. package/dist/delivery/vision-judge.d.ts +21 -0
  65. package/dist/delivery/vision-judge.d.ts.map +1 -1
  66. package/dist/delivery/vision-judge.js +96 -0
  67. package/dist/delivery/vision-judge.js.map +1 -1
  68. package/dist/delivery/visual-gate.js.map +1 -1
  69. package/dist/types/config.d.ts +20 -0
  70. package/dist/types/config.d.ts.map +1 -1
  71. package/dist/types/config.js +7 -0
  72. package/dist/types/config.js.map +1 -1
  73. package/package.json +1 -1
  74. package/src/policies/enforcers/__pycache__/_common.cpython-312.pyc +0 -0
  75. package/src/policies/enforcers/enforcement_self_protect.py +96 -5
  76. package/templates/hooks/__pycache__/deliver_autoroute.cpython-312.pyc +0 -0
  77. package/tools/agents/scripts/__pycache__/toolcall_path_normalizer.cpython-312.pyc +0 -0
@@ -54,12 +54,103 @@ BYPASS_PATTERNS = (
54
54
  re.compile(r"UAP_USER_VALIDATION\s*=\s*['\"]?0", re.I),
55
55
  )
56
56
  # Destructive ops against the enforcer/policy surface.
57
- DESTRUCTIVE = re.compile(
58
- r"\b(rm|unlink|shred|truncate|mv|chmod)\b[^\n|;&]*"
59
- r"(\.policy-tools|src/policies|/policies/|anthropic-proxy\.env)",
60
- re.I,
57
+ #
58
+ # HONEST SCOPE: this is a NUDGE, not a boundary. An agent that can write a file
59
+ # and then run it (`Write tmp.sh` + `bash tmp.sh`), or use `python3 -c`, or hide
60
+ # a path in a variable, walks straight past anything a regex over shell text can
61
+ # see. What this catches is the naive, direct form — which is what was actually
62
+ # observed in the wild. The real control is that a missing manifest FAILS under
63
+ # max fidelity (see interaction-gate: skipped verdicts block when fidelity.max),
64
+ # so deleting the acceptance criteria is not rewarded with a pass.
65
+ #
66
+ # Matched at COMMAND POSITION, never in prose: `echo "do not rm .uap/x"` is a
67
+ # string, not a removal. This repo has already paid for that distinction once
68
+ # (5e26c66, "gates must match commands, not prose that mentions them").
69
+ DESTRUCTIVE_VERBS = (
70
+ "rm", "unlink", "shred", "truncate", "mv", "chmod", "chown", "chattr",
71
+ "dd", "cp", "tee", "install", "sed", "find", "ln",
61
72
  )
62
73
 
74
+ # Paths inside a protected tree that the agent legitimately writes. Without
75
+ # these the enforcer deadlocks the workflows it exists to support: the
76
+ # expert-review gate REQUIRES writing .uap/reviews/<branch>.json, and the visual
77
+ # and interaction gates write their own screenshots and evidence.
78
+ PROTECTED_EXEMPT = (
79
+ ".uap/interaction/evidence",
80
+ "policies/waivers",
81
+ )
82
+
83
+ # Deliberately NARROW inside .uap/: that directory is mostly runtime state the
84
+ # tooling writes constantly (verify-cadence, pending-deliver.jsonl, autoroute.log,
85
+ # visual screenshots, deliver logs) — the repo's own hook templates do
86
+ # `echo 0 > .uap/verify-cadence`. Guarding the whole tree would block the
87
+ # project's own plumbing while adding nothing: what must survive is the
88
+ # ACCEPTANCE CRITERIA and the project config, not the scratch state.
89
+ PROTECTED_TARGETS = (
90
+ ".policy-tools",
91
+ "src/policies",
92
+ "policies/",
93
+ ".uap/interaction",
94
+ ".uap.json",
95
+ "anthropic-proxy.env",
96
+ )
97
+
98
+ _SEGMENT_SPLIT = re.compile(r"(?:\|\||&&|[;\n|&])")
99
+ _ENV_ASSIGN = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*=\S*$")
100
+ _QUOTES = str.maketrans("", "", "\"'")
101
+
102
+
103
+ def _mentions_protected(text: str) -> bool:
104
+ """True when the text names a protected path and no exempt sub-path."""
105
+ low = text.translate(_QUOTES).lower()
106
+ # Normalise ./ and leading slashes so `./.uap`, `/.uap` and `.uap` agree.
107
+ low = low.replace("./", "/")
108
+ if any(ex in low for ex in PROTECTED_EXEMPT):
109
+ return False
110
+ # The .uap DIRECTORY ITSELF: `rm -rf .uap` / `find .uap -delete` destroy the
111
+ # manifest along with everything else, while `echo 0 > .uap/verify-cadence`
112
+ # names a deeper path and is ordinary tooling. So the bare directory is
113
+ # protected; a specific file under it is not (unless listed below).
114
+ for m in re.finditer(re.escape(".uap"), low):
115
+ if low[m.end():m.end() + 1] in ("", " ", '"', "'", "*"):
116
+ return True
117
+ for target in PROTECTED_TARGETS:
118
+ t = target.lower()
119
+ # Word-ish boundary so `.uap` matches `.uap`, `.uap/x` and `.uap.bak`
120
+ # but not an unrelated longer name like `.uapkeep`.
121
+ for m in re.finditer(re.escape(t), low):
122
+ tail = low[m.end():m.end() + 1]
123
+ if tail in ("", "/", ".", " ", '"', "'", "*"):
124
+ return True
125
+ return False
126
+
127
+
128
+ def _bash_destructive(command: str) -> bool:
129
+ """Destructive op against a protected path, judged per command segment."""
130
+ for segment in _SEGMENT_SPLIT.split(command or ""):
131
+ seg = segment.strip()
132
+ if not seg:
133
+ continue
134
+ # A redirect writes just as destructively as `rm`; the target may be
135
+ # quoted, ./-prefixed, or fd-numbered (`1> .uap/x`).
136
+ for m in re.finditer(r">>?", seg):
137
+ if _mentions_protected(seg[m.end():]):
138
+ return True
139
+ tokens = [t for t in seg.split() if not _ENV_ASSIGN.match(t)]
140
+ if not tokens:
141
+ continue
142
+ verb = tokens[0].rsplit("/", 1)[-1].lower()
143
+ if verb == "git" and len(tokens) > 1:
144
+ verb = f"git {tokens[1].lower()}"
145
+ if verb not in ("git clean", "git checkout"):
146
+ continue
147
+ elif verb not in DESTRUCTIVE_VERBS:
148
+ continue
149
+ if _mentions_protected(" ".join(tokens[1:])):
150
+ return True
151
+ return False
152
+
153
+
63
154
  OVERRIDE = os.environ.get("UAP_SELF_PROTECT_OFF") == "1"
64
155
 
65
156
 
@@ -123,7 +214,7 @@ def main() -> None:
123
214
  "`deliver` tool instead of disabling the gate. "
124
215
  "(Operator-only override: UAP_SELF_PROTECT_OFF=1.)",
125
216
  )
126
- if DESTRUCTIVE.search(cmd):
217
+ if _bash_destructive(cmd):
127
218
  emit(
128
219
  False,
129
220
  "BLOCKED: modifying/removing the policy enforcers or proxy env is "