@miller-tech/uap 1.61.3 → 1.63.0

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.
@@ -0,0 +1,134 @@
1
+ #!/usr/bin/env python3
2
+ """Tests for the workdir-scope policy enforcer.
3
+
4
+ Invoked the way the policy gate invokes it: a subprocess given --operation and
5
+ --args, returning {"allowed": bool, "reason": str} on stdout and exit 0/2. The
6
+ project roots are supplied via UAP_REPO_ROOT / UAP_WORKTREE_ROOT (as the gate
7
+ does), pointed at a temp directory so tests are hermetic.
8
+ """
9
+
10
+ import json
11
+ import os
12
+ import subprocess
13
+ import sys
14
+ import tempfile
15
+ import unittest
16
+ from pathlib import Path
17
+
18
+ ENFORCER = (
19
+ Path(__file__).resolve().parents[3]
20
+ / "src" / "policies" / "enforcers" / "workdir_scope.py"
21
+ )
22
+
23
+
24
+ def run(op, args, root, env_extra=None):
25
+ env = dict(os.environ)
26
+ env["UAP_REPO_ROOT"] = str(root)
27
+ env["UAP_WORKTREE_ROOT"] = str(root)
28
+ env.pop("UAP_WORKDIR_SCOPE_OFF", None)
29
+ env.pop("UAP_WORKDIR_ALLOW", None)
30
+ if env_extra:
31
+ env.update(env_extra)
32
+ p = subprocess.run(
33
+ [sys.executable, str(ENFORCER), "--operation", op, "--args", json.dumps(args)],
34
+ capture_output=True, text=True, env=env, cwd=str(root),
35
+ )
36
+ try:
37
+ out = json.loads(p.stdout)
38
+ except json.JSONDecodeError:
39
+ out = {"allowed": True, "reason": f"<unparseable: {p.stdout!r} {p.stderr!r}>"}
40
+ return out, p.returncode
41
+
42
+
43
+ class TestWorkdirScopeEnforcer(unittest.TestCase):
44
+ def setUp(self):
45
+ self._tmp = tempfile.TemporaryDirectory()
46
+ self.root = Path(self._tmp.name).resolve()
47
+ (self.root / ".worktrees" / "001-x").mkdir(parents=True)
48
+
49
+ def tearDown(self):
50
+ self._tmp.cleanup()
51
+
52
+ def _allow(self, out, code, msg=""):
53
+ self.assertTrue(out["allowed"], f"{msg}: {out}")
54
+ self.assertEqual(code, 0)
55
+
56
+ def _block(self, out, code, msg=""):
57
+ self.assertFalse(out["allowed"], f"{msg}: {out}")
58
+ self.assertEqual(code, 2)
59
+ self.assertIn("workdir-scope", out["reason"])
60
+
61
+ # --- file-write tools ---
62
+
63
+ def test_write_inside_root_allowed(self):
64
+ out, c = run("Write", {"file_path": str(self.root / "src/a.js")}, self.root)
65
+ self._allow(out, c, "in-root write")
66
+
67
+ def test_write_outside_root_blocked(self):
68
+ out, c = run("Write", {"file_path": "/home/cogtek/dev/octopusspace-shooter/x.js"}, self.root)
69
+ self._block(out, c, "out-of-root write")
70
+
71
+ def test_relative_path_allowed(self):
72
+ out, c = run("Edit", {"file_path": "src/a.js"}, self.root)
73
+ self._allow(out, c, "relative path")
74
+
75
+ def test_scratch_tmp_allowed(self):
76
+ out, c = run("Write", {"file_path": "/tmp/uap-scratch/x"}, self.root)
77
+ self._allow(out, c, "/tmp scratch")
78
+
79
+ def test_worktree_path_allowed(self):
80
+ out, c = run("Write", {"file_path": str(self.root / ".worktrees/001-x/f.ts")}, self.root)
81
+ self._allow(out, c, "worktree path")
82
+
83
+ def test_notebook_edit_outside_blocked(self):
84
+ out, c = run("NotebookEdit", {"notebook_path": "/etc/evil.ipynb"}, self.root)
85
+ self._block(out, c, "notebook outside")
86
+
87
+ # --- bash ---
88
+
89
+ def test_bash_mkdir_outside_blocked(self):
90
+ out, c = run("Bash", {"command": "mkdir -p /home/cogtek/dev/octopusspace-shooter/js"}, self.root)
91
+ self._block(out, c, "mkdir outside")
92
+
93
+ def test_bash_mkdir_inside_allowed(self):
94
+ out, c = run("Bash", {"command": "mkdir -p ./src/new"}, self.root)
95
+ self._allow(out, c, "mkdir inside")
96
+
97
+ def test_bash_read_only_allowed(self):
98
+ out, c = run("Bash", {"command": "cat /etc/hosts && ls /usr/bin"}, self.root)
99
+ self._allow(out, c, "read-only command")
100
+
101
+ def test_bash_cp_dest_outside_blocked(self):
102
+ out, c = run("Bash", {"command": "cp ./a.txt /var/tmp2/out/b.txt"}, self.root)
103
+ self._block(out, c, "cp dest outside")
104
+
105
+ def test_bash_cp_source_outside_dest_inside_allowed(self):
106
+ out, c = run("Bash", {"command": "cp /etc/hosts ./local-hosts"}, self.root)
107
+ self._allow(out, c, "read source outside, write inside")
108
+
109
+ def test_bash_redirect_outside_blocked(self):
110
+ out, c = run("Bash", {"command": "echo hi > /opt/somewhere/file"}, self.root)
111
+ self._block(out, c, "redirect outside")
112
+
113
+ # --- overrides / non-mutating ---
114
+
115
+ def test_scope_off_override_allows(self):
116
+ out, c = run("Write", {"file_path": "/anywhere/x"}, self.root, {"UAP_WORKDIR_SCOPE_OFF": "1"})
117
+ self._allow(out, c, "scope-off override")
118
+
119
+ def test_workdir_allow_widens(self):
120
+ out, c = run("Write", {"file_path": "/opt/extra/x"}, self.root, {"UAP_WORKDIR_ALLOW": "/opt/extra"})
121
+ self._allow(out, c, "allow-list widened")
122
+
123
+ def test_non_path_op_allowed(self):
124
+ out, c = run("Grep", {"pattern": "foo"}, self.root)
125
+ self._allow(out, c, "non-path op")
126
+
127
+ def test_read_op_outside_allowed(self):
128
+ # Reading outside the workdir is fine; only mutations are scoped.
129
+ out, c = run("Read", {"file_path": "/etc/hosts"}, self.root)
130
+ self._allow(out, c, "read outside allowed")
131
+
132
+
133
+ if __name__ == "__main__":
134
+ unittest.main()