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,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `date` in its display forms (APRV-508).
|
|
3
|
+
*
|
|
4
|
+
* The classifier's `date-read` row asks this file one question of an argv and
|
|
5
|
+
* the assignments in front of it: is this a `date` that only PRINTS a time,
|
|
6
|
+
* reading no file to do it?
|
|
7
|
+
*
|
|
8
|
+
* `date` has two jobs. One prints a time; the other sets the system clock, and
|
|
9
|
+
* the setting forms are spelled with the same binary and overlapping flags.
|
|
10
|
+
* So this reads the argv the way an option parser does and answers `false` for
|
|
11
|
+
* anything it does not recognise as display-only. `false` is the fail-closed
|
|
12
|
+
* answer: the classifier row does not match, and the command keeps the
|
|
13
|
+
* `unclassified` refusal it had before this row existed.
|
|
14
|
+
*
|
|
15
|
+
* Two implementations are in scope, and an argv is accepted only where both
|
|
16
|
+
* read it as a display: GNU coreutils (getopt) and uutils coreutils 0.10, the
|
|
17
|
+
* `date` the hosted Agent Village boxes run (clap). Where they differ the
|
|
18
|
+
* stricter reading wins: a required value may not start with `-` (getopt takes
|
|
19
|
+
* it as the value, clap may take it as the next flag), and long options match
|
|
20
|
+
* by EXACT name only (getopt and clap both accept a unique prefix, so `--se`
|
|
21
|
+
* is `--set` to both).
|
|
22
|
+
*
|
|
23
|
+
* Accepted, and nothing else:
|
|
24
|
+
*
|
|
25
|
+
* - no arguments at all (`date`);
|
|
26
|
+
* - at most one operand, and it must start with `+` (the output format). Any
|
|
27
|
+
* other operand is a clock setting (`MMDDhhmm[[CC]YY][.ss]`) on GNU, BSD and
|
|
28
|
+
* uutils alike;
|
|
29
|
+
* - `-u`, `--utc`, `--universal` (print in UTC);
|
|
30
|
+
* - `-R`, `--rfc-2822`, `--rfc-email`;
|
|
31
|
+
* - `-I` alone or with an ATTACHED precision (`-Iseconds`), and `--iso-8601`
|
|
32
|
+
* alone or with `=precision`;
|
|
33
|
+
* - `--rfc-3339=FMT` or `--rfc-3339 FMT`, FMT one of `date`, `seconds`, `ns`;
|
|
34
|
+
* - `-d STRING`, `-ud STRING`, `--date=STRING`, `--date STRING`: the string is
|
|
35
|
+
* parsed and printed, never applied to the clock. A `TZ="…"` inside the
|
|
36
|
+
* string names a zone and is held to {@link isZoneValue};
|
|
37
|
+
* - `--debug`, `--resolution`, `-h`/`--help`, `-V`/`--version`, which print
|
|
38
|
+
* diagnostics or text and read nothing;
|
|
39
|
+
* - the clusters `-uR`, `-Ru` and `-ud`, and no other: a cluster is accepted
|
|
40
|
+
* only by its exact spelling.
|
|
41
|
+
*
|
|
42
|
+
* Declined, so `unclassified` as before:
|
|
43
|
+
*
|
|
44
|
+
* - `-s`/`--set` (sets the clock), in any spelling or cluster;
|
|
45
|
+
* - `-f`/`--file` (reads a file's lines and echoes the bad ones back; on BSD
|
|
46
|
+
* `-f` is an input format that sets the clock without `-j`);
|
|
47
|
+
* - `-r`/`--reference` in EVERY spelling. It prints a file's modification
|
|
48
|
+
* time, which is a read of that file's metadata, and the agents this row
|
|
49
|
+
* exists for need the clock rather than a file's mtime. Admitting it would
|
|
50
|
+
* put a second spelling of `stat` behind a row whose whole claim is that it
|
|
51
|
+
* reads no file, so it stays refused;
|
|
52
|
+
* - BSD's `-j`/`-v`/`-n`, a bare `--`, every long-option abbreviation, an
|
|
53
|
+
* attached short value (`-d@0`, `-d=@0`), and every cluster not listed;
|
|
54
|
+
* - any word this file cannot read literally: one carrying `$`, a backtick, a
|
|
55
|
+
* glob or a brace, since the shell may split or expand it into flags this
|
|
56
|
+
* parse never saw. The classifier also declines a segment with a
|
|
57
|
+
* substituted word;
|
|
58
|
+
* - an assignment other than {@link DATE_ENV_NAMES}, and one of those whose
|
|
59
|
+
* value could name a file (`TZ=/etc/x`, `TZ=:../x`, `LC_ALL=/tmp/x`): a zone
|
|
60
|
+
* or locale value that is a path makes `date` open that file.
|
|
61
|
+
*/
|
|
62
|
+
/** Assignments a display `date` may carry: the zone, and the locale it prints in. */
|
|
63
|
+
export declare const DATE_ENV_NAMES: readonly string[];
|
|
64
|
+
/** Is this a zone value that names no file outside the zone database? */
|
|
65
|
+
export declare function isZoneValue(value: string): boolean;
|
|
66
|
+
/**
|
|
67
|
+
* May this `NAME=value` word stand in front of a display `date`?
|
|
68
|
+
*
|
|
69
|
+
* Only the zone and locale names, and only with a value that cannot name a
|
|
70
|
+
* file: a zone outside the database or a locale that is a path makes `date`
|
|
71
|
+
* open whatever the value names.
|
|
72
|
+
*/
|
|
73
|
+
export declare function isDateAssignment(word: string): boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Is this `date` argv a display form? `args` is the words after the binary,
|
|
76
|
+
* quotes already removed; `assignments` the `NAME=value` words in front of it.
|
|
77
|
+
*
|
|
78
|
+
* A strict allow-grammar: every word must be one of the exact spellings below,
|
|
79
|
+
* a value in a position one of them opens, or the single `+FORMAT` operand.
|
|
80
|
+
* Anything else (a cluster not listed, an abbreviation, `--`, an `=` joined
|
|
81
|
+
* to a short flag) answers `false`.
|
|
82
|
+
*/
|
|
83
|
+
export declare function isDateDisplay(args: readonly string[], assignments?: readonly string[]): boolean;
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `date` in its display forms (APRV-508).
|
|
3
|
+
*
|
|
4
|
+
* The classifier's `date-read` row asks this file one question of an argv and
|
|
5
|
+
* the assignments in front of it: is this a `date` that only PRINTS a time,
|
|
6
|
+
* reading no file to do it?
|
|
7
|
+
*
|
|
8
|
+
* `date` has two jobs. One prints a time; the other sets the system clock, and
|
|
9
|
+
* the setting forms are spelled with the same binary and overlapping flags.
|
|
10
|
+
* So this reads the argv the way an option parser does and answers `false` for
|
|
11
|
+
* anything it does not recognise as display-only. `false` is the fail-closed
|
|
12
|
+
* answer: the classifier row does not match, and the command keeps the
|
|
13
|
+
* `unclassified` refusal it had before this row existed.
|
|
14
|
+
*
|
|
15
|
+
* Two implementations are in scope, and an argv is accepted only where both
|
|
16
|
+
* read it as a display: GNU coreutils (getopt) and uutils coreutils 0.10, the
|
|
17
|
+
* `date` the hosted Agent Village boxes run (clap). Where they differ the
|
|
18
|
+
* stricter reading wins: a required value may not start with `-` (getopt takes
|
|
19
|
+
* it as the value, clap may take it as the next flag), and long options match
|
|
20
|
+
* by EXACT name only (getopt and clap both accept a unique prefix, so `--se`
|
|
21
|
+
* is `--set` to both).
|
|
22
|
+
*
|
|
23
|
+
* Accepted, and nothing else:
|
|
24
|
+
*
|
|
25
|
+
* - no arguments at all (`date`);
|
|
26
|
+
* - at most one operand, and it must start with `+` (the output format). Any
|
|
27
|
+
* other operand is a clock setting (`MMDDhhmm[[CC]YY][.ss]`) on GNU, BSD and
|
|
28
|
+
* uutils alike;
|
|
29
|
+
* - `-u`, `--utc`, `--universal` (print in UTC);
|
|
30
|
+
* - `-R`, `--rfc-2822`, `--rfc-email`;
|
|
31
|
+
* - `-I` alone or with an ATTACHED precision (`-Iseconds`), and `--iso-8601`
|
|
32
|
+
* alone or with `=precision`;
|
|
33
|
+
* - `--rfc-3339=FMT` or `--rfc-3339 FMT`, FMT one of `date`, `seconds`, `ns`;
|
|
34
|
+
* - `-d STRING`, `-ud STRING`, `--date=STRING`, `--date STRING`: the string is
|
|
35
|
+
* parsed and printed, never applied to the clock. A `TZ="…"` inside the
|
|
36
|
+
* string names a zone and is held to {@link isZoneValue};
|
|
37
|
+
* - `--debug`, `--resolution`, `-h`/`--help`, `-V`/`--version`, which print
|
|
38
|
+
* diagnostics or text and read nothing;
|
|
39
|
+
* - the clusters `-uR`, `-Ru` and `-ud`, and no other: a cluster is accepted
|
|
40
|
+
* only by its exact spelling.
|
|
41
|
+
*
|
|
42
|
+
* Declined, so `unclassified` as before:
|
|
43
|
+
*
|
|
44
|
+
* - `-s`/`--set` (sets the clock), in any spelling or cluster;
|
|
45
|
+
* - `-f`/`--file` (reads a file's lines and echoes the bad ones back; on BSD
|
|
46
|
+
* `-f` is an input format that sets the clock without `-j`);
|
|
47
|
+
* - `-r`/`--reference` in EVERY spelling. It prints a file's modification
|
|
48
|
+
* time, which is a read of that file's metadata, and the agents this row
|
|
49
|
+
* exists for need the clock rather than a file's mtime. Admitting it would
|
|
50
|
+
* put a second spelling of `stat` behind a row whose whole claim is that it
|
|
51
|
+
* reads no file, so it stays refused;
|
|
52
|
+
* - BSD's `-j`/`-v`/`-n`, a bare `--`, every long-option abbreviation, an
|
|
53
|
+
* attached short value (`-d@0`, `-d=@0`), and every cluster not listed;
|
|
54
|
+
* - any word this file cannot read literally: one carrying `$`, a backtick, a
|
|
55
|
+
* glob or a brace, since the shell may split or expand it into flags this
|
|
56
|
+
* parse never saw. The classifier also declines a segment with a
|
|
57
|
+
* substituted word;
|
|
58
|
+
* - an assignment other than {@link DATE_ENV_NAMES}, and one of those whose
|
|
59
|
+
* value could name a file (`TZ=/etc/x`, `TZ=:../x`, `LC_ALL=/tmp/x`): a zone
|
|
60
|
+
* or locale value that is a path makes `date` open that file.
|
|
61
|
+
*/
|
|
62
|
+
/** Assignments a display `date` may carry: the zone, and the locale it prints in. */
|
|
63
|
+
export const DATE_ENV_NAMES = ["TZ", "LANG", "LANGUAGE", "LC_ALL", "LC_TIME"];
|
|
64
|
+
/** Characters the shell may expand into more words, or into other words. */
|
|
65
|
+
const UNREADABLE_WORD = /[$`*?[\]{}]/u;
|
|
66
|
+
/** Long options that take no value, by exact spelling. */
|
|
67
|
+
const LONG_BARE = [
|
|
68
|
+
"--utc",
|
|
69
|
+
"--universal",
|
|
70
|
+
"--rfc-2822",
|
|
71
|
+
"--rfc-email",
|
|
72
|
+
"--iso-8601",
|
|
73
|
+
"--debug",
|
|
74
|
+
"--resolution",
|
|
75
|
+
"--help",
|
|
76
|
+
"--version",
|
|
77
|
+
];
|
|
78
|
+
/**
|
|
79
|
+
* Short words that take no value, by exact spelling. Clusters are admitted
|
|
80
|
+
* only as listed here: no general cluster parsing, so a letter this file does
|
|
81
|
+
* not know can never ride inside one.
|
|
82
|
+
*/
|
|
83
|
+
const SHORT_BARE = ["-u", "-R", "-h", "-V", "-I", "-uR", "-Ru"];
|
|
84
|
+
/** The `-I` / `--iso-8601` precisions, attached (`-Iseconds`, `--iso-8601=ns`). */
|
|
85
|
+
const ISO_PRECISIONS = ["date", "hours", "minutes", "seconds", "ns"];
|
|
86
|
+
/** The `--rfc-3339` precisions. */
|
|
87
|
+
const RFC3339_PRECISIONS = ["date", "seconds", "ns"];
|
|
88
|
+
/** Words that take the NEXT word as a date string. */
|
|
89
|
+
const DATE_VALUE_WORDS = ["-d", "-ud", "--date"];
|
|
90
|
+
/**
|
|
91
|
+
* A tz database name (`Asia/Kolkata`, `Etc/GMT+5`, `UTC`), optionally behind
|
|
92
|
+
* the POSIX `:`. Every segment is a name: no leading `/`, no `.` anywhere, so
|
|
93
|
+
* neither an absolute path nor a `..` climb out of the zoneinfo directory.
|
|
94
|
+
*/
|
|
95
|
+
const ZONE_NAME = /^:?[A-Za-z_][A-Za-z0-9_+-]*(?:\/[A-Za-z0-9_+-]+)*$/u;
|
|
96
|
+
/**
|
|
97
|
+
* A POSIX TZ rule with no transition dates (`IST-5:30`, `EST5EDT`, `<+0530>-5:30`).
|
|
98
|
+
* A rule never names a file.
|
|
99
|
+
*/
|
|
100
|
+
const POSIX_ZONE = /^(?:[A-Za-z]{3,}|<[A-Za-z0-9+-]{3,}>)[+-]?\d{1,2}(?::\d{2}){0,2}(?:(?:[A-Za-z]{3,}|<[A-Za-z0-9+-]{3,}>)(?:[+-]?\d{1,2}(?::\d{2}){0,2})?)?$/u;
|
|
101
|
+
/** A locale name (`C`, `C.UTF-8`, `en_US.UTF-8`, `en:fr`), never a path. */
|
|
102
|
+
const LOCALE_VALUE = /^[A-Za-z0-9_@.:-]*$/u;
|
|
103
|
+
/** Is this a zone value that names no file outside the zone database? */
|
|
104
|
+
export function isZoneValue(value) {
|
|
105
|
+
if (value === "")
|
|
106
|
+
return true;
|
|
107
|
+
if (value.includes(".."))
|
|
108
|
+
return false;
|
|
109
|
+
return ZONE_NAME.test(value) || POSIX_ZONE.test(value);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* May this `NAME=value` word stand in front of a display `date`?
|
|
113
|
+
*
|
|
114
|
+
* Only the zone and locale names, and only with a value that cannot name a
|
|
115
|
+
* file: a zone outside the database or a locale that is a path makes `date`
|
|
116
|
+
* open whatever the value names.
|
|
117
|
+
*/
|
|
118
|
+
export function isDateAssignment(word) {
|
|
119
|
+
const equals = word.indexOf("=");
|
|
120
|
+
if (equals <= 0)
|
|
121
|
+
return false;
|
|
122
|
+
const name = word.slice(0, equals);
|
|
123
|
+
const value = word.slice(equals + 1);
|
|
124
|
+
if (!DATE_ENV_NAMES.includes(name))
|
|
125
|
+
return false;
|
|
126
|
+
if (UNREADABLE_WORD.test(value) || value.includes("~"))
|
|
127
|
+
return false;
|
|
128
|
+
if (name === "TZ")
|
|
129
|
+
return isZoneValue(value);
|
|
130
|
+
return !value.includes("..") && LOCALE_VALUE.test(value);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Does a `-d` string carry only zones that name no file?
|
|
134
|
+
*
|
|
135
|
+
* GNU (and the parser uutils uses) accept a leading `TZ="Zone"` inside the
|
|
136
|
+
* date string, and resolve it as the variable would be resolved.
|
|
137
|
+
*/
|
|
138
|
+
function isDateString(value) {
|
|
139
|
+
// Case-insensitive (refutation S2): GNU's prefix is `TZ=` exactly, but a
|
|
140
|
+
// parser that folds case would read `tz="/etc/x"` as a zone path too.
|
|
141
|
+
const embedded = /TZ\s*=\s*(?:"([^"]*)"|'([^']*)'|(\S*))/giu;
|
|
142
|
+
for (let match = embedded.exec(value); match !== null; match = embedded.exec(value)) {
|
|
143
|
+
const zone = match[1] ?? match[2] ?? match[3] ?? "";
|
|
144
|
+
if (!isZoneValue(zone))
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
/** A required value: present, not flag-shaped, and readable as a date string. */
|
|
150
|
+
function isRequiredValue(value) {
|
|
151
|
+
return value !== undefined && !value.startsWith("-") && isDateString(value);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Is this `date` argv a display form? `args` is the words after the binary,
|
|
155
|
+
* quotes already removed; `assignments` the `NAME=value` words in front of it.
|
|
156
|
+
*
|
|
157
|
+
* A strict allow-grammar: every word must be one of the exact spellings below,
|
|
158
|
+
* a value in a position one of them opens, or the single `+FORMAT` operand.
|
|
159
|
+
* Anything else (a cluster not listed, an abbreviation, `--`, an `=` joined
|
|
160
|
+
* to a short flag) answers `false`.
|
|
161
|
+
*/
|
|
162
|
+
export function isDateDisplay(args, assignments = []) {
|
|
163
|
+
if (!assignments.every((word) => isDateAssignment(word)))
|
|
164
|
+
return false;
|
|
165
|
+
if (args.some((arg) => UNREADABLE_WORD.test(arg)))
|
|
166
|
+
return false;
|
|
167
|
+
const seen = new Set();
|
|
168
|
+
let operands = 0;
|
|
169
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
170
|
+
const arg = args[index];
|
|
171
|
+
// A flag word given twice: clap (uutils) refuses it, getopt takes the last.
|
|
172
|
+
if (arg.startsWith("-")) {
|
|
173
|
+
if (seen.has(arg))
|
|
174
|
+
return false;
|
|
175
|
+
seen.add(arg);
|
|
176
|
+
}
|
|
177
|
+
if (LONG_BARE.includes(arg) || SHORT_BARE.includes(arg))
|
|
178
|
+
continue;
|
|
179
|
+
if (arg.startsWith("-I") && ISO_PRECISIONS.includes(arg.slice(2)))
|
|
180
|
+
continue;
|
|
181
|
+
if (arg.startsWith("--iso-8601=") && ISO_PRECISIONS.includes(arg.slice("--iso-8601=".length)))
|
|
182
|
+
continue;
|
|
183
|
+
if (arg.startsWith("--rfc-3339=") && RFC3339_PRECISIONS.includes(arg.slice("--rfc-3339=".length)))
|
|
184
|
+
continue;
|
|
185
|
+
if (arg === "--rfc-3339") {
|
|
186
|
+
index += 1;
|
|
187
|
+
if (!RFC3339_PRECISIONS.includes(args[index] ?? ""))
|
|
188
|
+
return false;
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
if (arg.startsWith("--date=")) {
|
|
192
|
+
if (!isDateString(arg.slice("--date=".length)))
|
|
193
|
+
return false;
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
if (DATE_VALUE_WORDS.includes(arg)) {
|
|
197
|
+
index += 1;
|
|
198
|
+
if (!isRequiredValue(args[index]))
|
|
199
|
+
return false;
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
// An operand: exactly one, and only an output format. Anything else is a
|
|
203
|
+
// flag this grammar does not list, or a clock setting.
|
|
204
|
+
if (!arg.startsWith("+"))
|
|
205
|
+
return false;
|
|
206
|
+
operands += 1;
|
|
207
|
+
if (operands > 1)
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=date-argv.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"date-argv.js","sourceRoot":"","sources":["../../../src/core/date-argv.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `exec.code` gate: what the attested policy says about opaque agent code
|
|
3
|
+
* (APRV-510, 0.4.5 candidate).
|
|
4
|
+
*
|
|
5
|
+
* `python3 -c`, `node -e` and kin, an interpreter running a file that is not a
|
|
6
|
+
* skill script, and a Hermes `execute_code` call are all programs whose effect
|
|
7
|
+
* no classifier reads. Before this module every one of them was refused before
|
|
8
|
+
* policy (`hook-opaque`, `hook-unclassified`, `hook-hermes-execute-code-unbound`),
|
|
9
|
+
* and that stays the answer for every policy that does not speak: the gate is
|
|
10
|
+
* CLOSED unless the operator's policy opens it, key by key.
|
|
11
|
+
*
|
|
12
|
+
* Two keys, each opening one thing, and an absent key is the closed answer:
|
|
13
|
+
*
|
|
14
|
+
* - `exec.code.custody: attested` opens the class for the shell forms. It is the
|
|
15
|
+
* operator's statement that credential custody is in place: opaque code run by
|
|
16
|
+
* an agent that can read the tenant's credentials is a way round every gate
|
|
17
|
+
* that guards them, so the class may not exist without that statement.
|
|
18
|
+
* - `exec.code.execute_code: hooked`, beside the first, opens Hermes's
|
|
19
|
+
* `execute_code`. It is the operator's statement that a tool call made from
|
|
20
|
+
* inside the `execute_code` kernel re-fires the hook, which only a live probe
|
|
21
|
+
* can establish (`docs/hermes-hook.md`); without it one `execute_code` call
|
|
22
|
+
* would be an unbounded bypass of the adapter.
|
|
23
|
+
*
|
|
24
|
+
* Pure: a function of the parsed policy and nothing else. It decides whether a
|
|
25
|
+
* call may be CLASSIFIED `exec.code`; what the class then resolves to is the
|
|
26
|
+
* policy's ordinary class resolution (an `exec.code` row, an `exec.*` row, or
|
|
27
|
+
* `defaults.autonomy`), unchanged.
|
|
28
|
+
*/
|
|
29
|
+
import type { Policy } from "./policy-load.js";
|
|
30
|
+
/** The `exec` policy block (schema/policy.schema.json, APRV-510). */
|
|
31
|
+
export interface ExecBlock {
|
|
32
|
+
code?: {
|
|
33
|
+
custody?: "attested" | "unattested";
|
|
34
|
+
execute_code?: "hooked" | "unhooked";
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/** The class an opened program is judged under. Mirrors `command-class.ts`. */
|
|
38
|
+
export declare const EXEC_CODE_POLICY_CLASS = "exec.code";
|
|
39
|
+
/** The policy line that opens the class, as a refusal names it. */
|
|
40
|
+
export declare const EXEC_CODE_CUSTODY_KEY = "exec.code.custody: attested";
|
|
41
|
+
/** The policy line that opens Hermes `execute_code`, as a refusal names it. */
|
|
42
|
+
export declare const EXEC_CODE_EXECUTE_KEY = "exec.code.execute_code: hooked";
|
|
43
|
+
/**
|
|
44
|
+
* Why the gate is closed, which decides the refusal's code and words.
|
|
45
|
+
*
|
|
46
|
+
* - `silent`: the policy says nothing about `exec.code` (no `exec` block and no
|
|
47
|
+
* `exec.code` class row), or there is no policy. The refusal is today's,
|
|
48
|
+
* byte for byte.
|
|
49
|
+
* - `unattested`: the policy names `exec.code` (an `exec` block, or a class row
|
|
50
|
+
* for it) without `custody: attested`. The refusal is
|
|
51
|
+
* `hook-exec-code-custody-unattested`, naming the key.
|
|
52
|
+
* - `window`: an open window is standing. A window suspends the policy's
|
|
53
|
+
* answer, and opening this class is a policy statement, so it is not reached
|
|
54
|
+
* through one; the refusal is today's.
|
|
55
|
+
* - `harness`: the adapter binds native bytes this class's payload cannot
|
|
56
|
+
* carry (Codex), so the class is not opened there; the refusal is today's.
|
|
57
|
+
*/
|
|
58
|
+
export type ExecCodeClosed = "silent" | "unattested" | "window" | "harness";
|
|
59
|
+
export interface ExecCodeGate {
|
|
60
|
+
/** May a shell segment be classified `exec.code`? */
|
|
61
|
+
shell: boolean;
|
|
62
|
+
/** May a Hermes `execute_code` call be classified `exec.code`? */
|
|
63
|
+
executeCode: boolean;
|
|
64
|
+
/** Why `shell` is false; `null` when it is true. */
|
|
65
|
+
closed: ExecCodeClosed | null;
|
|
66
|
+
}
|
|
67
|
+
/** The gate with everything closed for `reason`. */
|
|
68
|
+
export declare function closedExecCodeGate(reason: ExecCodeClosed): ExecCodeGate;
|
|
69
|
+
/**
|
|
70
|
+
* Does the policy name `exec.code` anywhere? An `exec` block, or a `classes`
|
|
71
|
+
* key that is exactly `exec.code`. A family row (`exec.*`) is not a mention:
|
|
72
|
+
* it was written for `exec.local`, and a policy that predates the class keeps
|
|
73
|
+
* its refusals byte for byte.
|
|
74
|
+
*/
|
|
75
|
+
export declare function execCodeMentioned(policy: Policy): boolean;
|
|
76
|
+
/** What `policy` opens. `null` (no policy loaded) opens nothing. */
|
|
77
|
+
export declare function execCodeGate(policy: Policy | null): ExecCodeGate;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `exec.code` gate: what the attested policy says about opaque agent code
|
|
3
|
+
* (APRV-510, 0.4.5 candidate).
|
|
4
|
+
*
|
|
5
|
+
* `python3 -c`, `node -e` and kin, an interpreter running a file that is not a
|
|
6
|
+
* skill script, and a Hermes `execute_code` call are all programs whose effect
|
|
7
|
+
* no classifier reads. Before this module every one of them was refused before
|
|
8
|
+
* policy (`hook-opaque`, `hook-unclassified`, `hook-hermes-execute-code-unbound`),
|
|
9
|
+
* and that stays the answer for every policy that does not speak: the gate is
|
|
10
|
+
* CLOSED unless the operator's policy opens it, key by key.
|
|
11
|
+
*
|
|
12
|
+
* Two keys, each opening one thing, and an absent key is the closed answer:
|
|
13
|
+
*
|
|
14
|
+
* - `exec.code.custody: attested` opens the class for the shell forms. It is the
|
|
15
|
+
* operator's statement that credential custody is in place: opaque code run by
|
|
16
|
+
* an agent that can read the tenant's credentials is a way round every gate
|
|
17
|
+
* that guards them, so the class may not exist without that statement.
|
|
18
|
+
* - `exec.code.execute_code: hooked`, beside the first, opens Hermes's
|
|
19
|
+
* `execute_code`. It is the operator's statement that a tool call made from
|
|
20
|
+
* inside the `execute_code` kernel re-fires the hook, which only a live probe
|
|
21
|
+
* can establish (`docs/hermes-hook.md`); without it one `execute_code` call
|
|
22
|
+
* would be an unbounded bypass of the adapter.
|
|
23
|
+
*
|
|
24
|
+
* Pure: a function of the parsed policy and nothing else. It decides whether a
|
|
25
|
+
* call may be CLASSIFIED `exec.code`; what the class then resolves to is the
|
|
26
|
+
* policy's ordinary class resolution (an `exec.code` row, an `exec.*` row, or
|
|
27
|
+
* `defaults.autonomy`), unchanged.
|
|
28
|
+
*/
|
|
29
|
+
/** The class an opened program is judged under. Mirrors `command-class.ts`. */
|
|
30
|
+
export const EXEC_CODE_POLICY_CLASS = "exec.code";
|
|
31
|
+
/** The policy line that opens the class, as a refusal names it. */
|
|
32
|
+
export const EXEC_CODE_CUSTODY_KEY = "exec.code.custody: attested";
|
|
33
|
+
/** The policy line that opens Hermes `execute_code`, as a refusal names it. */
|
|
34
|
+
export const EXEC_CODE_EXECUTE_KEY = "exec.code.execute_code: hooked";
|
|
35
|
+
/** The gate with everything closed for `reason`. */
|
|
36
|
+
export function closedExecCodeGate(reason) {
|
|
37
|
+
return { shell: false, executeCode: false, closed: reason };
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Does the policy name `exec.code` anywhere? An `exec` block, or a `classes`
|
|
41
|
+
* key that is exactly `exec.code`. A family row (`exec.*`) is not a mention:
|
|
42
|
+
* it was written for `exec.local`, and a policy that predates the class keeps
|
|
43
|
+
* its refusals byte for byte.
|
|
44
|
+
*/
|
|
45
|
+
export function execCodeMentioned(policy) {
|
|
46
|
+
if (policy.exec !== undefined)
|
|
47
|
+
return true;
|
|
48
|
+
return Object.prototype.hasOwnProperty.call(policy.classes ?? {}, EXEC_CODE_POLICY_CLASS);
|
|
49
|
+
}
|
|
50
|
+
/** What `policy` opens. `null` (no policy loaded) opens nothing. */
|
|
51
|
+
export function execCodeGate(policy) {
|
|
52
|
+
if (policy === null)
|
|
53
|
+
return closedExecCodeGate("silent");
|
|
54
|
+
const code = policy.exec?.code;
|
|
55
|
+
if (code?.custody !== "attested") {
|
|
56
|
+
return closedExecCodeGate(execCodeMentioned(policy) ? "unattested" : "silent");
|
|
57
|
+
}
|
|
58
|
+
return { shell: true, executeCode: code.execute_code === "hooked", closed: null };
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=exec-code.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec-code.js","sourceRoot":"","sources":["../../../src/core/exec-code.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAYH,+EAA+E;AAC/E,MAAM,CAAC,MAAM,sBAAsB,GAAG,WAAW,CAAC;AAElD,mEAAmE;AACnE,MAAM,CAAC,MAAM,qBAAqB,GAAG,6BAA6B,CAAC;AAEnE,+EAA+E;AAC/E,MAAM,CAAC,MAAM,qBAAqB,GAAG,gCAAgC,CAAC;AA4BtE,oDAAoD;AACpD,MAAM,UAAU,kBAAkB,CAAC,MAAsB;IACvD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAC9D,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAc;IAC9C,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAC3C,OAAO,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,EAAE,sBAAsB,CAAC,CAAC;AAC5F,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,YAAY,CAAC,MAAqB;IAChD,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IACzD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC;IAC/B,IAAI,IAAI,EAAE,OAAO,KAAK,UAAU,EAAE,CAAC;QACjC,OAAO,kBAAkB,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IACjF,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,CAAC,YAAY,KAAK,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;AACpF,CAAC"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `git push` read the way git 2.50's option parser reads it (APRV-517).
|
|
3
|
+
*
|
|
4
|
+
* The classifier's `git-push` row used to look for flags by exact spelling:
|
|
5
|
+
* `--delete` and `-d`, `--force` and `-f`. git's parser accepts more than
|
|
6
|
+
* that, and every extra spelling it accepts did the same thing as the one it
|
|
7
|
+
* stands for while classifying as a plain push:
|
|
8
|
+
*
|
|
9
|
+
* - a short bundle: `-ud`, `-du`, `-fd`, `-dn` carry `-d` (and `-f`);
|
|
10
|
+
* - a long-option abbreviation by unique prefix: `--de`, `--del`, `--dele`
|
|
11
|
+
* and `--delet` are `--delete`, `--m` is `--mirror`, `--pru` is `--prune`;
|
|
12
|
+
* - a deleting option the exact list never named: `--prune` removes every
|
|
13
|
+
* remote ref a wildcard refspec no longer matches;
|
|
14
|
+
* - the words after a value-taking option (`-o x`, `--repo x`) shift the
|
|
15
|
+
* positionals, and a global option in front of `push` (`git --no-pager
|
|
16
|
+
* push origin`) shifted them too.
|
|
17
|
+
*
|
|
18
|
+
* So this file is the closed rule: it walks the argv exactly as git's
|
|
19
|
+
* `parse-options.c` does over the option table of `builtin/push.c` in git
|
|
20
|
+
* 2.50, and reports what the push does rather than which words it contains.
|
|
21
|
+
* Anything it cannot walk is a refusal, never a guess:
|
|
22
|
+
*
|
|
23
|
+
* - `unreadable`: a word the shell may split or expand into words this walk
|
|
24
|
+
* never saw (`$`, a backtick, a glob, a brace expansion), a word that came
|
|
25
|
+
* out of a command substitution, or an assignment or global option in
|
|
26
|
+
* front of `push` that steers which configuration git reads (`GIT_*=`,
|
|
27
|
+
* `HOME=`, `XDG_CONFIG_HOME=`, `--git-dir=`, `--work-tree=`,
|
|
28
|
+
* `--config-env=`: `remote.origin.push=:refs/heads/main` deletes the trunk
|
|
29
|
+
* with no refspec on the command line, and `remote.<name>.mirror` turns a
|
|
30
|
+
* plain push into a mirror). Any of these can hold `-d`, `:main` or `+main`;
|
|
31
|
+
* - `unparseable`: an option git does not know, an ambiguous abbreviation, a
|
|
32
|
+
* value where none is taken, a missing value, or a global option in front
|
|
33
|
+
* of `push` this file does not know. git refuses all of these itself, so
|
|
34
|
+
* refusing them costs nothing, and a newer git that grew an option this
|
|
35
|
+
* table lacks is refused rather than read as a plain push;
|
|
36
|
+
* - `runs-program`: `--receive-pack` / `--exec` name the program git starts
|
|
37
|
+
* for the far side, which for a local or `file://` remote runs on this
|
|
38
|
+
* machine, and `git --exec-path=` picks where git finds its helpers. What it runs is a command line this classifier is not reading,
|
|
39
|
+
* the reason `find -exec` is opaque (APRV-283).
|
|
40
|
+
*
|
|
41
|
+
* Negations (`--no-delete`) never clear a flag an earlier word set: a push
|
|
42
|
+
* that both deletes and un-deletes is read as deleting, which only ever picks
|
|
43
|
+
* the stricter class for a spelling nobody writes.
|
|
44
|
+
*
|
|
45
|
+
* The walk is checked against the real binary: tests/git-push-delete-spellings
|
|
46
|
+
* runs every deleting spelling against a bare local remote and asserts the ref
|
|
47
|
+
* is gone, and the generated differential there runs the corpus through git
|
|
48
|
+
* and asserts every deletion and every forced update it observed was
|
|
49
|
+
* classified at least that strictly.
|
|
50
|
+
*/
|
|
51
|
+
/** What an option does to the push, for the options whose effect matters. */
|
|
52
|
+
type PushEffect = "force" | "mirror" | "remove" | "prune" | "tags" | "all" | "submodules" | "program" | "help";
|
|
53
|
+
interface PushOption {
|
|
54
|
+
/** The long name as `builtin/push.c` spells it (`no-verify` keeps its `no-`). */
|
|
55
|
+
long: string;
|
|
56
|
+
short?: string;
|
|
57
|
+
/** `required`: the next word when not `=`-joined. `optional`: `=`-joined only. */
|
|
58
|
+
value: "none" | "required" | "optional";
|
|
59
|
+
/** `PARSE_OPT_NONEG`: no `--no-` form. */
|
|
60
|
+
noneg?: boolean;
|
|
61
|
+
/** `OPT_ALIAS`: an abbreviation matching this and its target is not ambiguous. */
|
|
62
|
+
aliasOf?: string;
|
|
63
|
+
effect?: PushEffect;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* `git push -h` on git 2.50, in `builtin/push.c` order. Order matters only for
|
|
67
|
+
* which candidate an ambiguity error names, never for which option matches.
|
|
68
|
+
*/
|
|
69
|
+
export declare const GIT_PUSH_OPTIONS: readonly PushOption[];
|
|
70
|
+
/** Can the shell turn this word into a different word, or into several? */
|
|
71
|
+
export declare function isUnreadablePushWord(word: string): boolean;
|
|
72
|
+
/** What a `git push` argv does, as far as its class is concerned. */
|
|
73
|
+
export interface GitPushReading {
|
|
74
|
+
/** `--force`, `-f`, `--force-with-lease`, `--force-if-includes`, `--mirror`. */
|
|
75
|
+
force: boolean;
|
|
76
|
+
/** `--delete` / `-d`: every refspec is a ref being removed. */
|
|
77
|
+
remove: boolean;
|
|
78
|
+
/** `--prune`: removes the remote refs a refspec pattern no longer matches. */
|
|
79
|
+
prune: boolean;
|
|
80
|
+
/** `--tags` / `--follow-tags`. */
|
|
81
|
+
tags: boolean;
|
|
82
|
+
/** `--all`, `--branches`, `--mirror`, or a submodule push: the trunk among them. */
|
|
83
|
+
all: boolean;
|
|
84
|
+
/** Non-option words: the repository, then the refspecs. */
|
|
85
|
+
positionals: string[];
|
|
86
|
+
}
|
|
87
|
+
export type GitPushParse = {
|
|
88
|
+
ok: true;
|
|
89
|
+
reading: GitPushReading;
|
|
90
|
+
} | {
|
|
91
|
+
ok: false;
|
|
92
|
+
kind: "unreadable" | "unparseable" | "runs-program";
|
|
93
|
+
detail: string;
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* Split the words after `git` at the `push` subcommand: the global options in
|
|
97
|
+
* front of it, and the push's own argv after it.
|
|
98
|
+
*/
|
|
99
|
+
export declare function splitGitPush(gitArgs: readonly string[]): {
|
|
100
|
+
globals: string[];
|
|
101
|
+
words: string[];
|
|
102
|
+
} | null;
|
|
103
|
+
/**
|
|
104
|
+
* Walk a `git push` argv (the words after `push`) the way git does.
|
|
105
|
+
*
|
|
106
|
+
* `assignments` are the `NAME=value` words in front of `git`, `substituted`
|
|
107
|
+
* says whether any word came out of a command substitution, and `globals` are
|
|
108
|
+
* the git options in front of `push` ({@link splitGitPush}).
|
|
109
|
+
*/
|
|
110
|
+
export declare function parseGitPushArgv(words: readonly string[], assignments?: readonly string[], substituted?: boolean, globals?: readonly string[]): GitPushParse;
|
|
111
|
+
export {};
|