@sema-agent/core 7.10.0 → 7.11.1
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/CHANGELOG.md +90 -0
- package/dist/agents/child-model-seat.d.ts +45 -18
- package/dist/agents/child-model-seat.js +12 -8
- package/dist/agents/subagent.js +6 -5
- package/dist/agents/teacher.js +2 -2
- package/dist/core/auto-mode-defaults.d.ts +19 -3
- package/dist/core/auto-mode-defaults.js +1 -0
- package/dist/core/auto-mode.d.ts +24 -20
- package/dist/core/auto-mode.js +12 -12
- package/dist/core/gate-fold.js +1 -0
- package/dist/core/gate-lanes.d.ts +6 -1
- package/dist/core/gate-lanes.js +45 -18
- package/dist/core/governance-codes.d.ts +1 -0
- package/dist/core/governance-codes.js +1 -0
- package/dist/core/hooks.d.ts +13 -0
- package/dist/core/permission-rule-model.d.ts +5 -3
- package/dist/core/permission-rule-model.js +7 -3
- package/dist/core/persisted-rule-arms.js +4 -3
- package/dist/core/read-only-shell-table.d.ts +87 -0
- package/dist/core/read-only-shell-table.js +485 -0
- package/dist/core/read-only-shell.d.ts +42 -0
- package/dist/core/read-only-shell.js +316 -0
- package/dist/core/roles.d.ts +3 -2
- package/dist/core/runner/contracts.d.ts +70 -0
- package/dist/core/runner/gate-exit.d.ts +5 -0
- package/dist/core/runner/prepare-caps-and-workflow.js +38 -12
- package/dist/core/runner/prepare-gate-stations.js +9 -0
- package/dist/core/runner/prepare-task.js +1 -1
- package/dist/core/runner/prepare-turn-wiring.js +1 -1
- package/dist/core/runner/runtask.js +34 -510
- package/dist/core/runner/stream-halt-verbs.d.ts +38 -0
- package/dist/core/runner/stream-halt-verbs.js +82 -0
- package/dist/core/runner/stream-lifecycle-verbs.d.ts +34 -0
- package/dist/core/runner/stream-lifecycle-verbs.js +126 -0
- package/dist/core/runner/stream-reap.d.ts +30 -0
- package/dist/core/runner/stream-reap.js +40 -0
- package/dist/core/runner/stream-settle-backstop.d.ts +38 -0
- package/dist/core/runner/stream-settle-backstop.js +113 -0
- package/dist/core/runner/stream-steer-verb.d.ts +30 -0
- package/dist/core/runner/stream-steer-verb.js +185 -0
- package/dist/core/shell-lexer.d.ts +18 -0
- package/dist/core/shell-lexer.js +17 -10
- package/dist/core/shell-wrapper-table.js +8 -5
- package/dist/core/tool-policy.d.ts +4 -1
- package/dist/core/tool-policy.js +1 -1
- package/dist/core/tools.d.ts +28 -7
- package/dist/core/tools.js +44 -4
- package/dist/core/trace.d.ts +15 -0
- package/dist/engine/harness/agent-harness.d.ts +3 -1
- package/dist/engine/harness/agent-harness.js +1 -1
- package/dist/engine/harness/types.d.ts +4 -2
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -0
- package/dist/orchestration/run-workflow-tool.d.ts +5 -2
- package/dist/orchestration/run-workflow-tool.js +2 -1
- package/dist/orchestration/workflow-governance.d.ts +3 -2
- package/dist/orchestration/workflow-primitives.d.ts +4 -1
- package/dist/orchestration/workflow-primitives.js +1 -6
- package/dist/orchestration/workflow.d.ts +12 -4
- package/dist/orchestration/workflow.js +24 -7
- package/dist/prompt-assembly/turn-snapshot.d.ts +4 -2
- package/dist/tools/fs/bash-readonly-classifier.d.ts +5 -0
- package/dist/tools/fs/bash-readonly-classifier.js +1 -0
- package/dist/tools/fs/fs-bash.js +3 -2
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +35 -1
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
export const READ_ONLY_FLAG_ARITIES = ["none", "number", "string", "char", "{}", "EOF"];
|
|
2
|
+
export const FLAG_VALUE_ACCEPTS = {
|
|
3
|
+
none: () => false,
|
|
4
|
+
number: (value) => /^\d+$/.test(value),
|
|
5
|
+
string: () => true,
|
|
6
|
+
char: (value) => value.length === 1,
|
|
7
|
+
"{}": (value) => value === "{}",
|
|
8
|
+
EOF: (value) => value === "EOF",
|
|
9
|
+
};
|
|
10
|
+
const GIT_REFS = { "--all": "none", "--branches": "none", "--tags": "none", "--remotes": "none" };
|
|
11
|
+
const GIT_DATES = { "--since": "string", "--after": "string", "--until": "string", "--before": "string" };
|
|
12
|
+
const GIT_LOG_VIEW = { "--oneline": "none", "--graph": "none", "--decorate": "none", "--no-decorate": "none", "--date": "string", "--relative-date": "none" };
|
|
13
|
+
const GIT_COUNT = { "--max-count": "number", "-n": "number" };
|
|
14
|
+
const GIT_STAT = { "--stat": "none", "--numstat": "none", "--shortstat": "none", "--name-only": "none", "--name-status": "none" };
|
|
15
|
+
const GIT_COLOR = { "--color": "none", "--no-color": "none" };
|
|
16
|
+
const GIT_PATCH = { "--patch": "none", "-p": "none", "--no-patch": "none", "--no-ext-diff": "none", "-s": "none" };
|
|
17
|
+
const GIT_AUTHOR = { "--author": "string", "--committer": "string", "--grep": "string" };
|
|
18
|
+
const SIGNATURE_FORMAT = /%[-+ ]?G|%\(\*?signature/;
|
|
19
|
+
const NAMED_FORMATS = new Set(["oneline", "short", "medium", "full", "fuller", "email", "raw"]);
|
|
20
|
+
const unknownNamedFormat = (value) => value !== "" && !value.includes("%") && !value.startsWith("format:") && !value.startsWith("tformat:") && !NAMED_FORMATS.has(value);
|
|
21
|
+
const formatOperandDanger = (args) => {
|
|
22
|
+
for (let i = 0; i < args.length; i++) {
|
|
23
|
+
const word = args[i];
|
|
24
|
+
if (SIGNATURE_FORMAT.test(word))
|
|
25
|
+
return true;
|
|
26
|
+
for (const flag of ["--format", "--pretty", "--sort"]) {
|
|
27
|
+
let value;
|
|
28
|
+
if (word === flag && i + 1 < args.length)
|
|
29
|
+
value = args[i + 1];
|
|
30
|
+
else if (word.startsWith(`${flag}=`))
|
|
31
|
+
value = word.slice(flag.length + 1);
|
|
32
|
+
if (value === undefined)
|
|
33
|
+
continue;
|
|
34
|
+
if (SIGNATURE_FORMAT.test(value) || value.includes("signature"))
|
|
35
|
+
return true;
|
|
36
|
+
if (flag !== "--sort" && unknownNamedFormat(value))
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return false;
|
|
41
|
+
};
|
|
42
|
+
const ghOperandDanger = (args) => {
|
|
43
|
+
for (const word of args) {
|
|
44
|
+
if (word === "")
|
|
45
|
+
continue;
|
|
46
|
+
let operand = word;
|
|
47
|
+
if (word.startsWith("-")) {
|
|
48
|
+
const eq = word.indexOf("=");
|
|
49
|
+
if (eq === -1)
|
|
50
|
+
continue;
|
|
51
|
+
operand = word.slice(eq + 1);
|
|
52
|
+
if (operand === "")
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (!operand.includes("/") && !operand.includes("://") && !operand.includes("@"))
|
|
56
|
+
continue;
|
|
57
|
+
if (operand.includes("://") || operand.includes("@"))
|
|
58
|
+
return true;
|
|
59
|
+
if ((operand.match(/\//g) ?? []).length >= 2)
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
return false;
|
|
63
|
+
};
|
|
64
|
+
const DOCKER_RETARGET_FLAGS = ["-H", "-c", "-r", "--host", "--context", "--config", "--tlscacert", "--tlscert", "--tlskey", "--url", "--connection", "--identity", "--remote", "--module", "--out"];
|
|
65
|
+
const DOCKER_RETARGET_LETTERS = new Set(DOCKER_RETARGET_FLAGS.filter((f) => f.length === 2).map((f) => f[1]));
|
|
66
|
+
export const dockerRetargetDanger = (args) => args.some((word) => {
|
|
67
|
+
if (DOCKER_RETARGET_FLAGS.some((f) => word === f || word.startsWith(`${f}=`) || (f.length === 2 && word.length > 2 && word.startsWith(f))))
|
|
68
|
+
return true;
|
|
69
|
+
const cluster = /^-([A-Za-z]+)/.exec(word)?.[1];
|
|
70
|
+
if (cluster !== undefined && cluster.length >= 2)
|
|
71
|
+
for (const letter of cluster)
|
|
72
|
+
if (DOCKER_RETARGET_LETTERS.has(letter))
|
|
73
|
+
return true;
|
|
74
|
+
return false;
|
|
75
|
+
});
|
|
76
|
+
const before = (s, sep) => s.split(sep)[0];
|
|
77
|
+
const GREP_FLAGS = { "-e": "string", "--regexp": "string", "-f": "string", "--file": "string", "-F": "none", "--fixed-strings": "none", "-G": "none", "--basic-regexp": "none", "-E": "none", "--extended-regexp": "none", "-P": "none", "--perl-regexp": "none", "-i": "none", "--ignore-case": "none", "--no-ignore-case": "none", "-v": "none", "--invert-match": "none", "-w": "none", "--word-regexp": "none", "-x": "none", "--line-regexp": "none", "-c": "none", "--count": "none", "--color": "string", "--colour": "string", "-L": "none", "--files-without-match": "none", "-l": "none", "--files-with-matches": "none", "-m": "number", "--max-count": "number", "-o": "none", "--only-matching": "none", "-q": "none", "--quiet": "none", "--silent": "none", "-s": "none", "--no-messages": "none", "-b": "none", "--byte-offset": "none", "-H": "none", "--with-filename": "none", "-h": "none", "--no-filename": "none", "--label": "string", "-n": "none", "--line-number": "none", "-T": "none", "--initial-tab": "none", "-u": "none", "--unix-byte-offsets": "none", "-Z": "none", "--null": "none", "-z": "none", "--null-data": "none", "-A": "number", "--after-context": "number", "-B": "number", "--before-context": "number", "-C": "number", "--context": "number", "--group-separator": "string", "--no-group-separator": "none", "-a": "none", "--text": "none", "--binary-files": "string", "-I": "none", "-D": "string", "--devices": "string", "-d": "string", "--directories": "string", "--exclude": "string", "--exclude-from": "string", "--exclude-dir": "string", "--include": "string", "-r": "none", "--recursive": "none", "-R": "none", "--dereference-recursive": "none", "--line-buffered": "none", "-U": "none", "--binary": "none", "--help": "none", "-V": "none", "--version": "none" };
|
|
78
|
+
const FD_FLAGS = { "-h": "none", "--help": "none", "-V": "none", "--version": "none", "-H": "none", "--hidden": "none", "-I": "none", "--no-ignore": "none", "--no-ignore-vcs": "none", "--no-ignore-parent": "none", "-s": "none", "--case-sensitive": "none", "-i": "none", "--ignore-case": "none", "-g": "none", "--glob": "none", "--regex": "none", "-F": "none", "--fixed-strings": "none", "-a": "none", "--absolute-path": "none", "-L": "none", "--follow": "none", "-p": "none", "--full-path": "none", "-0": "none", "--print0": "none", "-d": "number", "--max-depth": "number", "--min-depth": "number", "--exact-depth": "number", "-t": "string", "--type": "string", "-e": "string", "--extension": "string", "-S": "string", "--size": "string", "--changed-within": "string", "--changed-before": "string", "-o": "string", "--owner": "string", "-E": "string", "--exclude": "string", "--ignore-file": "string", "-c": "string", "--color": "string", "-j": "number", "--threads": "number", "--max-buffer-time": "string", "--max-results": "number", "-1": "none", "-q": "none", "--quiet": "none", "--show-errors": "none", "--strip-cwd-prefix": "none", "--one-file-system": "none", "--prune": "none", "--search-path": "string", "--base-directory": "string", "--path-separator": "string", "--batch-size": "number", "--no-require-git": "none", "--hyperlink": "string", "--and": "string", "--format": "string" };
|
|
79
|
+
const CHECKSUM_FLAGS = { "-b": "none", "--binary": "none", "-t": "none", "--text": "none", "-c": "none", "--check": "none", "--ignore-missing": "none", "--quiet": "none", "--status": "none", "--strict": "none", "-w": "none", "--warn": "none", "--tag": "none", "-z": "none", "--zero": "none", "--help": "none", "--version": "none" };
|
|
80
|
+
const TEST_INTEGER_OPERATORS = new Set(["-eq", "-ne", "-lt", "-le", "-gt", "-ge"]);
|
|
81
|
+
const TEST_INTEGER = /^-?(0[xX][0-9a-fA-F]+|[0-9]+#[0-9a-zA-Z]+|[0-9]+)$/;
|
|
82
|
+
export const READ_ONLY_COMMAND_TABLE = {
|
|
83
|
+
xargs: { safeFlags: { "-I": "{}", "-n": "number", "-P": "number", "-L": "number", "-s": "number", "-E": "EOF", "-0": "none", "-t": "none", "-r": "none", "-x": "none", "-d": "char" } },
|
|
84
|
+
"git diff": {
|
|
85
|
+
safeFlags: { ...GIT_STAT, ...GIT_COLOR, "--dirstat": "none", "--summary": "none", "--patch-with-stat": "none", "--word-diff": "none", "--word-diff-regex": "string", "--color-words": "none", "--no-renames": "none", "--no-ext-diff": "none", "--check": "none", "--ws-error-highlight": "string", "--full-index": "none", "--binary": "none", "--abbrev": "number", "--break-rewrites": "none", "--find-renames": "none", "--find-copies": "none", "--find-copies-harder": "none", "--irreversible-delete": "none", "--diff-algorithm": "string", "--histogram": "none", "--patience": "none", "--minimal": "none", "--ignore-space-at-eol": "none", "--ignore-space-change": "none", "--ignore-all-space": "none", "--ignore-blank-lines": "none", "--inter-hunk-context": "number", "--function-context": "none", "--exit-code": "none", "--quiet": "none", "--cached": "none", "--staged": "none", "--pickaxe-regex": "none", "--pickaxe-all": "none", "--no-index": "none", "--relative": "string", "--diff-filter": "string", "-p": "none", "-u": "none", "-s": "none", "-M": "none", "-C": "none", "-B": "none", "-D": "none", "-l": "none", "-S": "string", "-G": "string", "-O": "string", "-R": "none" },
|
|
86
|
+
},
|
|
87
|
+
"git log": {
|
|
88
|
+
safeFlags: { ...GIT_LOG_VIEW, ...GIT_REFS, ...GIT_DATES, ...GIT_COUNT, ...GIT_STAT, ...GIT_COLOR, ...GIT_PATCH, ...GIT_AUTHOR, "--abbrev-commit": "none", "--full-history": "none", "--dense": "none", "--sparse": "none", "--simplify-merges": "none", "--ancestry-path": "none", "--source": "none", "--first-parent": "none", "--merges": "none", "--no-merges": "none", "--reverse": "none", "--walk-reflogs": "none", "--skip": "number", "--max-age": "number", "--min-age": "number", "--no-min-parents": "none", "--no-max-parents": "none", "--follow": "none", "--no-walk": "none", "--left-right": "none", "--cherry-mark": "none", "--cherry-pick": "none", "--boundary": "none", "--topo-order": "none", "--date-order": "none", "--author-date-order": "none", "--pretty": "string", "--format": "string", "--diff-filter": "string", "-S": "string", "-G": "string", "--pickaxe-regex": "none", "--pickaxe-all": "none" },
|
|
89
|
+
dangerous: formatOperandDanger,
|
|
90
|
+
},
|
|
91
|
+
"git show": {
|
|
92
|
+
safeFlags: { ...GIT_LOG_VIEW, ...GIT_STAT, ...GIT_COLOR, ...GIT_PATCH, "--abbrev-commit": "none", "--word-diff": "none", "--word-diff-regex": "string", "--color-words": "none", "--pretty": "string", "--format": "string", "--first-parent": "none", "--raw": "none", "--diff-filter": "string", "-m": "none", "--quiet": "none" },
|
|
93
|
+
dangerous: formatOperandDanger,
|
|
94
|
+
},
|
|
95
|
+
"git shortlog": {
|
|
96
|
+
safeFlags: { ...GIT_REFS, ...GIT_DATES, "-s": "none", "--summary": "none", "-n": "none", "--numbered": "none", "-e": "none", "--email": "none", "-c": "none", "--committer": "none", "--group": "string", "--format": "string", "--no-merges": "none", "--author": "string" },
|
|
97
|
+
dangerous: formatOperandDanger,
|
|
98
|
+
},
|
|
99
|
+
"git reflog": {
|
|
100
|
+
safeFlags: { ...GIT_LOG_VIEW, ...GIT_REFS, ...GIT_DATES, ...GIT_COUNT, ...GIT_AUTHOR },
|
|
101
|
+
dangerous: (args) => {
|
|
102
|
+
const reading = new Set(["show", "list"]);
|
|
103
|
+
const writing = new Set(["expire", "delete", "exists", "drop", "write"]);
|
|
104
|
+
const first = args[0];
|
|
105
|
+
if (first !== undefined && first !== "" && !first.startsWith("-") && !reading.has(first))
|
|
106
|
+
return true;
|
|
107
|
+
return args.some((w) => writing.has(w));
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
"git stash list": { safeFlags: { ...GIT_LOG_VIEW, ...GIT_REFS, ...GIT_COUNT } },
|
|
111
|
+
"git ls-remote": {
|
|
112
|
+
safeFlags: { "--branches": "none", "-b": "none", "--tags": "none", "-t": "none", "--heads": "none", "-h": "none", "--refs": "none", "--quiet": "none", "-q": "none", "--exit-code": "none", "--get-url": "none", "--symref": "none", "--sort": "string" },
|
|
113
|
+
dangerous: (args) => {
|
|
114
|
+
if (args.some((w) => w === "-o" || w === "--server-option" || w.startsWith("--server-option=")))
|
|
115
|
+
return true;
|
|
116
|
+
let pastDashes = false;
|
|
117
|
+
for (let i = 0; i < args.length; i++) {
|
|
118
|
+
const w = args[i];
|
|
119
|
+
if (w === "")
|
|
120
|
+
continue;
|
|
121
|
+
if (!pastDashes && w === "--") {
|
|
122
|
+
pastDashes = true;
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (!pastDashes && w.startsWith("-") && w !== "-") {
|
|
126
|
+
if (w === "--sort")
|
|
127
|
+
i++;
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
return formatOperandDanger(args);
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
"git status": { safeFlags: { "--short": "none", "-s": "none", "--branch": "none", "-b": "none", "--porcelain": "none", "--long": "none", "--verbose": "none", "-v": "none", "--untracked-files": "string", "-u": "string", "--ignored": "none", "--ignore-submodules": "string", "--column": "none", "--no-column": "none", "--ahead-behind": "none", "--no-ahead-behind": "none", "--renames": "none", "--no-renames": "none", "--find-renames": "string", "-M": "string" } },
|
|
136
|
+
"git blame": { safeFlags: { ...GIT_COLOR, "-L": "string", "--porcelain": "none", "-p": "none", "--line-porcelain": "none", "--incremental": "none", "--root": "none", "--show-stats": "none", "--show-name": "none", "--show-number": "none", "-n": "none", "--show-email": "none", "-e": "none", "-f": "none", "--date": "string", "-w": "none", "--ignore-rev": "string", "--ignore-revs-file": "string", "-M": "none", "-C": "none", "--score-debug": "none", "--abbrev": "number", "-s": "none", "-l": "none", "-t": "none" } },
|
|
137
|
+
"git ls-files": { safeFlags: { "--cached": "none", "-c": "none", "--deleted": "none", "-d": "none", "--modified": "none", "-m": "none", "--others": "none", "-o": "none", "--ignored": "none", "-i": "none", "--stage": "none", "-s": "none", "--killed": "none", "-k": "none", "--unmerged": "none", "-u": "none", "--directory": "none", "--no-empty-directory": "none", "--eol": "none", "--full-name": "none", "--abbrev": "number", "--debug": "none", "-z": "none", "-t": "none", "-v": "none", "-f": "none", "--exclude": "string", "-x": "string", "--exclude-from": "string", "-X": "string", "--exclude-per-directory": "string", "--exclude-standard": "none", "--error-unmatch": "none", "--recurse-submodules": "none" } },
|
|
138
|
+
"git config --get": { safeFlags: { "--local": "none", "--global": "none", "--system": "none", "--worktree": "none", "--default": "string", "--type": "string", "--bool": "none", "--int": "none", "--bool-or-int": "none", "--path": "none", "--expiry-date": "none", "-z": "none", "--null": "none", "--name-only": "none", "--show-origin": "none", "--show-scope": "none" } },
|
|
139
|
+
"git remote show": {
|
|
140
|
+
safeFlags: { "-n": "none" },
|
|
141
|
+
dangerous: (args) => {
|
|
142
|
+
const dd = args.indexOf("--");
|
|
143
|
+
const flags = dd === -1 ? args : args.slice(0, dd);
|
|
144
|
+
const operands = dd === -1 ? [] : args.slice(dd + 1);
|
|
145
|
+
const names = flags.filter((w) => w !== "-n").concat(operands);
|
|
146
|
+
if (names.length !== 1)
|
|
147
|
+
return true;
|
|
148
|
+
if (!flags.includes("-n"))
|
|
149
|
+
return true;
|
|
150
|
+
return !/^[a-zA-Z0-9_][a-zA-Z0-9_-]*$/.test(names[0]);
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
"git remote": { safeFlags: { "-v": "none", "--verbose": "none" }, dangerous: (args) => args.some((w) => w !== "-v" && w !== "--verbose") },
|
|
154
|
+
"git merge-base": { safeFlags: { "--is-ancestor": "none", "--fork-point": "none", "--octopus": "none", "--independent": "none", "--all": "none" } },
|
|
155
|
+
"git rev-parse": { safeFlags: { "--verify": "none", "--short": "string", "--abbrev-ref": "none", "--symbolic": "none", "--symbolic-full-name": "none", "--show-toplevel": "none", "--show-cdup": "none", "--show-prefix": "none", "--git-dir": "none", "--git-common-dir": "none", "--absolute-git-dir": "none", "--show-superproject-working-tree": "none", "--is-inside-work-tree": "none", "--is-inside-git-dir": "none", "--is-bare-repository": "none", "--is-shallow-repository": "none", "--is-shallow-update": "none", "--path-prefix": "none" } },
|
|
156
|
+
"git rev-list": {
|
|
157
|
+
safeFlags: { ...GIT_REFS, ...GIT_DATES, ...GIT_COUNT, ...GIT_AUTHOR, "--count": "none", "--reverse": "none", "--first-parent": "none", "--ancestry-path": "none", "--merges": "none", "--no-merges": "none", "--min-parents": "number", "--max-parents": "number", "--no-min-parents": "none", "--no-max-parents": "none", "--skip": "number", "--max-age": "number", "--min-age": "number", "--walk-reflogs": "none", "--oneline": "none", "--abbrev-commit": "none", "--pretty": "string", "--format": "string", "--abbrev": "number", "--full-history": "none", "--dense": "none", "--sparse": "none", "--source": "none", "--graph": "none" },
|
|
158
|
+
dangerous: formatOperandDanger,
|
|
159
|
+
},
|
|
160
|
+
"git describe": { safeFlags: { "--tags": "none", "--match": "string", "--exclude": "string", "--long": "none", "--abbrev": "number", "--always": "none", "--contains": "none", "--first-match": "none", "--exact-match": "none", "--candidates": "number", "--dirty": "none", "--broken": "none" } },
|
|
161
|
+
"git cat-file": { safeFlags: { "-t": "none", "-s": "none", "-p": "none", "-e": "none", "--batch-check": "none", "--allow-undetermined-type": "none" } },
|
|
162
|
+
"git for-each-ref": {
|
|
163
|
+
safeFlags: { "--format": "string", "--sort": "string", "--count": "number", "--contains": "string", "--no-contains": "string", "--merged": "string", "--no-merged": "string", "--points-at": "string" },
|
|
164
|
+
dangerous: formatOperandDanger,
|
|
165
|
+
},
|
|
166
|
+
"git grep": { safeFlags: { "-e": "string", "-E": "none", "--extended-regexp": "none", "-G": "none", "--basic-regexp": "none", "-F": "none", "--fixed-strings": "none", "-P": "none", "--perl-regexp": "none", "-i": "none", "--ignore-case": "none", "-v": "none", "--invert-match": "none", "-w": "none", "--word-regexp": "none", "-n": "none", "--line-number": "none", "-c": "none", "--count": "none", "-l": "none", "--files-with-matches": "none", "-L": "none", "--files-without-match": "none", "-h": "none", "-H": "none", "--heading": "none", "--break": "none", "--full-name": "none", "--color": "none", "--no-color": "none", "-o": "none", "--only-matching": "none", "-A": "number", "--after-context": "number", "-B": "number", "--before-context": "number", "-C": "number", "--context": "number", "--and": "none", "--or": "none", "--not": "none", "--max-depth": "number", "--untracked": "none", "--no-index": "none", "--recurse-submodules": "none", "--cached": "none", "--threads": "number", "-q": "none", "--quiet": "none" } },
|
|
167
|
+
"git stash show": { safeFlags: { ...GIT_STAT, ...GIT_COLOR, ...GIT_PATCH, "--word-diff": "none", "--word-diff-regex": "string", "--diff-filter": "string", "--abbrev": "number" } },
|
|
168
|
+
"git worktree list": { safeFlags: { "--porcelain": "none", "-v": "none", "--verbose": "none", "--expire": "string" } },
|
|
169
|
+
"git tag": {
|
|
170
|
+
safeFlags: { "-l": "none", "--list": "none", "-n": "number", "--contains": "string", "--no-contains": "string", "--merged": "string", "--no-merged": "string", "--sort": "string", "--format": "string", "--points-at": "string", "--column": "none", "--no-column": "none", "-i": "none", "--ignore-case": "none" },
|
|
171
|
+
dangerous: (args) => {
|
|
172
|
+
if (formatOperandDanger(args))
|
|
173
|
+
return true;
|
|
174
|
+
const valued = new Set(["--contains", "--no-contains", "--merged", "--no-merged", "--points-at", "--sort", "--format", "-n"]);
|
|
175
|
+
let i = 0;
|
|
176
|
+
let listing = false;
|
|
177
|
+
let pastDashes = false;
|
|
178
|
+
while (i < args.length) {
|
|
179
|
+
const w = args[i];
|
|
180
|
+
if (w === "") {
|
|
181
|
+
i++;
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
if (w === "--" && !pastDashes) {
|
|
185
|
+
pastDashes = true;
|
|
186
|
+
i++;
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
if (!pastDashes && w.startsWith("-")) {
|
|
190
|
+
if (w === "--list" || w === "-l")
|
|
191
|
+
listing = true;
|
|
192
|
+
else if (w[0] === "-" && w[1] !== "-" && w.length > 2 && !w.includes("=") && w.slice(1).includes("l"))
|
|
193
|
+
listing = true;
|
|
194
|
+
if (w.includes("="))
|
|
195
|
+
i++;
|
|
196
|
+
else if (valued.has(w))
|
|
197
|
+
i += 2;
|
|
198
|
+
else
|
|
199
|
+
i++;
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
if (!listing)
|
|
203
|
+
return true;
|
|
204
|
+
i++;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return false;
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
"git branch": {
|
|
211
|
+
safeFlags: { "-l": "none", "--list": "none", "-a": "none", "--all": "none", "-r": "none", "--remotes": "none", "-v": "none", "-vv": "none", "--verbose": "none", "--color": "none", "--no-color": "none", "--column": "none", "--no-column": "none", "--abbrev": "number", "--no-abbrev": "none", "--contains": "string", "--no-contains": "string", "--merged": "none", "--no-merged": "none", "--points-at": "string", "--sort": "string", "--show-current": "none", "-i": "none", "--ignore-case": "none" },
|
|
212
|
+
dangerous: (args) => {
|
|
213
|
+
if (formatOperandDanger(args))
|
|
214
|
+
return true;
|
|
215
|
+
const valued = new Set(["--contains", "--no-contains", "--points-at", "--sort"]);
|
|
216
|
+
const optionalOperand = new Set(["--merged", "--no-merged"]);
|
|
217
|
+
let i = 0;
|
|
218
|
+
let lastFlag = "";
|
|
219
|
+
let listing = false;
|
|
220
|
+
let pastDashes = false;
|
|
221
|
+
while (i < args.length) {
|
|
222
|
+
const w = args[i];
|
|
223
|
+
if (w === "") {
|
|
224
|
+
i++;
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
if (w === "--" && !pastDashes) {
|
|
228
|
+
pastDashes = true;
|
|
229
|
+
lastFlag = "";
|
|
230
|
+
i++;
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
if (!pastDashes && w.startsWith("-")) {
|
|
234
|
+
if (w === "--list" || w === "-l")
|
|
235
|
+
listing = true;
|
|
236
|
+
else if (w[0] === "-" && w[1] !== "-" && w.length > 2 && !w.includes("=") && w.slice(1).includes("l"))
|
|
237
|
+
listing = true;
|
|
238
|
+
if (w.includes("=")) {
|
|
239
|
+
lastFlag = before(w, "=");
|
|
240
|
+
i++;
|
|
241
|
+
}
|
|
242
|
+
else if (valued.has(w)) {
|
|
243
|
+
lastFlag = w;
|
|
244
|
+
i += 2;
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
lastFlag = w;
|
|
248
|
+
i++;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
if (!listing && !optionalOperand.has(lastFlag))
|
|
253
|
+
return true;
|
|
254
|
+
i++;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return false;
|
|
258
|
+
},
|
|
259
|
+
},
|
|
260
|
+
"gh pr view": { safeFlags: { "--json": "string", "--comments": "none", "--repo": "string", "-R": "string" }, dangerous: ghOperandDanger },
|
|
261
|
+
"gh pr list": { safeFlags: { "--state": "string", "-s": "string", "--author": "string", "--assignee": "string", "--label": "string", "--limit": "number", "-L": "number", "--base": "string", "--head": "string", "--search": "string", "--json": "string", "--draft": "none", "--app": "string", "--repo": "string", "-R": "string" }, dangerous: ghOperandDanger },
|
|
262
|
+
"gh pr diff": { safeFlags: { "--color": "string", "--name-only": "none", "--patch": "none", "--repo": "string", "-R": "string" }, dangerous: ghOperandDanger },
|
|
263
|
+
"gh pr checks": { safeFlags: { "--watch": "none", "--required": "none", "--fail-fast": "none", "--json": "string", "--interval": "number", "--repo": "string", "-R": "string" }, dangerous: ghOperandDanger },
|
|
264
|
+
"gh issue view": { safeFlags: { "--json": "string", "--comments": "none", "--repo": "string", "-R": "string" }, dangerous: ghOperandDanger },
|
|
265
|
+
"gh issue list": { safeFlags: { "--state": "string", "-s": "string", "--assignee": "string", "--author": "string", "--label": "string", "--limit": "number", "-L": "number", "--milestone": "string", "--search": "string", "--json": "string", "--app": "string", "--repo": "string", "-R": "string" }, dangerous: ghOperandDanger },
|
|
266
|
+
"gh repo view": { safeFlags: { "--json": "string" }, dangerous: ghOperandDanger },
|
|
267
|
+
"gh run list": { safeFlags: { "--branch": "string", "-b": "string", "--status": "string", "-s": "string", "--workflow": "string", "-w": "string", "--limit": "number", "-L": "number", "--json": "string", "--repo": "string", "-R": "string", "--event": "string", "-e": "string", "--user": "string", "-u": "string", "--created": "string", "--commit": "string", "-c": "string" }, dangerous: ghOperandDanger },
|
|
268
|
+
"gh run view": { safeFlags: { "--log": "none", "--log-failed": "none", "--exit-status": "none", "--verbose": "none", "-v": "none", "--json": "string", "--repo": "string", "-R": "string", "--job": "string", "-j": "string", "--attempt": "number", "-a": "number" }, dangerous: ghOperandDanger },
|
|
269
|
+
"gh auth status": { safeFlags: { "--active": "none", "-a": "none", "--hostname": "string", "-h": "string", "--json": "string" }, dangerous: ghOperandDanger },
|
|
270
|
+
"gh pr status": { safeFlags: { "--conflict-status": "none", "-c": "none", "--json": "string", "--repo": "string", "-R": "string" }, dangerous: ghOperandDanger },
|
|
271
|
+
"gh issue status": { safeFlags: { "--json": "string", "--repo": "string", "-R": "string" }, dangerous: ghOperandDanger },
|
|
272
|
+
"gh release list": { safeFlags: { "--exclude-drafts": "none", "--exclude-pre-releases": "none", "--json": "string", "--limit": "number", "-L": "number", "--order": "string", "-O": "string", "--repo": "string", "-R": "string" }, dangerous: ghOperandDanger },
|
|
273
|
+
"gh release view": { safeFlags: { "--json": "string", "--repo": "string", "-R": "string" }, dangerous: ghOperandDanger },
|
|
274
|
+
"gh workflow list": { safeFlags: { "--all": "none", "-a": "none", "--json": "string", "--limit": "number", "-L": "number", "--repo": "string", "-R": "string" }, dangerous: ghOperandDanger },
|
|
275
|
+
"gh workflow view": { safeFlags: { "--ref": "string", "-r": "string", "--yaml": "none", "-y": "none", "--repo": "string", "-R": "string" }, dangerous: ghOperandDanger },
|
|
276
|
+
"gh label list": { safeFlags: { "--json": "string", "--limit": "number", "-L": "number", "--order": "string", "--search": "string", "-S": "string", "--sort": "string", "--repo": "string", "-R": "string" }, dangerous: ghOperandDanger },
|
|
277
|
+
"gh search repos": { safeFlags: { "--archived": "none", "--created": "string", "--followers": "string", "--forks": "string", "--good-first-issues": "string", "--help-wanted-issues": "string", "--include-forks": "string", "--json": "string", "--language": "string", "--license": "string", "--limit": "number", "-L": "number", "--match": "string", "--number-topics": "string", "--order": "string", "--owner": "string", "--size": "string", "--sort": "string", "--stars": "string", "--topic": "string", "--updated": "string", "--visibility": "string" } },
|
|
278
|
+
"gh search issues": { safeFlags: { "--app": "string", "--assignee": "string", "--author": "string", "--closed": "string", "--commenter": "string", "--comments": "string", "--created": "string", "--include-prs": "none", "--interactions": "string", "--involves": "string", "--json": "string", "--label": "string", "--language": "string", "--limit": "number", "-L": "number", "--locked": "none", "--match": "string", "--mentions": "string", "--milestone": "string", "--no-assignee": "none", "--no-label": "none", "--no-milestone": "none", "--no-project": "none", "--order": "string", "--owner": "string", "--project": "string", "--reactions": "string", "--repo": "string", "-R": "string", "--sort": "string", "--state": "string", "--team-mentions": "string", "--updated": "string", "--visibility": "string" } },
|
|
279
|
+
"gh search prs": { safeFlags: { "--app": "string", "--assignee": "string", "--author": "string", "--base": "string", "-B": "string", "--checks": "string", "--closed": "string", "--commenter": "string", "--comments": "string", "--created": "string", "--draft": "none", "--head": "string", "-H": "string", "--interactions": "string", "--involves": "string", "--json": "string", "--label": "string", "--language": "string", "--limit": "number", "-L": "number", "--locked": "none", "--match": "string", "--mentions": "string", "--merged": "none", "--merged-at": "string", "--milestone": "string", "--no-assignee": "none", "--no-label": "none", "--no-milestone": "none", "--no-project": "none", "--order": "string", "--owner": "string", "--project": "string", "--reactions": "string", "--repo": "string", "-R": "string", "--review": "string", "--review-requested": "string", "--reviewed-by": "string", "--sort": "string", "--state": "string", "--team-mentions": "string", "--updated": "string", "--visibility": "string" } },
|
|
280
|
+
"gh search commits": { safeFlags: { "--author": "string", "--author-date": "string", "--author-email": "string", "--author-name": "string", "--committer": "string", "--committer-date": "string", "--committer-email": "string", "--committer-name": "string", "--hash": "string", "--json": "string", "--limit": "number", "-L": "number", "--merge": "none", "--order": "string", "--owner": "string", "--parent": "string", "--repo": "string", "-R": "string", "--sort": "string", "--tree": "string", "--visibility": "string" } },
|
|
281
|
+
"gh search code": { safeFlags: { "--extension": "string", "--filename": "string", "--json": "string", "--language": "string", "--limit": "number", "-L": "number", "--match": "string", "--owner": "string", "--repo": "string", "-R": "string", "--size": "string" } },
|
|
282
|
+
"docker logs": { safeFlags: { "--follow": "none", "-f": "none", "--tail": "string", "-n": "string", "--timestamps": "none", "-t": "none", "--since": "string", "--until": "string", "--details": "none" }, dangerous: dockerRetargetDanger },
|
|
283
|
+
"docker inspect": { safeFlags: { "--format": "string", "-f": "string", "--type": "string", "--size": "none", "-s": "none" }, dangerous: dockerRetargetDanger },
|
|
284
|
+
file: { safeFlags: { "--brief": "none", "-b": "none", "--mime": "none", "-i": "none", "--mime-type": "none", "--mime-encoding": "none", "--apple": "none", "--check-encoding": "none", "-c": "none", "--exclude": "string", "--exclude-quiet": "string", "--print0": "none", "-0": "none", "-F": "string", "--separator": "string", "--help": "none", "--version": "none", "-v": "none", "--no-dereference": "none", "-h": "none", "--dereference": "none", "-L": "none", "--keep-going": "none", "-k": "none", "--list": "none", "-l": "none", "--no-buffer": "none", "-n": "none", "--preserve-date": "none", "-p": "none", "--raw": "none", "-r": "none", "-s": "none", "--special-files": "none" } },
|
|
285
|
+
sort: { safeFlags: { "--ignore-leading-blanks": "none", "-b": "none", "--dictionary-order": "none", "-d": "none", "--ignore-case": "none", "-f": "none", "--general-numeric-sort": "none", "-g": "none", "--human-numeric-sort": "none", "-h": "none", "--ignore-nonprinting": "none", "-i": "none", "--month-sort": "none", "-M": "none", "--numeric-sort": "none", "-n": "none", "--random-sort": "none", "-R": "none", "--reverse": "none", "-r": "none", "--sort": "string", "--stable": "none", "-s": "none", "--unique": "none", "-u": "none", "--version-sort": "none", "-V": "none", "--zero-terminated": "none", "-z": "none", "--key": "string", "-k": "string", "--field-separator": "string", "-t": "string", "--check": "none", "-c": "none", "--check-char-order": "none", "-C": "none", "--merge": "none", "-m": "none", "--buffer-size": "string", "-S": "string", "--parallel": "number", "--batch-size": "number", "--help": "none", "--version": "none" } },
|
|
286
|
+
man: {
|
|
287
|
+
safeFlags: { "-a": "none", "--all": "none", "-d": "none", "-f": "none", "--whatis": "none", "-h": "none", "-k": "none", "--apropos": "none", "-w": "none", "-S": "string", "-s": "string" },
|
|
288
|
+
dangerous: (args) => {
|
|
289
|
+
const searchFlags = new Set(["-k", "-f", "--apropos", "--whatis"]);
|
|
290
|
+
const valuedFlags = new Set(["-S", "-s"]);
|
|
291
|
+
let searching = false;
|
|
292
|
+
for (const w of args) {
|
|
293
|
+
if (w === "--")
|
|
294
|
+
break;
|
|
295
|
+
if (!w.startsWith("-") || w === "-")
|
|
296
|
+
continue;
|
|
297
|
+
if (w.startsWith("--")) {
|
|
298
|
+
if (searchFlags.has(w))
|
|
299
|
+
searching = true;
|
|
300
|
+
}
|
|
301
|
+
else if (/[kf]/.test(w.slice(1)))
|
|
302
|
+
searching = true;
|
|
303
|
+
}
|
|
304
|
+
let searchSeen = false;
|
|
305
|
+
let pastDashes = false;
|
|
306
|
+
for (let i = 0; i < args.length; i++) {
|
|
307
|
+
const w = args[i];
|
|
308
|
+
if (!pastDashes && w === "--") {
|
|
309
|
+
pastDashes = true;
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
if (!(pastDashes || !w.startsWith("-") || w === "-")) {
|
|
313
|
+
if (searchFlags.has(w))
|
|
314
|
+
searchSeen = true;
|
|
315
|
+
else if (valuedFlags.has(w))
|
|
316
|
+
i++;
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
pastDashes = true;
|
|
320
|
+
if (searching && w.startsWith("-"))
|
|
321
|
+
return true;
|
|
322
|
+
if (!searchSeen && (w.includes("/") || w.includes("\\") || w.includes("~")))
|
|
323
|
+
return true;
|
|
324
|
+
}
|
|
325
|
+
return false;
|
|
326
|
+
},
|
|
327
|
+
},
|
|
328
|
+
help: { safeFlags: { "-d": "none" }, dangerous: (args) => args.some((w) => w.includes("/") || w.includes("\\") || w.includes("~")) },
|
|
329
|
+
netstat: { safeFlags: { "-a": "none", "-L": "none", "-l": "none", "-n": "none", "-f": "string", "-g": "none", "-i": "none", "-I": "string", "-s": "none", "-r": "none", "-m": "none", "-v": "none" } },
|
|
330
|
+
ps: {
|
|
331
|
+
safeFlags: { "-e": "none", "-A": "none", "-a": "none", "-d": "none", "-N": "none", "--deselect": "none", "-f": "none", "-F": "none", "-l": "none", "-j": "none", "-y": "none", "-w": "none", "-ww": "none", "--width": "number", "-c": "none", "-H": "none", "--forest": "none", "--headers": "none", "--no-headers": "none", "-n": "string", "--sort": "string", "-L": "none", "-T": "none", "-m": "none", "-C": "string", "-G": "string", "-g": "string", "-p": "string", "--pid": "string", "-q": "string", "--quick-pid": "string", "-s": "string", "--sid": "string", "-t": "string", "--tty": "string", "-U": "string", "-u": "string", "--user": "string", "--help": "none", "--info": "none", "-V": "none", "--version": "none" },
|
|
332
|
+
dangerous: (args) => args.some((w) => !w.startsWith("-") && /^[a-zA-Z]*e[a-zA-Z]*$/.test(w)),
|
|
333
|
+
},
|
|
334
|
+
base64: { respectsDoubleDash: false, safeFlags: { "-d": "none", "-D": "none", "--decode": "none", "-b": "number", "--break": "number", "-w": "number", "--wrap": "number", "-i": "string", "--input": "string", "--ignore-garbage": "none", "-h": "none", "--help": "none", "--version": "none" } },
|
|
335
|
+
grep: { safeFlags: GREP_FLAGS },
|
|
336
|
+
egrep: { safeFlags: GREP_FLAGS },
|
|
337
|
+
fgrep: { safeFlags: GREP_FLAGS },
|
|
338
|
+
rg: { safeFlags: { "-e": "string", "--regexp": "string", "-f": "string", "-i": "none", "--ignore-case": "none", "-S": "none", "--smart-case": "none", "-F": "none", "--fixed-strings": "none", "-w": "none", "--word-regexp": "none", "-v": "none", "--invert-match": "none", "-c": "none", "--count": "none", "-l": "none", "--files-with-matches": "none", "--files-without-match": "none", "-n": "none", "--line-number": "none", "-o": "none", "--only-matching": "none", "-A": "number", "--after-context": "number", "-B": "number", "--before-context": "number", "-C": "number", "--context": "number", "-H": "none", "-h": "none", "--heading": "none", "--no-heading": "none", "-q": "none", "--quiet": "none", "--column": "none", "-g": "string", "--glob": "string", "-t": "string", "--type": "string", "-T": "string", "--type-not": "string", "--type-list": "none", "--hidden": "none", "--no-ignore": "none", "-u": "none", "-m": "number", "--max-count": "number", "-d": "number", "--max-depth": "number", "-a": "none", "--text": "none", "-L": "none", "--follow": "none", "--color": "string", "--json": "none", "--stats": "none", "--help": "none", "--version": "none", "--debug": "none", "--": "none" } },
|
|
339
|
+
sha256sum: { safeFlags: CHECKSUM_FLAGS },
|
|
340
|
+
sha1sum: { safeFlags: CHECKSUM_FLAGS },
|
|
341
|
+
md5sum: { safeFlags: CHECKSUM_FLAGS },
|
|
342
|
+
tree: { safeFlags: { "-a": "none", "-d": "none", "-l": "none", "-f": "none", "-x": "none", "-L": "number", "-P": "string", "-I": "string", "--gitignore": "none", "--gitfile": "string", "--ignore-case": "none", "--matchdirs": "none", "--metafirst": "none", "--prune": "none", "--info": "none", "--infofile": "string", "--noreport": "none", "--charset": "string", "--filelimit": "number", "-q": "none", "-N": "none", "-Q": "none", "-p": "none", "-u": "none", "-g": "none", "-s": "none", "-h": "none", "--si": "none", "--du": "none", "-D": "none", "--timefmt": "string", "-F": "none", "--inodes": "none", "--device": "none", "-v": "none", "-t": "none", "-c": "none", "-U": "none", "-r": "none", "--dirsfirst": "none", "--filesfirst": "none", "--sort": "string", "-i": "none", "-A": "none", "-S": "none", "-n": "none", "-C": "none", "-X": "none", "-J": "none", "-H": "string", "--nolinks": "none", "--hintro": "string", "--houtro": "string", "-T": "string", "--hyperlink": "none", "--scheme": "string", "--authority": "string", "--fromfile": "none", "--fromtabfile": "none", "--fflinks": "none", "--help": "none", "--version": "none" } },
|
|
343
|
+
date: {
|
|
344
|
+
safeFlags: { "-d": "string", "--date": "string", "-r": "string", "--reference": "string", "-u": "none", "--utc": "none", "--universal": "none", "-I": "none", "--iso-8601": "string", "-R": "none", "--rfc-email": "none", "--rfc-3339": "string", "--debug": "none", "--help": "none", "--version": "none" },
|
|
345
|
+
dangerous: (args) => {
|
|
346
|
+
const valued = new Set(["-d", "--date", "-r", "--reference", "--rfc-3339"]);
|
|
347
|
+
let i = 0;
|
|
348
|
+
while (i < args.length) {
|
|
349
|
+
const w = args[i];
|
|
350
|
+
if (w.startsWith("--") && w.includes("="))
|
|
351
|
+
i++;
|
|
352
|
+
else if (w.startsWith("-"))
|
|
353
|
+
i += valued.has(w) ? 2 : 1;
|
|
354
|
+
else {
|
|
355
|
+
if (!w.startsWith("+"))
|
|
356
|
+
return true;
|
|
357
|
+
i++;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return false;
|
|
361
|
+
},
|
|
362
|
+
},
|
|
363
|
+
hostname: {
|
|
364
|
+
safeFlags: { "-f": "none", "--fqdn": "none", "--long": "none", "-s": "none", "--short": "none", "-i": "none", "--ip-address": "none", "-I": "none", "--all-ip-addresses": "none", "-a": "none", "--alias": "none", "-d": "none", "--domain": "none", "-A": "none", "--all-fqdns": "none", "-v": "none", "--verbose": "none", "-h": "none", "--help": "none", "-V": "none", "--version": "none" },
|
|
365
|
+
wordsShape: (args) => args.every((w) => /^-[a-zA-Z]$/.test(w) || /^--[a-zA-Z-]+$/.test(w)),
|
|
366
|
+
},
|
|
367
|
+
lsof: {
|
|
368
|
+
safeFlags: { "-?": "none", "-h": "none", "-v": "none", "-a": "none", "-b": "none", "-C": "none", "-l": "none", "-n": "none", "-N": "none", "-O": "none", "-P": "none", "-Q": "none", "-R": "none", "-t": "none", "-U": "none", "-V": "none", "-X": "none", "-H": "none", "-E": "none", "-F": "none", "-g": "none", "-i": "none", "-K": "none", "-L": "none", "-o": "none", "-r": "none", "-s": "none", "-S": "none", "-T": "none", "-x": "none", "-A": "string", "-c": "string", "-d": "string", "-e": "string", "-k": "string", "-p": "string", "-u": "string" },
|
|
369
|
+
dangerous: (args) => {
|
|
370
|
+
for (let i = 0; i < args.length; i++) {
|
|
371
|
+
const w = args[i];
|
|
372
|
+
if (w === "+m" || w.startsWith("+m"))
|
|
373
|
+
return true;
|
|
374
|
+
if (/^-[a-zA-Z]*i\S*@/.test(w) && /[a-zA-Z]/.test(before(w.slice(w.indexOf("@") + 1), ":")))
|
|
375
|
+
return true;
|
|
376
|
+
if (/^-[a-zA-Z]*i$/.test(w)) {
|
|
377
|
+
const next = args[i + 1] ?? "";
|
|
378
|
+
if (next.includes("@") && /[a-zA-Z]/.test(before(next.slice(next.indexOf("@") + 1), ":")))
|
|
379
|
+
return true;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
return false;
|
|
383
|
+
},
|
|
384
|
+
},
|
|
385
|
+
pgrep: { safeFlags: { "-d": "string", "--delimiter": "string", "-l": "none", "--list-name": "none", "-a": "none", "--list-full": "none", "-v": "none", "--inverse": "none", "-w": "none", "--lightweight": "none", "-c": "none", "--count": "none", "-f": "none", "--full": "none", "-g": "string", "--pgroup": "string", "-G": "string", "--group": "string", "-i": "none", "--ignore-case": "none", "-n": "none", "--newest": "none", "-o": "none", "--oldest": "none", "-O": "string", "--older": "string", "-P": "string", "--parent": "string", "-s": "string", "--session": "string", "-t": "string", "--terminal": "string", "-u": "string", "--euid": "string", "-U": "string", "--uid": "string", "-x": "none", "--exact": "none", "-F": "string", "--pidfile": "string", "-L": "none", "--logpidfile": "none", "-r": "string", "--runstates": "string", "--ns": "string", "--nslist": "string", "--help": "none", "-V": "none", "--version": "none" } },
|
|
386
|
+
tput: {
|
|
387
|
+
safeFlags: { "-T": "string", "-V": "none", "-x": "none" },
|
|
388
|
+
dangerous: (args) => {
|
|
389
|
+
const terminalWriting = new Set(["init", "reset", "rs1", "rs2", "rs3", "is1", "is2", "is3", "iprog", "if", "rf", "clear", "flash", "mc0", "mc4", "mc5", "mc5i", "mc5p", "pfkey", "pfloc", "pfx", "pfxl", "smcup", "rmcup"]);
|
|
390
|
+
let i = 0;
|
|
391
|
+
let pastDashes = false;
|
|
392
|
+
while (i < args.length) {
|
|
393
|
+
const w = args[i];
|
|
394
|
+
if (w === "--") {
|
|
395
|
+
pastDashes = true;
|
|
396
|
+
i++;
|
|
397
|
+
}
|
|
398
|
+
else if (!pastDashes && w.startsWith("-")) {
|
|
399
|
+
if (w === "-S")
|
|
400
|
+
return true;
|
|
401
|
+
if (!w.startsWith("--") && w.length > 2 && w.includes("S"))
|
|
402
|
+
return true;
|
|
403
|
+
i += w === "-T" ? 2 : 1;
|
|
404
|
+
}
|
|
405
|
+
else {
|
|
406
|
+
if (terminalWriting.has(w))
|
|
407
|
+
return true;
|
|
408
|
+
i++;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
return false;
|
|
412
|
+
},
|
|
413
|
+
},
|
|
414
|
+
ss: {
|
|
415
|
+
safeFlags: { "-h": "none", "--help": "none", "-V": "none", "--version": "none", "-n": "none", "--numeric": "none", "-a": "none", "--all": "none", "-l": "none", "--listening": "none", "-o": "none", "--options": "none", "-e": "none", "--extended": "none", "-m": "none", "--memory": "none", "-p": "none", "--processes": "none", "-i": "none", "--info": "none", "-s": "none", "--summary": "none", "-4": "none", "--ipv4": "none", "-6": "none", "--ipv6": "none", "-0": "none", "--packet": "none", "-t": "none", "--tcp": "none", "-M": "none", "--mptcp": "none", "-S": "none", "--sctp": "none", "-u": "none", "--udp": "none", "-d": "none", "--dccp": "none", "-w": "none", "--raw": "none", "-x": "none", "--unix": "none", "--tipc": "none", "--vsock": "none", "-f": "string", "--family": "string", "-A": "string", "--query": "string", "--socket": "string", "-Z": "none", "--context": "none", "-z": "none", "--contexts": "none", "-b": "none", "--bpf": "none", "-E": "none", "--events": "none", "-H": "none", "--no-header": "none", "-O": "none", "--oneline": "none", "--tipcinfo": "none", "--tos": "none", "--cgroup": "none", "--inet-sockopt": "none" },
|
|
416
|
+
dangerous: (args) => {
|
|
417
|
+
const keyword = /^(dst|src|dport|sport|and|or|not|eq|ne|ge|le|gt|lt|autobound|state|exclude|dev|fwmark|cgroup)$/;
|
|
418
|
+
const takesOperand = /^(state|exclude|dport|sport|dev|fwmark|cgroup)$/;
|
|
419
|
+
const valuedFlag = /^(-f|--family|-A|--query|--socket)$/;
|
|
420
|
+
const filter = [];
|
|
421
|
+
let pastDashes = false;
|
|
422
|
+
for (let i = 0; i < args.length; i++) {
|
|
423
|
+
const w = args[i];
|
|
424
|
+
if (!pastDashes && w === "--") {
|
|
425
|
+
pastDashes = true;
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
if (!pastDashes && w.startsWith("-")) {
|
|
429
|
+
if (valuedFlag.test(w))
|
|
430
|
+
i++;
|
|
431
|
+
continue;
|
|
432
|
+
}
|
|
433
|
+
filter.push(w);
|
|
434
|
+
}
|
|
435
|
+
const tokens = filter.join(" ").split(/[\s()=!<>&|,]+/).filter(Boolean);
|
|
436
|
+
let skipOperand = false;
|
|
437
|
+
for (const token of tokens) {
|
|
438
|
+
if (skipOperand) {
|
|
439
|
+
skipOperand = false;
|
|
440
|
+
continue;
|
|
441
|
+
}
|
|
442
|
+
if (keyword.test(token)) {
|
|
443
|
+
skipOperand = takesOperand.test(token);
|
|
444
|
+
continue;
|
|
445
|
+
}
|
|
446
|
+
if (/[g-zG-Z]/.test(token) || (/[a-fA-F]/.test(token) && (token.includes(".") || !token.includes(":"))))
|
|
447
|
+
return true;
|
|
448
|
+
}
|
|
449
|
+
return false;
|
|
450
|
+
},
|
|
451
|
+
},
|
|
452
|
+
fd: { safeFlags: FD_FLAGS },
|
|
453
|
+
fdfind: { safeFlags: FD_FLAGS },
|
|
454
|
+
pyright: { respectsDoubleDash: false, safeFlags: { "--outputjson": "none", "--pythonversion": "string", "--pythonplatform": "string", "--level": "string", "--stats": "none", "--verbose": "none", "--version": "none", "--dependencies": "none", "--warnings": "none" }, dangerous: (args) => args.some((w) => w === "--watch" || w === "-w") },
|
|
455
|
+
test: {
|
|
456
|
+
respectsDoubleDash: false,
|
|
457
|
+
safeFlags: { "-b": "string", "-c": "string", "-d": "string", "-e": "string", "-f": "string", "-g": "string", "-G": "string", "-h": "string", "-k": "string", "-L": "string", "-N": "string", "-O": "string", "-p": "string", "-r": "string", "-s": "string", "-S": "string", "-t": "string", "-u": "string", "-w": "string", "-x": "string", "-z": "string", "-n": "string", "-eq": "string", "-ne": "string", "-lt": "string", "-le": "string", "-gt": "string", "-ge": "string", "-nt": "string", "-ot": "string", "-ef": "string" },
|
|
458
|
+
dangerous: (args) => {
|
|
459
|
+
if (args.some((w) => w === "-v" || w === "-R" || w === "-a" || w === "-o" || /\[/.test(w)))
|
|
460
|
+
return true;
|
|
461
|
+
for (let i = 0; i < args.length; i++) {
|
|
462
|
+
const w = args[i];
|
|
463
|
+
if (TEST_INTEGER_OPERATORS.has(w))
|
|
464
|
+
for (const side of [args[i - 1], args[i + 1]])
|
|
465
|
+
if (side !== undefined && !TEST_INTEGER.test(side))
|
|
466
|
+
return true;
|
|
467
|
+
if (w === "-t") {
|
|
468
|
+
const fd = args[i + 1];
|
|
469
|
+
if (fd !== undefined && !TEST_INTEGER.test(fd))
|
|
470
|
+
return true;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
return false;
|
|
474
|
+
},
|
|
475
|
+
},
|
|
476
|
+
};
|
|
477
|
+
export const READ_ONLY_BARE_PROGRAMS = ["docker ps", "docker images", "cal", "uptime", "cat", "head", "tail", "wc", "stat", "strings", "hexdump", "od", "nl", "id", "uname", "free", "df", "du", "locale", "groups", "nproc", "basename", "dirname", "realpath", "cut", "paste", "tr", "column", "tac", "rev", "fold", "expand", "unexpand", "fmt", "comm", "cmp", "numfmt", "readlink", "diff", "true", "false", "sleep", "which", "type", "expr", "seq", "tsort", "pr"];
|
|
478
|
+
export const READ_ONLY_GLOB_PROGRAMS = ["ls", "cat", "head", "tail", "wc", "stat", "grep", "egrep", "fgrep", "diff", "du", "df", "echo", "strings", "hexdump", "od", "nl", "cut", "column", "tr", "tac", "rev", "cmp", "basename", "dirname", "realpath", "readlink", "sha256sum", "sha1sum", "md5sum", "cd"];
|
|
479
|
+
export const READ_ONLY_EXACT_FORMS = [["claude", "-h"], ["claude", "--help"], ["node", "-v"], ["node", "--version"], ["python", "--version"], ["python3", "--version"], ["ip", "addr"]];
|
|
480
|
+
export const READ_ONLY_BARE_ONLY = ["pwd", "whoami", "alias"];
|
|
481
|
+
export const FIND_ACTION_PRIMARIES = ["-delete", "-exec", "-execdir", "-ok", "-okdir", "-fprint", "-fprint0", "-fls", "-fprintf", "-files0-from"];
|
|
482
|
+
export const FIND_VALUE_PRIMARIES = ["-name", "-iname", "-path", "-ipath", "-lname", "-ilname", "-regex", "-iregex", "-wholename", "-iwholename", "-samefile", "-newer", "-anewer", "-cnewer", "-mnewer", "-perm", "-user", "-group", "-uid", "-gid", "-size", "-type", "-xtype", "-fstype", "-inum", "-links", "-used", "-context", "-amin", "-cmin", "-mmin", "-atime", "-ctime", "-mtime", "-mindepth", "-maxdepth", "-printf", "-regextype", "-D", "-f", "-flags", "-Bnewer", "-Btime", "-Bmin", "-files0-from", "-xattrname"];
|
|
483
|
+
export const FIND_NEWER_PRIMARY = /^-newer[aBcm][aBcmt]$/;
|
|
484
|
+
export const READ_ONLY_ENV_NAMES = ["GOEXPERIMENT", "GOOS", "GOARCH", "CGO_ENABLED", "GO111MODULE", "RUST_BACKTRACE", "RUST_LOG", "NODE_ENV", "PYTHONUNBUFFERED", "PYTHONDONTWRITEBYTECODE", "PYTEST_DISABLE_PLUGIN_AUTOLOAD", "PYTEST_DEBUG", "ANTHROPIC_API_KEY", "LANG", "LANGUAGE", "LC_ALL", "LC_CTYPE", "LC_TIME", "CHARSET", "TERM", "COLORTERM", "NO_COLOR", "FORCE_COLOR", "TZ", "LS_COLORS", "LSCOLORS", "GREP_COLOR", "GREP_COLORS", "GCC_COLORS", "TIME_STYLE", "BLOCK_SIZE", "BLOCKSIZE", "COLUMNS", "LINES", "CLICOLOR", "CLICOLOR_FORCE", "CI", "DEBIAN_FRONTEND", "GIT_TERMINAL_PROMPT"];
|
|
485
|
+
export const XARGS_READ_ONLY_TARGETS = ["echo", "printf", "wc", "grep", "egrep", "fgrep", "head", "tail"];
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The READ-ONLY shell reader — "does this command line provably only READ?" — the allow-layer member
|
|
3
|
+
* the tool gate consults for the shell tool after every tightening lane and after the person's own allow
|
|
4
|
+
* rules, and before the classifier or a person is asked (`gate-lanes.ts`). Upstream (CC 2.1.250) has the
|
|
5
|
+
* same station: its shell tool's `isReadOnly` runs the command through a parser, splits it into its
|
|
6
|
+
* simple commands, and admits the whole line only when EVERY simple command is read-only by its tables;
|
|
7
|
+
* this module is that reading over this engine's own lexer ({@link readShellCommand}, the tightening
|
|
8
|
+
* reader's) and the transcribed tables (`read-only-shell-table.ts`).
|
|
9
|
+
*
|
|
10
|
+
* The reading itself is the contract on {@link readOnlyShellVerdict}.
|
|
11
|
+
*
|
|
12
|
+
* What the verdict is NOT: a security boundary of its own. The deny/ask lanes run before it and win; the
|
|
13
|
+
* mandated asks (an operator's per-call shell confirmation, a tool's own egress/irreversibility marks, an
|
|
14
|
+
* explicit ask rule, a hook's ask, governance) are never cleared by it — the gate's allow-layer predicate
|
|
15
|
+
* decides where it may speak, exactly as it decides for a person's stored allow rule.
|
|
16
|
+
*/
|
|
17
|
+
import { type ShellCommandShape } from "./shell-lexer.js";
|
|
18
|
+
/** The reader's answer: read-only, or not — with the first reason the reading stopped on (a sentence a
|
|
19
|
+
* trace or a card can show; never adjudication input). */
|
|
20
|
+
export type ReadOnlyShellVerdict = {
|
|
21
|
+
readonly readOnly: true;
|
|
22
|
+
} | {
|
|
23
|
+
readonly readOnly: false;
|
|
24
|
+
readonly reason: string;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Read `command` as the allow layer must: read-only, or the first reason it is not. A shape already read by
|
|
28
|
+
* the caller may be handed in (the gate reads every shell command once).
|
|
29
|
+
*
|
|
30
|
+
* @contract read_only_shell.every_segment — a command line is read-only iff the lexer reads it whole (no
|
|
31
|
+
* unreadable segment, no group/subshell/control structure, no background `&`), no word anywhere carries a
|
|
32
|
+
* variable, arithmetic or command substitution, and EVERY segment is read-only on its own: its
|
|
33
|
+
* redirections only read or discard (`<` family, `/dev/null`, a descriptor duplication), its leading
|
|
34
|
+
* assignments name only variables from {@link READ_ONLY_ENV_NAMES} with literal values, and its program
|
|
35
|
+
* with its arguments is admitted by exactly one of three readings in order — a segment carrying a GLOB is
|
|
36
|
+
* admitted only under {@link READ_ONLY_GLOB_PROGRAMS}; else the structural reading (the bare-program set,
|
|
37
|
+
* the exact forms, `echo`/`ls`/`cd`/`find`/`printf`/`history`/`arch`/`ifconfig`); else the flag-vetted
|
|
38
|
+
* table with its per-row danger predicates, and the `uniq`/`jq` forms. A `cd` beside a `git` in one line
|
|
39
|
+
* is never read-only (the git command's repository is then not the session's). Anything the reading does
|
|
40
|
+
* not positively admit is NOT read-only — the answer is never a guess, and a `false` costs one question.
|
|
41
|
+
*/
|
|
42
|
+
export declare function readOnlyShellVerdict(command: string, shape?: ShellCommandShape): ReadOnlyShellVerdict;
|