approval-md 0.4.3 → 0.4.6
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/SPEC.md +13 -2
- package/dist/src/channels/telegram.d.ts +105 -31
- package/dist/src/channels/telegram.js +157 -81
- package/dist/src/channels/telegram.js.map +1 -1
- package/dist/src/cli/channel-telegram.d.ts +100 -35
- package/dist/src/cli/channel-telegram.js +216 -119
- package/dist/src/cli/channel-telegram.js.map +1 -1
- package/dist/src/cli/doctor.js +33 -13
- package/dist/src/cli/doctor.js.map +1 -1
- package/dist/src/cli/help.js +3 -3
- package/dist/src/cli/hook-exec-code.d.ts +150 -0
- package/dist/src/cli/hook-exec-code.js +357 -0
- package/dist/src/cli/hook-exec-code.js.map +1 -0
- package/dist/src/cli/hook.d.ts +125 -1
- package/dist/src/cli/hook.js +2177 -107
- package/dist/src/cli/hook.js.map +1 -1
- package/dist/src/cli/wordmark.d.ts +2 -2
- package/dist/src/cli/wordmark.js +2 -2
- package/dist/src/core/command-class.d.ts +368 -1
- package/dist/src/core/command-class.js +2830 -93
- package/dist/src/core/command-class.js.map +1 -1
- package/dist/src/core/date-argv.d.ts +83 -0
- package/dist/src/core/date-argv.js +212 -0
- package/dist/src/core/date-argv.js.map +1 -0
- package/dist/src/core/exec-code.d.ts +77 -0
- package/dist/src/core/exec-code.js +60 -0
- package/dist/src/core/exec-code.js.map +1 -0
- package/dist/src/core/git-push-argv.d.ts +111 -0
- package/dist/src/core/git-push-argv.js +375 -0
- package/dist/src/core/git-push-argv.js.map +1 -0
- package/dist/src/core/harness-version.d.ts +71 -30
- package/dist/src/core/harness-version.js +101 -34
- package/dist/src/core/harness-version.js.map +1 -1
- package/dist/src/core/policy-load.d.ts +8 -0
- package/dist/src/core/policy-load.js.map +1 -1
- package/dist/src/core/read-scope.js +1 -0
- package/dist/src/core/read-scope.js.map +1 -1
- package/dist/src/core/sysinfo-argv.d.ts +48 -0
- package/dist/src/core/sysinfo-argv.js +340 -0
- package/dist/src/core/sysinfo-argv.js.map +1 -0
- package/dist/src/daemon/git-evidence.d.ts +2 -2
- package/dist/src/daemon/git-evidence.js +1 -1
- package/docs/README-extended.md +3 -2
- package/docs/cli-reference.md +20 -7
- package/package.json +2 -2
- package/schema/event.schema.json +1 -1
- package/schema/fixtures/policy/invalid/exec-code-custody-boolean.json +11 -0
- package/schema/fixtures/policy/invalid/exec-code-execute-code-value.json +11 -0
- package/schema/fixtures/policy/invalid/exec-code-unknown-key.json +12 -0
- package/schema/fixtures/policy/valid/exec-code-both.json +17 -0
- package/schema/fixtures/policy/valid/exec-code-custody.json +11 -0
- package/schema/policy.schema.json +23 -0
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The system-information readers' safe forms (APRV-508).
|
|
3
|
+
*
|
|
4
|
+
* `uptime`, `whoami`, `hostname`, `df`, `free`, `seq` and `paste` each print
|
|
5
|
+
* something and change nothing in the forms below, and some of them change
|
|
6
|
+
* something in forms that look almost the same: `hostname NAME` and `hostname
|
|
7
|
+
* -F FILE` SET the host name, `free -s N` loops forever, `uptime FILE` reads a
|
|
8
|
+
* utmp file. So each binary gets a STRICT allow-grammar rather than a seat in
|
|
9
|
+
* the read table:
|
|
10
|
+
*
|
|
11
|
+
* - every flag is matched by its exact spelling. No abbreviation (getopt and
|
|
12
|
+
* clap both accept a unique prefix of a long option), no cluster except the
|
|
13
|
+
* ones a table below lists word for word, no `=` joined to a short flag;
|
|
14
|
+
* - `--` is never accepted;
|
|
15
|
+
* - an operand is accepted only where the binary's grammar names one, and
|
|
16
|
+
* only in the shape it names (a number for `seq`, a file for `paste`);
|
|
17
|
+
* - a word carrying `$`, a backtick, a glob or a brace is never accepted,
|
|
18
|
+
* since the shell may expand it into words this grammar never saw;
|
|
19
|
+
* - nothing else. An argv this file does not recognise answers `false`, and
|
|
20
|
+
* the classifier row does not match, so the command keeps the
|
|
21
|
+
* `unclassified` refusal it had before the row existed.
|
|
22
|
+
*
|
|
23
|
+
* Two implementations are read at once: GNU (coreutils, procps, net-tools
|
|
24
|
+
* `hostname`) and uutils coreutils 0.10, which the hosted boxes run. Where a
|
|
25
|
+
* spelling exists in one and not the other, the other errors out on it, which
|
|
26
|
+
* is a refusal by the tool rather than a different act.
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* Characters the shell may expand into more words, or into other words. A
|
|
30
|
+
* leading `~` is not among them: it expands to one path and never splits, and
|
|
31
|
+
* the read scope already judges a `~` path as one it cannot place.
|
|
32
|
+
*/
|
|
33
|
+
const UNREADABLE_WORD = /[$`*?[\]{}]/u;
|
|
34
|
+
/** No word of this argv is one the shell may expand. */
|
|
35
|
+
function literal(args) {
|
|
36
|
+
return !args.some((arg) => UNREADABLE_WORD.test(arg));
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The option each flag word sets, so that two spellings of one option count
|
|
40
|
+
* as a repeat (`-s --serial`, `-h --human-readable`, `-h -hT`). A word not in
|
|
41
|
+
* the map is its own option.
|
|
42
|
+
*/
|
|
43
|
+
const SAME_OPTION = {
|
|
44
|
+
"--human-readable": ["-h"],
|
|
45
|
+
"--si": ["-H"],
|
|
46
|
+
"--print-type": ["-T"],
|
|
47
|
+
"--inodes": ["-i"],
|
|
48
|
+
"--all": ["-a"],
|
|
49
|
+
"--local": ["-l"],
|
|
50
|
+
"--portability": ["-P"],
|
|
51
|
+
"-hT": ["-h", "-T"],
|
|
52
|
+
"-Th": ["-h", "-T"],
|
|
53
|
+
"-hi": ["-h", "-i"],
|
|
54
|
+
"-ih": ["-h", "-i"],
|
|
55
|
+
"-hl": ["-h", "-l"],
|
|
56
|
+
"-lh": ["-h", "-l"],
|
|
57
|
+
"--bytes": ["-b"],
|
|
58
|
+
"--kibi": ["-k"],
|
|
59
|
+
"--mebi": ["-m"],
|
|
60
|
+
"--gibi": ["-g"],
|
|
61
|
+
"--human": ["-h"],
|
|
62
|
+
"--lohi": ["-l"],
|
|
63
|
+
"--total": ["-t"],
|
|
64
|
+
"--wide": ["-w"],
|
|
65
|
+
"--committed": ["-v"],
|
|
66
|
+
"-ht": ["-h", "-t"],
|
|
67
|
+
"-th": ["-h", "-t"],
|
|
68
|
+
"-hw": ["-h", "-w"],
|
|
69
|
+
"-wh": ["-h", "-w"],
|
|
70
|
+
"--equal-width": ["-w"],
|
|
71
|
+
"--separator": ["-s"],
|
|
72
|
+
"--format": ["-f"],
|
|
73
|
+
"--serial": ["-s"],
|
|
74
|
+
"--zero-terminated": ["-z"],
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* No option is given twice, in any spelling. clap (uutils) refuses a repeated
|
|
78
|
+
* option where getopt takes the last one, so the grammar admits neither.
|
|
79
|
+
* `-dX` / `--delimiters=X` and `--separator=X` / `--format=X` count by name.
|
|
80
|
+
*/
|
|
81
|
+
function noRepeatedFlag(args, valueWords = []) {
|
|
82
|
+
const seen = new Set();
|
|
83
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
84
|
+
const arg = args[index];
|
|
85
|
+
if (!arg.startsWith("-") || arg === "-" || /^-[\d.]/u.test(arg))
|
|
86
|
+
continue;
|
|
87
|
+
let name = arg;
|
|
88
|
+
if (arg.startsWith("--delimiters=") || (arg.startsWith("-d") && !arg.startsWith("--")))
|
|
89
|
+
name = "-d";
|
|
90
|
+
else if (arg.startsWith("--separator="))
|
|
91
|
+
name = "-s";
|
|
92
|
+
else if (arg.startsWith("--format="))
|
|
93
|
+
name = "-f";
|
|
94
|
+
for (const option of SAME_OPTION[name] ?? [name]) {
|
|
95
|
+
if (seen.has(option))
|
|
96
|
+
return false;
|
|
97
|
+
seen.add(option);
|
|
98
|
+
}
|
|
99
|
+
// A separate-word value is not a flag, even when it looks like one.
|
|
100
|
+
if (valueWords.includes(arg))
|
|
101
|
+
index += 1;
|
|
102
|
+
}
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
/** `--help` or `--version`, alone. */
|
|
106
|
+
function isHelpOrVersion(args) {
|
|
107
|
+
return args.length === 1 && (args[0] === "--help" || args[0] === "--version");
|
|
108
|
+
}
|
|
109
|
+
/** `uptime` and `whoami`: nothing, or `--help` / `--version`. */
|
|
110
|
+
export function isBareInfo(args) {
|
|
111
|
+
return args.length === 0 || isHelpOrVersion(args);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* `hostname`: nothing, or ONE display flag. Any operand sets the host name,
|
|
115
|
+
* and `-F`/`--file`/`-b`/`--boot` set it from a file or at boot, so none of
|
|
116
|
+
* them is in the list.
|
|
117
|
+
*/
|
|
118
|
+
const HOSTNAME_FLAGS = [
|
|
119
|
+
"-s",
|
|
120
|
+
"--short",
|
|
121
|
+
"-f",
|
|
122
|
+
"--fqdn",
|
|
123
|
+
"--long",
|
|
124
|
+
"-d",
|
|
125
|
+
"--domain",
|
|
126
|
+
"-i",
|
|
127
|
+
"--ip-address",
|
|
128
|
+
"-I",
|
|
129
|
+
"--all-ip-addresses",
|
|
130
|
+
"--help",
|
|
131
|
+
"--version",
|
|
132
|
+
];
|
|
133
|
+
export function isHostnameDisplay(args) {
|
|
134
|
+
if (args.length === 0)
|
|
135
|
+
return true;
|
|
136
|
+
return args.length === 1 && HOSTNAME_FLAGS.includes(args[0]);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* `df`: display flags only. A path operand would be a read the read scope
|
|
140
|
+
* judges, and the agents asking need the mount table, so operands are not in
|
|
141
|
+
* the grammar. `--output` and the type filters are left out with them.
|
|
142
|
+
*/
|
|
143
|
+
const DF_FLAGS = [
|
|
144
|
+
"-h",
|
|
145
|
+
"--human-readable",
|
|
146
|
+
"-H",
|
|
147
|
+
"--si",
|
|
148
|
+
"-k",
|
|
149
|
+
"-T",
|
|
150
|
+
"--print-type",
|
|
151
|
+
"-i",
|
|
152
|
+
"--inodes",
|
|
153
|
+
"-a",
|
|
154
|
+
"--all",
|
|
155
|
+
"-l",
|
|
156
|
+
"--local",
|
|
157
|
+
"-P",
|
|
158
|
+
"--portability",
|
|
159
|
+
"--total",
|
|
160
|
+
"--help",
|
|
161
|
+
"--version",
|
|
162
|
+
// The clusters, by exact spelling.
|
|
163
|
+
"-hT",
|
|
164
|
+
"-Th",
|
|
165
|
+
"-hi",
|
|
166
|
+
"-ih",
|
|
167
|
+
"-hl",
|
|
168
|
+
"-lh",
|
|
169
|
+
];
|
|
170
|
+
export function isDfDisplay(args) {
|
|
171
|
+
return literal(args) && noRepeatedFlag(args) && args.every((arg) => DF_FLAGS.includes(arg));
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* `free`: display flags only. `-s`/`--seconds` and `-c`/`--count` repeat the
|
|
175
|
+
* report on a timer, which holds the turn rather than reading anything, and
|
|
176
|
+
* stay out.
|
|
177
|
+
*/
|
|
178
|
+
const FREE_FLAGS = [
|
|
179
|
+
"-b",
|
|
180
|
+
"--bytes",
|
|
181
|
+
"-k",
|
|
182
|
+
"--kibi",
|
|
183
|
+
"--kilo",
|
|
184
|
+
"-m",
|
|
185
|
+
"--mebi",
|
|
186
|
+
"--mega",
|
|
187
|
+
"-g",
|
|
188
|
+
"--gibi",
|
|
189
|
+
"--giga",
|
|
190
|
+
"-h",
|
|
191
|
+
"--human",
|
|
192
|
+
"--si",
|
|
193
|
+
"-l",
|
|
194
|
+
"--lohi",
|
|
195
|
+
"-t",
|
|
196
|
+
"--total",
|
|
197
|
+
"-w",
|
|
198
|
+
"--wide",
|
|
199
|
+
"-v",
|
|
200
|
+
"--committed",
|
|
201
|
+
"--help",
|
|
202
|
+
"--version",
|
|
203
|
+
"-ht",
|
|
204
|
+
"-th",
|
|
205
|
+
"-hw",
|
|
206
|
+
"-wh",
|
|
207
|
+
];
|
|
208
|
+
export function isFreeDisplay(args) {
|
|
209
|
+
return literal(args) && noRepeatedFlag(args) && args.every((arg) => FREE_FLAGS.includes(arg));
|
|
210
|
+
}
|
|
211
|
+
/** A decimal number, signed or not, with an optional exponent. No hex, `inf` or `nan`. */
|
|
212
|
+
const SEQ_NUMBER = /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][-+]?\d+)?$/u;
|
|
213
|
+
/**
|
|
214
|
+
* The most values an admitted `seq` prints (refutation S4). `seq 1e18` is a
|
|
215
|
+
* read in the sense that it changes nothing, but it holds the turn the way
|
|
216
|
+
* `free -s` does, so a range past this is not admitted.
|
|
217
|
+
*/
|
|
218
|
+
export const SEQ_MAX_VALUES = 100_000;
|
|
219
|
+
/**
|
|
220
|
+
* Can a double hold this number exactly enough to count with? (Recheck S-R2.)
|
|
221
|
+
* GNU and uutils count digit strings exactly, so a range narrower than half a
|
|
222
|
+
* ULP of its ends (`seq 1e30-ish 1e30-ish+5e13`) reads as one value to a
|
|
223
|
+
* double and prints fifty trillion. No more than 15 significant digits, and no
|
|
224
|
+
* magnitude above 2^53, keeps every admitted operand exact enough.
|
|
225
|
+
*/
|
|
226
|
+
function isCountable(word) {
|
|
227
|
+
const mantissa = word.replace(/^[-+]/u, "").replace(/[eE].*$/u, "").replace(".", "");
|
|
228
|
+
const significant = mantissa.replace(/^0+/u, "");
|
|
229
|
+
if (significant.length > 15)
|
|
230
|
+
return false;
|
|
231
|
+
return Math.abs(Number(word)) <= 2 ** 53;
|
|
232
|
+
}
|
|
233
|
+
/** How many values `seq FIRST [INCREMENT] LAST` prints; Infinity when it never ends. */
|
|
234
|
+
function seqCount(numbers) {
|
|
235
|
+
const [first, increment, last] = numbers.length === 1
|
|
236
|
+
? [1, 1, numbers[0]]
|
|
237
|
+
: numbers.length === 2
|
|
238
|
+
? [numbers[0], 1, numbers[1]]
|
|
239
|
+
: [numbers[0], numbers[1], numbers[2]];
|
|
240
|
+
if (![first, increment, last].every((n) => Number.isFinite(n)))
|
|
241
|
+
return Infinity;
|
|
242
|
+
if (increment === 0)
|
|
243
|
+
return Infinity;
|
|
244
|
+
const steps = Math.floor((last - first) / increment);
|
|
245
|
+
return steps < 0 ? 0 : steps + 1;
|
|
246
|
+
}
|
|
247
|
+
/** One floating-point directive, `%%` escapes around it, nothing else. */
|
|
248
|
+
const SEQ_FORMAT = /^(?:[^%]|%%)*%[-+ #0]*\d*(?:\.\d+)?[eEfFgG](?:[^%]|%%)*$/u;
|
|
249
|
+
/** A separator: any literal text that does not look like a flag. */
|
|
250
|
+
function isSeqSeparator(value) {
|
|
251
|
+
return value !== undefined && !value.startsWith("-");
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* `seq`: one to three numbers, with `-w`/`--equal-width`, `-s SEP` /
|
|
255
|
+
* `--separator=SEP` and `-f FMT` / `--format=FMT`. A separate-word value is
|
|
256
|
+
* the next word; the attached short spellings (`-s,`) are not in the grammar.
|
|
257
|
+
* Every flag comes BEFORE the first number: uutils reads a word after a number
|
|
258
|
+
* as another number (`seq 1 -w` is an error there and a flag to GNU), and the
|
|
259
|
+
* grammar takes the reading both agree on.
|
|
260
|
+
*/
|
|
261
|
+
export function isSeqDisplay(args) {
|
|
262
|
+
if (!literal(args) || !noRepeatedFlag(args, ["-s", "--separator", "-f", "--format"]))
|
|
263
|
+
return false;
|
|
264
|
+
if (isHelpOrVersion(args))
|
|
265
|
+
return true;
|
|
266
|
+
let numbers = 0;
|
|
267
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
268
|
+
const arg = args[index];
|
|
269
|
+
if (numbers > 0) {
|
|
270
|
+
if (!SEQ_NUMBER.test(arg))
|
|
271
|
+
return false;
|
|
272
|
+
numbers += 1;
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
if (arg === "-w" || arg === "--equal-width")
|
|
276
|
+
continue;
|
|
277
|
+
if (arg === "-s" || arg === "--separator") {
|
|
278
|
+
index += 1;
|
|
279
|
+
if (!isSeqSeparator(args[index]))
|
|
280
|
+
return false;
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
if (arg.startsWith("--separator="))
|
|
284
|
+
continue;
|
|
285
|
+
if (arg === "-f" || arg === "--format") {
|
|
286
|
+
index += 1;
|
|
287
|
+
// A flag-shaped value is the value to getopt and the next flag to clap.
|
|
288
|
+
const value = args[index] ?? "";
|
|
289
|
+
if (value.startsWith("-") || !SEQ_FORMAT.test(value))
|
|
290
|
+
return false;
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
if (arg.startsWith("--format=")) {
|
|
294
|
+
if (!SEQ_FORMAT.test(arg.slice("--format=".length)))
|
|
295
|
+
return false;
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
if (!SEQ_NUMBER.test(arg))
|
|
299
|
+
return false;
|
|
300
|
+
numbers += 1;
|
|
301
|
+
}
|
|
302
|
+
// GNU and uutils both refuse a format beside equal width.
|
|
303
|
+
const width = args.includes("-w") || args.includes("--equal-width");
|
|
304
|
+
const format = args.some((arg) => arg === "-f" || arg === "--format" || arg.startsWith("--format="));
|
|
305
|
+
if (width && format)
|
|
306
|
+
return false;
|
|
307
|
+
if (numbers < 1 || numbers > 3)
|
|
308
|
+
return false;
|
|
309
|
+
const operands = args.slice(args.length - numbers);
|
|
310
|
+
if (!operands.every((word) => isCountable(word)))
|
|
311
|
+
return false;
|
|
312
|
+
return seqCount(operands.map(Number)) <= SEQ_MAX_VALUES;
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* `paste`: files (or `-`, standard input) with `-s`/`--serial`,
|
|
316
|
+
* `-z`/`--zero-terminated`, and the delimiter list ATTACHED (`-d,`,
|
|
317
|
+
* `--delimiters=,`). Attached, so that every positional is a file: the read
|
|
318
|
+
* scope takes paste's operands exactly as it takes `cat`'s (`READ_TARGET_SHAPES`
|
|
319
|
+
* in read-scope.ts), and a delimiter in its own word would be taken for one.
|
|
320
|
+
*/
|
|
321
|
+
const PASTE_FLAGS = ["-s", "--serial", "-z", "--zero-terminated", "--help", "--version"];
|
|
322
|
+
export function isPasteRead(args) {
|
|
323
|
+
if (!literal(args) || !noRepeatedFlag(args))
|
|
324
|
+
return false;
|
|
325
|
+
for (const arg of args) {
|
|
326
|
+
if (PASTE_FLAGS.includes(arg))
|
|
327
|
+
continue;
|
|
328
|
+
if (arg.startsWith("--delimiters="))
|
|
329
|
+
continue;
|
|
330
|
+
if (arg.startsWith("-d") && arg.length > 2)
|
|
331
|
+
continue;
|
|
332
|
+
// A file, or `-` for standard input. Every other flag-shaped word is not
|
|
333
|
+
// in the grammar (`--`, `-d` with a separate value, abbreviations).
|
|
334
|
+
if (arg === "-" || !arg.startsWith("-"))
|
|
335
|
+
continue;
|
|
336
|
+
return false;
|
|
337
|
+
}
|
|
338
|
+
return true;
|
|
339
|
+
}
|
|
340
|
+
//# sourceMappingURL=sysinfo-argv.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sysinfo-argv.js","sourceRoot":"","sources":["../../../src/core/sysinfo-argv.ts"],"names":[],"mappings":"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"}
|
|
@@ -71,9 +71,9 @@ import type { LogHead } from "../core/log.js";
|
|
|
71
71
|
* because a module that resolves its own package root differs between the source
|
|
72
72
|
* tree and the build output for no benefit at all.
|
|
73
73
|
*/
|
|
74
|
-
export declare const APPROVALD_VERSION = "0.4.
|
|
74
|
+
export declare const APPROVALD_VERSION = "0.4.6";
|
|
75
75
|
/** `user.name` on every commit this module makes. */
|
|
76
|
-
export declare const GIT_EVIDENCE_AUTHOR_NAME = "approvald 0.4.
|
|
76
|
+
export declare const GIT_EVIDENCE_AUTHOR_NAME = "approvald 0.4.6";
|
|
77
77
|
/** `user.email` on every commit: fixed, and deliberately undeliverable. */
|
|
78
78
|
export declare const GIT_EVIDENCE_AUTHOR_EMAIL = "approvald@noreply.approval.md";
|
|
79
79
|
/**
|
|
@@ -75,7 +75,7 @@ import { PAYLOAD_STORE_DIRNAME } from "../core/payload-store.js";
|
|
|
75
75
|
* because a module that resolves its own package root differs between the source
|
|
76
76
|
* tree and the build output for no benefit at all.
|
|
77
77
|
*/
|
|
78
|
-
export const APPROVALD_VERSION = "0.4.
|
|
78
|
+
export const APPROVALD_VERSION = "0.4.6";
|
|
79
79
|
/** `user.name` on every commit this module makes. */
|
|
80
80
|
export const GIT_EVIDENCE_AUTHOR_NAME = `approvald ${APPROVALD_VERSION}`;
|
|
81
81
|
/** `user.email` on every commit: fixed, and deliberately undeliverable. */
|
package/docs/README-extended.md
CHANGED
|
@@ -341,6 +341,7 @@ semantics: SPEC.md section 5.
|
|
|
341
341
|
| `audit.skew_tolerance` | How far a gate-typed event's timestamp may step back before verification reports an anomaly. Report-only; default 2 seconds (§8). |
|
|
342
342
|
| `audit.checkpoint_keys` | Public halves of the Ed25519 keys permitted to sign a `log.checkpoint`, base64 DER SPKI. The private halves live in the vault and never in this file. A list, so a retired key stays listed: a checkpoint signed by a key the list does not carry is refused. Absent, empty or unreadable means verification skips the checkpoint check with a reason and never reports it as a pass (§9, APRV-220). |
|
|
343
343
|
| `delegation` | The judge's bounds (a pinned model that may later review samples, advise on cards and decide manual cards in named classes). RESERVED in 0.4.2: the block is validated and loads only in its off form (`model: null`, every list empty, `max_autonomy: manual`, `daily_cap: 0`, `advice: false`; `{}` is the same), which changes nothing. Any other value fails the policy closed with `delegation-not-supported`, so every class resolves `manual`. Absent means no judge, exactly as before the key existed. Cores before 0.4.2 refuse the key outright (§5.2 proposed, APRV-500; `docs/cli-reference.md`, policy check). |
|
|
344
|
+
| `exec` | What the operator attests about opaque agent code (`python3 -c`, `node -e`, `deno eval` and kin, an interpreter running a file that is not a skill script, Hermes `execute_code`). `exec.code.custody: attested` opens the `exec.code` class, which then resolves like any class and is recorded by the program's SHA-256 and length, never its text; `exec.code.execute_code: hooked` beside it opens Hermes `execute_code`. Absent, those calls are refused exactly as before the key existed; a policy that names `exec.code` without custody refuses `hook-exec-code-custody-unattested`. Cores before 0.4.5 refuse the key and fail the policy closed (§5.2 and §7 proposed, APRV-510; `docs/hermes-hook.md`). |
|
|
344
345
|
| `delegation.model` | The judge identity, `model:<name>@<major>.<minor>.<patch>`, or `null`. Reserved: non-null is `delegation-not-supported` (APRV-500). |
|
|
345
346
|
| `delegation.classes` | Exact keys of `classes` the judge may act on: no wildcard, not `human-only`, not `autonomous`. Reserved: non-empty is `delegation-not-supported` (APRV-500). |
|
|
346
347
|
| `delegation.max_autonomy` | `manual` (default), `supervised-live` or `supervised-retro`: a pin, so every delegated class must declare an autonomy at least as strict. Reserved: anything but `manual` is `delegation-not-supported` (APRV-500). |
|
|
@@ -740,9 +741,9 @@ and the reference phone channel is one app, Telegram.
|
|
|
740
741
|
| [`backlog/`](../backlog/) | Implementation work and remaining acceptance criteria. A task's existence is not a shipped feature. |
|
|
741
742
|
| [`index.html`](../index.html), [`features/`](../features/), [`llms.txt`](../llms.txt), [`llms-full.txt`](../llms-full.txt) | The website and its machine-readable companions. |
|
|
742
743
|
| [`metrics/`](../metrics/), [`brand/`](../brand/) | Agent-hour badge data and visual assets. Badge activity is not an adoption or security certification. |
|
|
743
|
-
| [`hosted/`](../hosted/README.md)
|
|
744
|
+
| [`hosted/`](../hosted/README.md) | Hosting scaffolding; separate from the core runtime contract. |
|
|
744
745
|
|
|
745
|
-
The hosted directory contains a policy builder and tenant launcher, not a complete account system or managed multi-tenant control plane.
|
|
746
|
+
The hosted directory contains a policy builder and tenant launcher, not a complete account system or managed multi-tenant control plane. It should not be advertised as a finished service merely because its directory exists. The judgy reviewer pages that used to sit beside it left the repository under APRV-393; commit `49574e4d` is the last one that holds them.
|
|
746
747
|
|
|
747
748
|
### Running the checks
|
|
748
749
|
|
package/docs/cli-reference.md
CHANGED
|
@@ -3424,9 +3424,13 @@ The checks, at length:
|
|
|
3424
3424
|
**fail-closed version floor** (a build at or after `main` `118984d7` of
|
|
3425
3425
|
2026-09-20) FAILS, because `v0.21.3` ignores `fail_closed` silently and every
|
|
3426
3426
|
broken hook on it proceeds. That half is read from `hermes --version` rather than
|
|
3427
|
-
from a record
|
|
3428
|
-
|
|
3429
|
-
|
|
3427
|
+
from a record, because a record keeps only the printable-ASCII prefix of
|
|
3428
|
+
Hermes's line (APRV-419): the build date survives and the upstream commit
|
|
3429
|
+
behind the non-ASCII separator does not. It compares the build date and the
|
|
3430
|
+
upstream commit, because the two builds report the same semver. Since APRV-419
|
|
3431
|
+
the unverified-change half compares Hermes like any other harness: the prefix,
|
|
3432
|
+
for example `Hermes Agent v0.21.3 (2026.9.14)`, on the newest hook record
|
|
3433
|
+
against the prefix of what `hermes --version` prints now.
|
|
3430
3434
|
- **live-draw** — whether a daemon is answering `supervised-live` draws for this
|
|
3431
3435
|
log (APRV-208). SKIP when the policy declares no live class: no draw is ever
|
|
3432
3436
|
made, and a missing socket is nothing. It FAILS in three shapes, all of them
|
|
@@ -4595,7 +4599,7 @@ to.
|
|
|
4595
4599
|
|
|
4596
4600
|
| Command | What it does |
|
|
4597
4601
|
| -------- | ------------ |
|
|
4598
|
-
| `/queue` | Replies with the
|
|
4602
|
+
| `/queue` | Replies with the count line and a numbered list of every pending request (the agent's summary and its age, no action key or task id), marking the one this listener has selected. Derived from the verified log at reply time, and it works while a request is selected. |
|
|
4599
4603
|
| `/skip` | Selects the next request; the skipped one goes to the BACK of this process's order and comes round again after the rest, with a fresh card when it does. |
|
|
4600
4604
|
| `/next` | Selects the next request; this process does not offer the passed-over one again, and sends no further card for it. |
|
|
4601
4605
|
|
|
@@ -4609,7 +4613,7 @@ it away from you.
|
|
|
4609
4613
|
**`/queue` is a list, and it says so** (APRV-256). The reply carries no decision
|
|
4610
4614
|
buttons of its own, and it names no position for the ones it points at: a
|
|
4611
4615
|
request is decided on its own approval card, wherever that card has ended up in
|
|
4612
|
-
the chat. The marker on the selected line reads `
|
|
4616
|
+
the chat. The marker on the selected line reads `sent to you earlier`,
|
|
4613
4617
|
because that is the whole of what the listener knows. Delivery bookkeeping
|
|
4614
4618
|
records that a send returned success; the Bot API never reports that a message
|
|
4615
4619
|
is still there, and a card can be deleted, buried, or lost with the chat
|
|
@@ -5652,7 +5656,11 @@ semantic-version-shaped tag, `tag <name>`, `--tags`, or `--follow-tags` is
|
|
|
5652
5656
|
that DELETES a remote ref — `--delete`, `-d`, or a colon refspec such as
|
|
5653
5657
|
`:refs/heads/x` — is `vcs.ref.delete` with the ref names bound (APRV-352), and a
|
|
5654
5658
|
tag deletion keeps `release.publish`; ordinary branch pushes retain their branch
|
|
5655
|
-
or trunk class.
|
|
5659
|
+
or trunk class. Flags are read the way git's option parser reads them, short
|
|
5660
|
+
bundles (`-ud`) and unique-prefix abbreviations (`--del`, `--m`) included, and
|
|
5661
|
+
`--prune` is a deletion too (APRV-517); a push git would refuse, or one naming
|
|
5662
|
+
a far-side program (`--exec`, `--receive-pack`), is `opaque`, and a word the
|
|
5663
|
+
shell may expand is `vcs.history.rewrite`.
|
|
5656
5664
|
|
|
5657
5665
|
`git tag` splits on its flags since APRV-397: a LISTING (`-l`, `--list`, `-n`,
|
|
5658
5666
|
`-n<num>`, `--contains`, `--points-at`, `--merged`, `--sort`, `--format`,
|
|
@@ -5715,7 +5723,12 @@ its own flags are not parsed as this verb's.
|
|
|
5715
5723
|
ONE inline script is classified by that script since APRV-380, so
|
|
5716
5724
|
`zsh -lc 'git push origin main'` is `vcs.push.main`; a script file, an extra
|
|
5717
5725
|
word, a redirection on the wrapper, an assignment prefix and a nested shell
|
|
5718
|
-
all stay opaque.
|
|
5726
|
+
all stay opaque. So does a shell that is not the system's, named as written
|
|
5727
|
+
(APRV-523): one spelled by a path outside `/bin`, `/usr/bin`,
|
|
5728
|
+
`/usr/local/bin`, `/sbin` and `/usr/sbin` (a relative path, a Homebrew
|
|
5729
|
+
shell under `/opt`), or by a word with an expansion or a backslash in it.
|
|
5730
|
+
An assignment-shaped shell word with a quoted or escaped name is
|
|
5731
|
+
`hook-unclassified`.
|
|
5719
5732
|
- `hook-unparseable` — the command line could not be tokenized.
|
|
5720
5733
|
- `hook-rejected` — a human said no.
|
|
5721
5734
|
- `hook-revoked` — a granted approval was withdrawn.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "approval-md",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.6",
|
|
4
4
|
"description": "Human approval for agent actions. CLI runtime for the approval.md convention (pre-release).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -59,5 +59,5 @@
|
|
|
59
59
|
"better-sqlite3": "12.11.1",
|
|
60
60
|
"yaml": "2.9.0"
|
|
61
61
|
},
|
|
62
|
-
"gitHead": "
|
|
62
|
+
"gitHead": "539cf3b6b847b0446730ed110b7f188660f2a9d7"
|
|
63
63
|
}
|
package/schema/event.schema.json
CHANGED
|
@@ -136,7 +136,7 @@
|
|
|
136
136
|
"minLength": 1,
|
|
137
137
|
"maxLength": 64,
|
|
138
138
|
"pattern": "^[\\u0020-\\u007e]+$",
|
|
139
|
-
"description": "SPEC.md §10.1 (amended, APRV-227): the version of the harness binary that issued this record, taken from the hook event where the harness supplies one and otherwise from `<binary> --version`, normalized to its first line. SELF-REPORTED, and informational only: nothing in the runtime reads it back as an input to a verdict, a floor, a budget, a streak or a sampling draw, so it satisfies SPEC.md §11.1 invariant 4 by having no path along which it could reduce scrutiny — its single reader is a doctor row that can only ADD a red line. Its SHAPE is constrained here because the value is the output of a third-party process being written to an append-only log: one line, printable ASCII, capped at 64 characters, so a banner or a stack trace (and whatever a stack trace quotes) cannot arrive through it. §11.1 invariant 3 has no exception for provenance. OPTIONAL and additive."
|
|
139
|
+
"description": "SPEC.md §10.1 (amended, APRV-227): the version of the harness binary that issued this record, taken from the hook event where the harness supplies one and otherwise from `<binary> --version`, normalized to its first line. SELF-REPORTED, and informational only: nothing in the runtime reads it back as an input to a verdict, a floor, a budget, a streak or a sampling draw, so it satisfies SPEC.md §11.1 invariant 4 by having no path along which it could reduce scrutiny — its single reader is a doctor row that can only ADD a red line. Its SHAPE is constrained here because the value is the output of a third-party process being written to an append-only log: one line, printable ASCII, capped at 64 characters, so a banner or a stack trace (and whatever a stack trace quotes) cannot arrive through it. §11.1 invariant 3 has no exception for provenance. APRV-419 chose option (c) in its no-schema form: a writer records the first line's printable-ASCII PREFIX, trimmed, cutting everything from the first character outside U+0020..U+007E, where it used to refuse the whole line. That is why a Hermes record carries `Hermes Agent v0.21.3 (2026.9.14)` from the line `Hermes Agent v0.21.3 (2026.9.14) \u00b7 upstream 913d4098`. Every value the prefix rule produces already satisfied this pattern and cap, so neither moved and no record changes validity. OPTIONAL and additive."
|
|
140
140
|
},
|
|
141
141
|
"est_cost_usd": {
|
|
142
142
|
"$ref": "#/$defs/usd_amount",
|
|
@@ -197,6 +197,29 @@
|
|
|
197
197
|
}
|
|
198
198
|
]
|
|
199
199
|
},
|
|
200
|
+
"exec": {
|
|
201
|
+
"type": "object",
|
|
202
|
+
"additionalProperties": false,
|
|
203
|
+
"description": "SPEC.md §5.2 and §7 (APRV-510, proposed; 0.4.5 candidate): what an operator has attested about opaque agent code. Absent, every policy behaves exactly as before the key existed: `python3 -c`, `node -e` and kin are refused `hook-opaque`, an interpreter running a file that is not a skill script is refused `hook-unclassified`, and a Hermes `execute_code` call is refused `hook-hermes-execute-code-unbound`. A core that predates this key refuses it at the schema, and the whole policy then fails closed (every class `manual`), so a tenant receives this block only after its pinned core knows it.",
|
|
204
|
+
"properties": {
|
|
205
|
+
"code": {
|
|
206
|
+
"type": "object",
|
|
207
|
+
"additionalProperties": false,
|
|
208
|
+
"description": "The `exec.code` class (APRV-510). Each key opens one thing and only the attested policy can set it; an absent key is the closed answer. With `custody: attested`, an inline program or a non-skill interpreter file run is classified `exec.code` and resolved by the policy like any class (an `exec.code` row, an `exec.*` family row, or `defaults.autonomy`), and a record of it binds the program's SHA-256 and length, never its text. The record says the program existed; it authorizes nothing the program then does through other tools, each of which is its own hook event.",
|
|
209
|
+
"properties": {
|
|
210
|
+
"custody": {
|
|
211
|
+
"enum": ["attested", "unattested"],
|
|
212
|
+
"description": "`attested`: the operator states that credential custody is in place for this agent (the code it runs cannot read credentials it should not hold), which is the precondition for opening the class at all, because opaque code run without it bypasses every gate that guards a credential. `unattested`, or the key absent: the class stays closed, and where the policy names `exec.code` anywhere the refusal is `hook-exec-code-custody-unattested`, naming this key."
|
|
213
|
+
},
|
|
214
|
+
"execute_code": {
|
|
215
|
+
"enum": ["hooked", "unhooked"],
|
|
216
|
+
"description": "Hermes `execute_code` only, and only beside `custody: attested`. `hooked`: the operator has observed that a tool call made from inside the `execute_code` kernel re-fires the hook (the lane L1 probe's answer), so the call is classified `exec.code` with the program's hash bound. `unhooked`, or the key absent: `execute_code` is refused `hook-hermes-execute-code-unbound` as before. Set it only on the probe's yes: if inner calls do not re-fire the hook, one `execute_code` call is an unbounded bypass of the whole adapter."
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
"examples": [{ "code": { "custody": "attested" } }, { "code": { "custody": "attested", "execute_code": "hooked" } }]
|
|
222
|
+
},
|
|
200
223
|
"read_scope": {
|
|
201
224
|
"type": "object",
|
|
202
225
|
"additionalProperties": false,
|