1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
// SPDX-License-Identifier: Apache-2.0
// Copyright 2026 Jonathan Shook
//! The command allow-gates behind the dispatching tools.
//!
//! `ct-test` and `ct-each` can run another program, so each runs **only**
//! commands on a fixed, compiled-in list. The lists are intentionally **static
//! and immutable**: nothing a caller does at run time can extend them, so an
//! agent driving these tools cannot grant itself new commands. A command that
//! is not on the relevant list is refused, and nothing runs. There is no shell
//! mode anywhere in the suite — every dispatch is a direct argv launch.
//!
//! The allowlist is **platform-aware**, so the tools are usable both on Unix /
//! MSYS2 and on native Windows (no MSYS2 required): [`CORE`] is the suite's own
//! read-only `ct-*` tools, present on every OS, and [`NATIVE`] adds the host
//! OS's stock read-only utilities (coreutils on Unix; `findstr`/`where`/… on
//! Windows). [`builtin`] is their union for the current platform. This changes
//! *which names resolve per OS*, not the no-shell, direct-argv guarantee.
//!
//! * `ct-test` gates on [`builtin`]: read-only commands only.
//! * `ct-each` gates through [`is_allowed_for_each`]: [`builtin`] plus
//! `ct-test` (itself gated, so still read-only), and — only behind an
//! explicit `--mutating` flag — the suite's own [`MUTATING_SUITE`] tools,
//! which carry their own `--expect`/`--dry-run` safety gates.
//!
//! Gating is by **program name** (the file-name component of the command, with
//! a Windows executable suffix like `.exe` stripped). It is a guard against
//! unintended side effects, not a sandbox: it does not inspect arguments or
//! resolve which binary a name ultimately runs.
use Path;
/// The suite's own read-only tools — the cross-platform core of the allowlist,
/// present and resolvable on every OS.
///
/// The mutating/dispatching tools (`ct-edit`/`ct-patch`/`ct-rules`/`ct-test`/
/// `ct-each`) and the umbrella `ct` are excluded because they change state or
/// dispatch; `ct-await` is included as a read-only **observer** (it only polls
/// other read-only probes), which also lets it serve as a portable, bounded
/// long-running command. The crate-/module-graph checks (`deps`/`mods`) are not
/// dispatch targets — they are built-in checks the rule layer runs in-process.
pub const CORE: & = &;
/// The host OS's stock read-only utilities, added to [`CORE`]. Deliberately
/// small and conservative: names whose ordinary use has no side effects.
/// (`find` is excluded: `-delete`/`-exec` make it not read-only.) There is no
/// run-time mechanism to add to this list.
pub const NATIVE: & = &;
/// Stock read-only programs that exist on a bare Windows install (real `.exe`s,
/// launched directly — still no shell). `findstr` covers grep- and cat-style
/// needs (it can read a file or stdin); `more`/`where`/`whoami`/`hostname` round
/// out the read-only set.
pub const NATIVE: & = &;
pub const NATIVE: & = &;
/// `ct-test`'s entire read-only allowlist for the current platform: the
/// cross-platform [`CORE`] plus the OS's [`NATIVE`] utilities. Returned as an
/// owned list so callers can `join`/iterate it in messages.
/// The suite's mutating tools, runnable by `ct-each` only behind its explicit
/// `--mutating` flag. Each carries its own `--expect`/`--dry-run` gates, so a
/// dispatched edit still has to assert its own effect before writing.
pub const MUTATING_SUITE: & = &;
/// The program name the gates check for a command: its file-name component,
/// so `ls`, `/bin/ls`, and `./ls` all gate on `ls`. On Windows a trailing
/// executable suffix (`.exe`/`.com`/`.bat`/`.cmd`, case-insensitive) is
/// stripped, so an absolute or sibling path like `...\ct-search.exe` gates as
/// `ct-search`.
///
/// # Examples
///
/// ```
/// use coding_tools::allowlist::gated_name;
///
/// assert_eq!(gated_name("/bin/ls"), "ls");
/// assert_eq!(gated_name("./parse"), "parse");
/// ```
/// Strip a Windows executable suffix from a program's file name. A no-op on
/// non-Windows, where a file may legitimately be named e.g. `foo.exe`.
/// Whether `name` is on `ct-test`'s fixed read-only allowlist for the current
/// platform ([`CORE`] plus the OS's [`NATIVE`] utilities).
///
/// # Examples
///
/// ```
/// use coding_tools::allowlist::is_allowed;
///
/// assert!(is_allowed("ct-search")); // a suite read-only tool, on every platform
/// assert!(!is_allowed("rm")); // not read-only, never runnable
/// assert!(!is_allowed("sh")); // no shell, ever
/// ```
/// Whether `name` is a permitted `ct-each` dispatch target.
///
/// The base set is [`BUILTIN`] plus `ct-test` (which only runs read-only
/// commands itself, so dispatching it stays read-only). With `mutating`, the
/// suite's [`MUTATING_SUITE`] tools are also permitted — and nothing else:
/// arbitrary mutating commands are never runnable.
///
/// # Examples
///
/// ```
/// use coding_tools::allowlist::is_allowed_for_each;
///
/// assert!(is_allowed_for_each("ct-view", false));
/// assert!(is_allowed_for_each("ct-test", false)); // itself gated read-only
/// assert!(!is_allowed_for_each("ct-edit", false)); // needs --mutating
/// assert!(is_allowed_for_each("ct-edit", true));
/// assert!(!is_allowed_for_each("rm", true)); // never, even with --mutating
/// assert!(!is_allowed_for_each("sh", true)); // no shell, ever
/// ```