@sema-agent/core 7.9.2 → 7.11.0

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 (86) hide show
  1. package/CHANGELOG.md +102 -0
  2. package/dist/agents/child-model-seat.d.ts +71 -14
  3. package/dist/agents/child-model-seat.js +23 -7
  4. package/dist/agents/subagent.d.ts +2 -2
  5. package/dist/agents/subagent.js +8 -5
  6. package/dist/agents/teacher.js +2 -2
  7. package/dist/core/ask-unresolvable-notice.d.ts +52 -0
  8. package/dist/core/ask-unresolvable-notice.js +25 -0
  9. package/dist/core/auto-mode-defaults.d.ts +15 -3
  10. package/dist/core/auto-mode-defaults.js +1 -0
  11. package/dist/core/auto-mode.d.ts +86 -23
  12. package/dist/core/auto-mode.js +43 -12
  13. package/dist/core/checkpoint-store.d.ts +14 -0
  14. package/dist/core/checkpoint-store.js +2 -1
  15. package/dist/core/engine-notice.d.ts +28 -7
  16. package/dist/core/gate-fold.js +1 -0
  17. package/dist/core/gate-lanes.d.ts +6 -1
  18. package/dist/core/gate-lanes.js +60 -18
  19. package/dist/core/governance-codes.d.ts +1 -1
  20. package/dist/core/governance-codes.js +4 -0
  21. package/dist/core/hooks.d.ts +37 -1
  22. package/dist/core/hooks.js +2 -0
  23. package/dist/core/permission-rule-model.d.ts +53 -16
  24. package/dist/core/permission-rule-model.js +59 -21
  25. package/dist/core/permission-rules.d.ts +6 -4
  26. package/dist/core/permission-rules.js +14 -14
  27. package/dist/core/persisted-rule-arms.js +4 -3
  28. package/dist/core/read-only-shell-table.d.ts +87 -0
  29. package/dist/core/read-only-shell-table.js +485 -0
  30. package/dist/core/read-only-shell.d.ts +42 -0
  31. package/dist/core/read-only-shell.js +316 -0
  32. package/dist/core/roles.d.ts +3 -2
  33. package/dist/core/runner/contracts.d.ts +26 -2
  34. package/dist/core/runner/denial-limit-arms.d.ts +14 -3
  35. package/dist/core/runner/denial-limit-arms.js +15 -5
  36. package/dist/core/runner/gate-exit.d.ts +5 -0
  37. package/dist/core/runner/permission-rule-lanes.d.ts +7 -1
  38. package/dist/core/runner/permission-rule-lanes.js +9 -3
  39. package/dist/core/runner/prepare-caps-and-workflow.d.ts +1 -1
  40. package/dist/core/runner/prepare-caps-and-workflow.js +35 -14
  41. package/dist/core/runner/prepare-gate-stations.d.ts +3 -2
  42. package/dist/core/runner/prepare-gate-stations.js +12 -0
  43. package/dist/core/runner/prepare-policy-chain.js +7 -6
  44. package/dist/core/runner/prepare-task.js +1 -1
  45. package/dist/core/runner/prepare-turn-wiring.js +1 -1
  46. package/dist/core/runner/prepare-wiring-manifest.d.ts +1 -1
  47. package/dist/core/runner/prepare-wiring-manifest.js +8 -1
  48. package/dist/core/runner/runtask.d.ts +33 -32
  49. package/dist/core/runner/runtask.js +63 -33
  50. package/dist/core/runner-deps.d.ts +9 -2
  51. package/dist/core/shell-lexer.d.ts +18 -0
  52. package/dist/core/shell-lexer.js +17 -10
  53. package/dist/core/shell-wrapper-table.js +8 -5
  54. package/dist/core/swappable-deps.d.ts +90 -0
  55. package/dist/core/swappable-deps.js +55 -0
  56. package/dist/core/tool-policy.d.ts +30 -1
  57. package/dist/core/tool-policy.js +6 -2
  58. package/dist/core/tools.d.ts +28 -7
  59. package/dist/core/tools.js +44 -4
  60. package/dist/core/trace.d.ts +15 -0
  61. package/dist/core/wiring-manifest.d.ts +15 -1
  62. package/dist/core/wiring-manifest.js +10 -2
  63. package/dist/core/workflow-journal-store.d.ts +21 -2
  64. package/dist/core/workflow-journal-store.js +1 -1
  65. package/dist/engine/execution-env/node-execution-env.d.ts +2 -0
  66. package/dist/engine/execution-env/node-execution-env.js +2 -1
  67. package/dist/engine/harness/agent-harness.d.ts +3 -1
  68. package/dist/engine/harness/agent-harness.js +1 -1
  69. package/dist/engine/harness/types.d.ts +15 -2
  70. package/dist/index.d.ts +8 -4
  71. package/dist/index.js +7 -3
  72. package/dist/orchestration/run-workflow-tool.d.ts +22 -2
  73. package/dist/orchestration/run-workflow-tool.js +14 -1
  74. package/dist/orchestration/workflow-governance.d.ts +3 -2
  75. package/dist/orchestration/workflow-observe.d.ts +1 -1
  76. package/dist/orchestration/workflow-observe.js +2 -0
  77. package/dist/orchestration/workflow-primitives.d.ts +4 -1
  78. package/dist/orchestration/workflow-primitives.js +1 -6
  79. package/dist/orchestration/workflow-types.d.ts +37 -2
  80. package/dist/orchestration/workflow-types.js +16 -0
  81. package/dist/orchestration/workflow.d.ts +53 -6
  82. package/dist/orchestration/workflow.js +319 -51
  83. package/dist/prompt-assembly/turn-snapshot.d.ts +4 -2
  84. package/dist/stores/file/workflow-journal-store.js +10 -3
  85. package/package.json +1 -1
  86. package/test/export-surface.snapshot.json +85 -5
@@ -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;