@gaunt-sloth/core 2.0.0-beta.4 → 2.0.0-beta.5
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.
|
@@ -47,6 +47,16 @@
|
|
|
47
47
|
* rule; which of those two goes last is decided by asking which one survives when the other is
|
|
48
48
|
* long.
|
|
49
49
|
*
|
|
50
|
+
* **The hosts are named in two groups, and the second is the point of [[EXT-156]].** The gate
|
|
51
|
+
* matches an NFKC-folded command, so the hosts it extracts can be spelled differently from the call
|
|
52
|
+
* shown above them — a fullwidth `r` in `registry.npmjs.org` folds to the real npm registry, which
|
|
53
|
+
* that command does not contain. {@link approvalHostGroups} asks the item's own control of each
|
|
54
|
+
* host — *does the call text contain it?* — and the ones that fail are labelled as such rather than
|
|
55
|
+
* shown as though the call had written them. It reads: no extraction and no matcher moves.
|
|
56
|
+
* **Whoever writes the call can suppress the disclosure for free** — a trailing shell comment
|
|
57
|
+
* naming the folded spelling is enough — so read it as a signal on an unwitting call, never as a
|
|
58
|
+
* control on a deliberate one; {@link approvalHostGroups}'s docblock says why.
|
|
59
|
+
*
|
|
50
60
|
* **The hosts are framed, not allow-listed.** `core/shell/openWorld`'s `listHostsForFloorNote`
|
|
51
61
|
* quotes a host only when it passes a character allow-list and a length bound, because that
|
|
52
62
|
* sentence is our prose sitting OUTSIDE the `<command_to_evaluate>` fence in a rater's prompt,
|
|
@@ -153,6 +163,46 @@ export declare const APPROVAL_REQUEST_HEADING = "\u26A0 Gaunt Sloth is asking ab
|
|
|
153
163
|
* argument, and that block is the same block.
|
|
154
164
|
*/
|
|
155
165
|
export declare const APPROVAL_HOSTS_LABEL = "Hosts this call names:";
|
|
166
|
+
/**
|
|
167
|
+
* [[EXT-156]] — our label for the hosts the block names that the call does not SPELL that way.
|
|
168
|
+
*
|
|
169
|
+
* It is a second label rather than a sentence appended to the first because the disagreement is a
|
|
170
|
+
* property of individual hosts: a call naming three of them may spell two and fold one, and a
|
|
171
|
+
* notice over the whole block would leave a reader to work out which. Two labelled groups say it
|
|
172
|
+
* structurally, and neither carries a value read out of the call.
|
|
173
|
+
*/
|
|
174
|
+
export declare const APPROVAL_FOLDED_HOSTS_LABEL = "\u26A0 Hosts this call names but does not spell this way:";
|
|
175
|
+
/**
|
|
176
|
+
* The second row of that block: the fact the label above is only useful with.
|
|
177
|
+
*
|
|
178
|
+
* *Different characters* rather than *look-alike characters*, because what the check establishes is
|
|
179
|
+
* that the characters differ and not that they resemble one another — a compatibility form the fold
|
|
180
|
+
* rewrites need not look like anything.
|
|
181
|
+
*/
|
|
182
|
+
export declare const APPROVAL_FOLDED_HOSTS_NOTE = " The call above writes them with different characters.";
|
|
183
|
+
/**
|
|
184
|
+
* [[EXT-156]] — the same row for the call whose displayed text is only PART of what it was read
|
|
185
|
+
* from, where the note above would be false.
|
|
186
|
+
*
|
|
187
|
+
* `approvalCallText` shows `args.command` alone whenever it is a string, and a `tool`/`mcpTool`
|
|
188
|
+
* call's hosts are read off all its arguments — so a host a sibling argument names is missing from
|
|
189
|
+
* the screen without having been folded from anything. The label still holds (the call as shown
|
|
190
|
+
* does not spell it) and only the reason changes, which is why this is a second NOTE and not a
|
|
191
|
+
* third group: splitting the hosts would tell a reader that two kinds of counterparty are at stake
|
|
192
|
+
* when there is one, and the thing they must not lose is the name.
|
|
193
|
+
*
|
|
194
|
+
* It claims two things, and the predicate that picks it enforces both: what is on screen is the
|
|
195
|
+
* command argument, and it is not all of them. `showsCommandArgumentOnly` is what makes the second
|
|
196
|
+
* half true rather than merely plausible — the row is printed only for a call that carries an
|
|
197
|
+
* argument besides `command`, so there really is a "them" and the screen really does hold one of
|
|
198
|
+
* it. A call whose `command` is its ONLY argument gets the characters note instead: its arguments
|
|
199
|
+
* are all on screen, and what the reader cannot see there is a character, not an argument.
|
|
200
|
+
*
|
|
201
|
+
* It deliberately does NOT say the hosts came from the arguments it does not show — a `command`
|
|
202
|
+
* argument spelling a whole URL is read for a host like any other value, and folds rather than
|
|
203
|
+
* hides.
|
|
204
|
+
*/
|
|
205
|
+
export declare const APPROVAL_UNSHOWN_HOSTS_NOTE = " The call above shows only its command argument, not all of them.";
|
|
156
206
|
/** The text a surface shows as the call: the command, or the arguments it was given instead. */
|
|
157
207
|
export declare function approvalCallText(pending: Pick<PendingToolInterrupt, 'args'>): string;
|
|
158
208
|
/**
|
|
@@ -182,6 +232,98 @@ export declare function approvalCallText(pending: Pick<PendingToolInterrupt, 'ar
|
|
|
182
232
|
* silent on precisely that call.
|
|
183
233
|
*/
|
|
184
234
|
export declare function approvalHosts(pending: Pick<PendingToolInterrupt, 'args' | 'subject'>): string[];
|
|
235
|
+
/**
|
|
236
|
+
* One call's hosts, split by whether the call SPELLS the host this block is about to name.
|
|
237
|
+
*
|
|
238
|
+
* @see {@link approvalHostGroups}
|
|
239
|
+
*/
|
|
240
|
+
export interface ApprovalHostGroups {
|
|
241
|
+
/** Hosts that occur in {@link approvalCallText} as written. */
|
|
242
|
+
written: string[];
|
|
243
|
+
/** Hosts that do not — produced by a fold the gate applies and the call's own text does not show. */
|
|
244
|
+
folded: string[];
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* [[EXT-156]] — the hosts of one call, split by whether the call spells the ones it names.
|
|
248
|
+
*
|
|
249
|
+
* **This reads; it decides nothing.** {@link approvalHosts} and both extractions behind it are
|
|
250
|
+
* untouched, and so is the {@link approvalCategoryFor} classifier that reads them: the same hosts
|
|
251
|
+
* are named, the same floor fires, the same category is chosen. What is added is the statement of a
|
|
252
|
+
* disagreement that was previously silent.
|
|
253
|
+
*
|
|
254
|
+
* ## The disagreement, and why it exists
|
|
255
|
+
*
|
|
256
|
+
* {@link approvalCallText} returns the call's true characters. `approvalHosts` reads its hosts
|
|
257
|
+
* through the gate's own extractions, which match `core/shell/normalize`'s **NFKC-folded** form —
|
|
258
|
+
* so a host written with a character NFKC rewrites is reported in its folded spelling, and the
|
|
259
|
+
* block goes on to name a host the call never wrote. Measured on U+FF52, the fullwidth `r`:
|
|
260
|
+
* `curl -o index.html https://registry.npmjs.org/simple/` names `https://registry.npmjs.org/simple/`,
|
|
261
|
+
* which is the real npm registry and is not in that command at all.
|
|
262
|
+
*
|
|
263
|
+
* **The fold is right and is not what changes here.** The hardline blocklist and the allow-list
|
|
264
|
+
* classifier match the normalised form deliberately, so trivial obfuscation cannot smuggle a command
|
|
265
|
+
* past the guard; removing it would widen what the gate lets through. The defect is that a
|
|
266
|
+
* *human-facing* field inherited it silently, and the remedy is to say so.
|
|
267
|
+
*
|
|
268
|
+
* **The `tool`/`mcpTool` arm is checked rather than assumed.** It reaches `core/approvals/toolHost`,
|
|
269
|
+
* which reads structured arguments by `URL` parsing — a DIFFERENT fold, not no fold. Measured: `URL`
|
|
270
|
+
* applies IDNA, so the same fullwidth `r` yields `registry.npmjs.org`, and a Cyrillic `а` yields the
|
|
271
|
+
* punycode `xn--pypal-4ve.com` for an argument spelling `pаypal.com`. Both are disagreements and
|
|
272
|
+
* both are disclosed here. That arm also lower-cases its host, which is why the comparison folds
|
|
273
|
+
* ASCII case, and ASCII case only — a host argument written `REGISTRY.npmjs.org` names the same
|
|
274
|
+
* host and must not raise an alarm that trains readers to ignore this one.
|
|
275
|
+
*
|
|
276
|
+
* ## What the predicate is, and how it errs
|
|
277
|
+
*
|
|
278
|
+
* *Does the host occur in the call text?* — the item's own control, needing no offset mapping back
|
|
279
|
+
* through NFKC (which is not length-preserving) and no second notion of what a host is. It is asked
|
|
280
|
+
* of the text the reader is actually shown, which is what makes the LABEL true of every host under
|
|
281
|
+
* it: the call as displayed really does not spell it.
|
|
282
|
+
*
|
|
283
|
+
* **The suppression direction is free, and it belongs to whoever wrote the call.** The test is
|
|
284
|
+
* occurrence anywhere in the call text, so a call that also writes the folded spelling somewhere
|
|
285
|
+
* else reads as written and says nothing. On the shell arm that takes the whole folded URL, since
|
|
286
|
+
* the host there IS the URL: `echo https://registry.npmjs.org/x && curl
|
|
287
|
+
* https://registry.npmjs.org/x` is suppressed while an incidental bare hostname elsewhere is not —
|
|
288
|
+
* **and so is a trailing `#` comment naming that URL, or a dead `echo`, or an unrelated argument,
|
|
289
|
+
* none of which change anything the command does.** On the tool arm the host is a bare hostname, so
|
|
290
|
+
* a sibling argument mentioning it in prose is enough. This is not an unlucky shape a reader may
|
|
291
|
+
* hope not to meet: an author who wants no disclosure appends one comment and gets none. What would
|
|
292
|
+
* close it is a POSITIONAL test, and a positional test is a second notion of where a host lives —
|
|
293
|
+
* the thing this must not grow. The residue is bounded rather than small: a suppressed disclosure
|
|
294
|
+
* on a call that already cannot auto-approve ([[EXT-61]] floors it at `destructive` before the
|
|
295
|
+
* rater).
|
|
296
|
+
*
|
|
297
|
+
* **The other direction costs a second note row, and the label is what the two of them share.**
|
|
298
|
+
* `approvalHosts` reads a `tool`/`mcpTool` call's hosts off ALL its arguments while
|
|
299
|
+
* `approvalCallText` displays `args.command` alone whenever it is a string, so the shown text is
|
|
300
|
+
* SOMETIMES less than what the hosts came from — when the call carries an argument besides
|
|
301
|
+
* `command`, and only then; see `showsCommandArgumentOnly`. On a call whose `command` is its only
|
|
302
|
+
* argument the display is the whole of them, and the ordinary characters note is the true one
|
|
303
|
+
* there. A host named by a sibling argument is correctly labelled (the
|
|
304
|
+
* call as shown does not spell it) while *"the call above writes them with different characters"*
|
|
305
|
+
* would be false: it does not write them at all. So the label is one and the note is two, chosen by
|
|
306
|
+
* that predicate — the classification is never weakened to make one sentence fit both. Comparing
|
|
307
|
+
* against the serialised arguments instead would have "fixed" this by calling such a host WRITTEN,
|
|
308
|
+
* printing `Hosts this call names:` beside a displayed call that names nobody — which is [[EXT-156]]'s
|
|
309
|
+
* own defect reached by another route. Do not.
|
|
310
|
+
*
|
|
311
|
+
* **The note is one per BLOCK; the reason is one per HOST.** A call can fold one host by characters
|
|
312
|
+
* and keep another off the screen behind an argument it does not display, and one row cannot say
|
|
313
|
+
* both — so a call carrying an argument besides `command` states the partial display, and the
|
|
314
|
+
* reader is left to the label for the other. The label is true of every host under it either way
|
|
315
|
+
* and no host moves between the groups, so what that reader loses is a reason, never a name.
|
|
316
|
+
*
|
|
317
|
+
* ## Why the raw spelling is not reproduced here
|
|
318
|
+
*
|
|
319
|
+
* It is already on the screen: the framed call sits directly above this block and carries the call's
|
|
320
|
+
* true characters, so both forms are in front of the reader and what was missing was the statement
|
|
321
|
+
* that they differ. Recovering the raw *host substring* to print beside the folded one would need
|
|
322
|
+
* exactly the NFKC offset mapping this predicate exists to avoid — and it would buy a reader nothing
|
|
323
|
+
* they can act on, since the two spellings are by construction ones a terminal font does not
|
|
324
|
+
* distinguish. The disagreement is the signal.
|
|
325
|
+
*/
|
|
326
|
+
export declare function approvalHostGroups(pending: Pick<PendingToolInterrupt, 'args' | 'subject'>): ApprovalHostGroups;
|
|
185
327
|
/**
|
|
186
328
|
* The {@link ApprovalCategory} for one gated call.
|
|
187
329
|
*
|
|
@@ -49,6 +49,46 @@ export const APPROVAL_REQUEST_HEADING = '⚠ Gaunt Sloth is asking about this ca
|
|
|
49
49
|
* argument, and that block is the same block.
|
|
50
50
|
*/
|
|
51
51
|
export const APPROVAL_HOSTS_LABEL = 'Hosts this call names:';
|
|
52
|
+
/**
|
|
53
|
+
* [[EXT-156]] — our label for the hosts the block names that the call does not SPELL that way.
|
|
54
|
+
*
|
|
55
|
+
* It is a second label rather than a sentence appended to the first because the disagreement is a
|
|
56
|
+
* property of individual hosts: a call naming three of them may spell two and fold one, and a
|
|
57
|
+
* notice over the whole block would leave a reader to work out which. Two labelled groups say it
|
|
58
|
+
* structurally, and neither carries a value read out of the call.
|
|
59
|
+
*/
|
|
60
|
+
export const APPROVAL_FOLDED_HOSTS_LABEL = '⚠ Hosts this call names but does not spell this way:';
|
|
61
|
+
/**
|
|
62
|
+
* The second row of that block: the fact the label above is only useful with.
|
|
63
|
+
*
|
|
64
|
+
* *Different characters* rather than *look-alike characters*, because what the check establishes is
|
|
65
|
+
* that the characters differ and not that they resemble one another — a compatibility form the fold
|
|
66
|
+
* rewrites need not look like anything.
|
|
67
|
+
*/
|
|
68
|
+
export const APPROVAL_FOLDED_HOSTS_NOTE = ' The call above writes them with different characters.';
|
|
69
|
+
/**
|
|
70
|
+
* [[EXT-156]] — the same row for the call whose displayed text is only PART of what it was read
|
|
71
|
+
* from, where the note above would be false.
|
|
72
|
+
*
|
|
73
|
+
* `approvalCallText` shows `args.command` alone whenever it is a string, and a `tool`/`mcpTool`
|
|
74
|
+
* call's hosts are read off all its arguments — so a host a sibling argument names is missing from
|
|
75
|
+
* the screen without having been folded from anything. The label still holds (the call as shown
|
|
76
|
+
* does not spell it) and only the reason changes, which is why this is a second NOTE and not a
|
|
77
|
+
* third group: splitting the hosts would tell a reader that two kinds of counterparty are at stake
|
|
78
|
+
* when there is one, and the thing they must not lose is the name.
|
|
79
|
+
*
|
|
80
|
+
* It claims two things, and the predicate that picks it enforces both: what is on screen is the
|
|
81
|
+
* command argument, and it is not all of them. `showsCommandArgumentOnly` is what makes the second
|
|
82
|
+
* half true rather than merely plausible — the row is printed only for a call that carries an
|
|
83
|
+
* argument besides `command`, so there really is a "them" and the screen really does hold one of
|
|
84
|
+
* it. A call whose `command` is its ONLY argument gets the characters note instead: its arguments
|
|
85
|
+
* are all on screen, and what the reader cannot see there is a character, not an argument.
|
|
86
|
+
*
|
|
87
|
+
* It deliberately does NOT say the hosts came from the arguments it does not show — a `command`
|
|
88
|
+
* argument spelling a whole URL is read for a host like any other value, and folds rather than
|
|
89
|
+
* hides.
|
|
90
|
+
*/
|
|
91
|
+
export const APPROVAL_UNSHOWN_HOSTS_NOTE = ' The call above shows only its command argument, not all of them.';
|
|
52
92
|
/**
|
|
53
93
|
* The command a pending call would run, when the call carries one as a plain string.
|
|
54
94
|
*
|
|
@@ -64,6 +104,40 @@ function commandStringOf(pending) {
|
|
|
64
104
|
export function approvalCallText(pending) {
|
|
65
105
|
return commandStringOf(pending) ?? JSON.stringify(pending.args);
|
|
66
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* Whether the text shown as the call is **only part of** what this call's hosts were read from.
|
|
109
|
+
*
|
|
110
|
+
* `approvalHosts` takes the structured-argument arm when the subject kind is `tool` or `mcpTool`,
|
|
111
|
+
* or when there is no string `command` at all. `approvalCallText` shows `args.command` whenever it
|
|
112
|
+
* is a string. **Those two conditions are not complements**, and the gap between them is a real
|
|
113
|
+
* shape: a `tool`/`mcpTool` call carrying a string `command` argument has its hosts read off ALL
|
|
114
|
+
* its arguments while the surface displays only that one — so a host named by a sibling argument is
|
|
115
|
+
* absent from the screen without being folded from anything. An MCP server free to name its own
|
|
116
|
+
* parameters may well expose a `command`, so the shape is constructible; how often a real server
|
|
117
|
+
* emits it is not established here, and this decides only which sentence the block prints.
|
|
118
|
+
*
|
|
119
|
+
* **Being on that arm is not sufficient, and the second condition is what the name promises.** A
|
|
120
|
+
* `tool`/`mcpTool` call whose `command` IS its only argument has its whole arguments on screen: the
|
|
121
|
+
* display is not part of anything, it is the lot. A note saying otherwise sends the reader looking
|
|
122
|
+
* for arguments that do not exist while the character that folded sits on the line above — this
|
|
123
|
+
* module's own defect, moved onto the arm that was supposed to be the remedy. So the call must also
|
|
124
|
+
* carry something besides `command`, which is the literal truth condition of the sentence this
|
|
125
|
+
* picks.
|
|
126
|
+
*
|
|
127
|
+
* **An argument COUNT, and not a comparison of texts, for a measured reason.** Asking whether the
|
|
128
|
+
* displayed text differs from the serialised arguments cannot answer this: `{"command":"…"}` is a
|
|
129
|
+
* different string from `…` on every call that reaches here, the one-argument call included. A
|
|
130
|
+
* comparison that did answer it would have to be asked per HOST, which is a bigger question than
|
|
131
|
+
* choosing a sentence and is not asked here.
|
|
132
|
+
*
|
|
133
|
+
* Neither branch moves, and nothing else reads this: it picks a sentence.
|
|
134
|
+
*/
|
|
135
|
+
function showsCommandArgumentOnly(pending) {
|
|
136
|
+
const kind = pending.subject?.kind;
|
|
137
|
+
return ((kind === 'tool' || kind === 'mcpTool') &&
|
|
138
|
+
commandStringOf(pending) !== undefined &&
|
|
139
|
+
Object.keys(pending.args).length > 1);
|
|
140
|
+
}
|
|
67
141
|
/**
|
|
68
142
|
* The hosts this call names in a fetch or transfer position — the counterparty's identity, in full.
|
|
69
143
|
*
|
|
@@ -103,6 +177,108 @@ export function approvalHosts(pending) {
|
|
|
103
177
|
}
|
|
104
178
|
return hosts;
|
|
105
179
|
}
|
|
180
|
+
/**
|
|
181
|
+
* `text` with its ASCII letters lower-cased, **and no other character touched.**
|
|
182
|
+
*
|
|
183
|
+
* The restriction is the safety property, not tidiness. `String.prototype.toLowerCase` is
|
|
184
|
+
* Unicode-aware: it maps U+212A KELVIN SIGN to `k`, which is exactly the class of character the
|
|
185
|
+
* check below exists to catch — NFKC rewrites that same sign to `K`, so a full case fold would map
|
|
186
|
+
* both sides onto `k`, find them equal, and suppress the disclosure on a host whose real spelling
|
|
187
|
+
* is a Kelvin sign. An ASCII-only fold cannot merge any non-ASCII character with an ASCII one, so
|
|
188
|
+
* it removes the case noise and nothing else.
|
|
189
|
+
*/
|
|
190
|
+
function asciiLowerCase(text) {
|
|
191
|
+
return text.replace(/[A-Z]/g, (letter) => letter.toLowerCase());
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* [[EXT-156]] — the hosts of one call, split by whether the call spells the ones it names.
|
|
195
|
+
*
|
|
196
|
+
* **This reads; it decides nothing.** {@link approvalHosts} and both extractions behind it are
|
|
197
|
+
* untouched, and so is the {@link approvalCategoryFor} classifier that reads them: the same hosts
|
|
198
|
+
* are named, the same floor fires, the same category is chosen. What is added is the statement of a
|
|
199
|
+
* disagreement that was previously silent.
|
|
200
|
+
*
|
|
201
|
+
* ## The disagreement, and why it exists
|
|
202
|
+
*
|
|
203
|
+
* {@link approvalCallText} returns the call's true characters. `approvalHosts` reads its hosts
|
|
204
|
+
* through the gate's own extractions, which match `core/shell/normalize`'s **NFKC-folded** form —
|
|
205
|
+
* so a host written with a character NFKC rewrites is reported in its folded spelling, and the
|
|
206
|
+
* block goes on to name a host the call never wrote. Measured on U+FF52, the fullwidth `r`:
|
|
207
|
+
* `curl -o index.html https://registry.npmjs.org/simple/` names `https://registry.npmjs.org/simple/`,
|
|
208
|
+
* which is the real npm registry and is not in that command at all.
|
|
209
|
+
*
|
|
210
|
+
* **The fold is right and is not what changes here.** The hardline blocklist and the allow-list
|
|
211
|
+
* classifier match the normalised form deliberately, so trivial obfuscation cannot smuggle a command
|
|
212
|
+
* past the guard; removing it would widen what the gate lets through. The defect is that a
|
|
213
|
+
* *human-facing* field inherited it silently, and the remedy is to say so.
|
|
214
|
+
*
|
|
215
|
+
* **The `tool`/`mcpTool` arm is checked rather than assumed.** It reaches `core/approvals/toolHost`,
|
|
216
|
+
* which reads structured arguments by `URL` parsing — a DIFFERENT fold, not no fold. Measured: `URL`
|
|
217
|
+
* applies IDNA, so the same fullwidth `r` yields `registry.npmjs.org`, and a Cyrillic `а` yields the
|
|
218
|
+
* punycode `xn--pypal-4ve.com` for an argument spelling `pаypal.com`. Both are disagreements and
|
|
219
|
+
* both are disclosed here. That arm also lower-cases its host, which is why the comparison folds
|
|
220
|
+
* ASCII case, and ASCII case only — a host argument written `REGISTRY.npmjs.org` names the same
|
|
221
|
+
* host and must not raise an alarm that trains readers to ignore this one.
|
|
222
|
+
*
|
|
223
|
+
* ## What the predicate is, and how it errs
|
|
224
|
+
*
|
|
225
|
+
* *Does the host occur in the call text?* — the item's own control, needing no offset mapping back
|
|
226
|
+
* through NFKC (which is not length-preserving) and no second notion of what a host is. It is asked
|
|
227
|
+
* of the text the reader is actually shown, which is what makes the LABEL true of every host under
|
|
228
|
+
* it: the call as displayed really does not spell it.
|
|
229
|
+
*
|
|
230
|
+
* **The suppression direction is free, and it belongs to whoever wrote the call.** The test is
|
|
231
|
+
* occurrence anywhere in the call text, so a call that also writes the folded spelling somewhere
|
|
232
|
+
* else reads as written and says nothing. On the shell arm that takes the whole folded URL, since
|
|
233
|
+
* the host there IS the URL: `echo https://registry.npmjs.org/x && curl
|
|
234
|
+
* https://registry.npmjs.org/x` is suppressed while an incidental bare hostname elsewhere is not —
|
|
235
|
+
* **and so is a trailing `#` comment naming that URL, or a dead `echo`, or an unrelated argument,
|
|
236
|
+
* none of which change anything the command does.** On the tool arm the host is a bare hostname, so
|
|
237
|
+
* a sibling argument mentioning it in prose is enough. This is not an unlucky shape a reader may
|
|
238
|
+
* hope not to meet: an author who wants no disclosure appends one comment and gets none. What would
|
|
239
|
+
* close it is a POSITIONAL test, and a positional test is a second notion of where a host lives —
|
|
240
|
+
* the thing this must not grow. The residue is bounded rather than small: a suppressed disclosure
|
|
241
|
+
* on a call that already cannot auto-approve ([[EXT-61]] floors it at `destructive` before the
|
|
242
|
+
* rater).
|
|
243
|
+
*
|
|
244
|
+
* **The other direction costs a second note row, and the label is what the two of them share.**
|
|
245
|
+
* `approvalHosts` reads a `tool`/`mcpTool` call's hosts off ALL its arguments while
|
|
246
|
+
* `approvalCallText` displays `args.command` alone whenever it is a string, so the shown text is
|
|
247
|
+
* SOMETIMES less than what the hosts came from — when the call carries an argument besides
|
|
248
|
+
* `command`, and only then; see `showsCommandArgumentOnly`. On a call whose `command` is its only
|
|
249
|
+
* argument the display is the whole of them, and the ordinary characters note is the true one
|
|
250
|
+
* there. A host named by a sibling argument is correctly labelled (the
|
|
251
|
+
* call as shown does not spell it) while *"the call above writes them with different characters"*
|
|
252
|
+
* would be false: it does not write them at all. So the label is one and the note is two, chosen by
|
|
253
|
+
* that predicate — the classification is never weakened to make one sentence fit both. Comparing
|
|
254
|
+
* against the serialised arguments instead would have "fixed" this by calling such a host WRITTEN,
|
|
255
|
+
* printing `Hosts this call names:` beside a displayed call that names nobody — which is [[EXT-156]]'s
|
|
256
|
+
* own defect reached by another route. Do not.
|
|
257
|
+
*
|
|
258
|
+
* **The note is one per BLOCK; the reason is one per HOST.** A call can fold one host by characters
|
|
259
|
+
* and keep another off the screen behind an argument it does not display, and one row cannot say
|
|
260
|
+
* both — so a call carrying an argument besides `command` states the partial display, and the
|
|
261
|
+
* reader is left to the label for the other. The label is true of every host under it either way
|
|
262
|
+
* and no host moves between the groups, so what that reader loses is a reason, never a name.
|
|
263
|
+
*
|
|
264
|
+
* ## Why the raw spelling is not reproduced here
|
|
265
|
+
*
|
|
266
|
+
* It is already on the screen: the framed call sits directly above this block and carries the call's
|
|
267
|
+
* true characters, so both forms are in front of the reader and what was missing was the statement
|
|
268
|
+
* that they differ. Recovering the raw *host substring* to print beside the folded one would need
|
|
269
|
+
* exactly the NFKC offset mapping this predicate exists to avoid — and it would buy a reader nothing
|
|
270
|
+
* they can act on, since the two spellings are by construction ones a terminal font does not
|
|
271
|
+
* distinguish. The disagreement is the signal.
|
|
272
|
+
*/
|
|
273
|
+
export function approvalHostGroups(pending) {
|
|
274
|
+
const callText = asciiLowerCase(approvalCallText(pending));
|
|
275
|
+
const written = [];
|
|
276
|
+
const folded = [];
|
|
277
|
+
for (const host of approvalHosts(pending)) {
|
|
278
|
+
(callText.includes(asciiLowerCase(host)) ? written : folded).push(host);
|
|
279
|
+
}
|
|
280
|
+
return { written, folded };
|
|
281
|
+
}
|
|
106
282
|
/**
|
|
107
283
|
* The {@link ApprovalCategory} for one gated call.
|
|
108
284
|
*
|
|
@@ -287,14 +463,33 @@ export function approvalRequestRows(pending, options) {
|
|
|
287
463
|
// The counterparties, LAST. See this module's header for why this block and not the command is
|
|
288
464
|
// the one that ends the request: a hostile URL's identity is what a reader must not lose, and the
|
|
289
465
|
// rows nearest the prompt are the rows that survive.
|
|
290
|
-
|
|
291
|
-
|
|
466
|
+
//
|
|
467
|
+
// [[EXT-156]] — in two groups, because a host the gate's fold produced and a host the call
|
|
468
|
+
// actually wrote are different facts and the block used to state them as one. The hosts and their
|
|
469
|
+
// order within each group are `approvalHostGroups`', which reads `approvalHosts` unchanged.
|
|
470
|
+
const { written, folded } = approvalHostGroups(pending);
|
|
471
|
+
if (written.length > 0) {
|
|
292
472
|
push('warn', APPROVAL_HOSTS_LABEL);
|
|
293
|
-
for (const host of
|
|
473
|
+
for (const host of written) {
|
|
294
474
|
for (const line of frameUntrustedText(host, { width }).lines)
|
|
295
475
|
push('warn', line);
|
|
296
476
|
}
|
|
297
477
|
}
|
|
478
|
+
// The folded ones after them, and so last of all: of the two groups this is the one a reader must
|
|
479
|
+
// not lose, by the same argument that puts the whole block at the bottom. Both labels are our own
|
|
480
|
+
// prose; only the host rows are framed, exactly as before.
|
|
481
|
+
if (folded.length > 0) {
|
|
482
|
+
push('danger', APPROVAL_FOLDED_HOSTS_LABEL);
|
|
483
|
+
// One label, two reasons. The label is true of every host here — the call as SHOWN does not
|
|
484
|
+
// spell it — but the reason is not: where the surface displays `args.command` alone, a host a
|
|
485
|
+
// sibling argument names was never written with different characters, it was never written on
|
|
486
|
+
// screen at all. Saying the wrong one is how a reader concludes the block is guessing.
|
|
487
|
+
push('chrome', showsCommandArgumentOnly(pending) ? APPROVAL_UNSHOWN_HOSTS_NOTE : APPROVAL_FOLDED_HOSTS_NOTE);
|
|
488
|
+
for (const host of folded) {
|
|
489
|
+
for (const line of frameUntrustedText(host, { width }).lines)
|
|
490
|
+
push('danger', line);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
298
493
|
return rows;
|
|
299
494
|
}
|
|
300
495
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"approvalRequest.js","sourceRoot":"","sources":["../../../src/core/approvals/approvalRequest.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"approvalRequest.js","sourceRoot":"","sources":["../../../src/core/approvals/approvalRequest.ts"],"names":[],"mappings":"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"}
|