fix(exec): detect combined env split carriers · openclaw/openclaw@e2f4aa4
vincentkoc
·
2026-05-04
·
via Recent Commits to openclaw:main
| Original file line number | Diff line number | Diff line change |
|---|
@@ -50,6 +50,7 @@ Docs: https://docs.openclaw.ai
|
50 | 50 | |
51 | 51 | ### Fixes |
52 | 52 | |
| 53 | +- Exec approvals: detect `env -S` split-string command-carrier risks when `-S`/`-s` is combined with other env short options, so approval explanations do not miss split payloads hidden behind `env -iS...`. Thanks @vincentkoc. |
53 | 54 | - Voice Call: mark realtime calls completed when the realtime provider closes normally, so Twilio/OpenAI/Google realtime stop events do not leave active call records behind. Thanks @vincentkoc. |
54 | 55 | - Exec approvals: treat POSIX `exec` as a command carrier for inline eval, shell-wrapper, and eval/source detection, so approval explanations and command-risk checks do not miss payloads hidden behind `exec`. Thanks @vincentkoc. |
55 | 56 | - Google Meet: log the resolved audio provider model when starting Chrome and paired-node Meet talk-back bridges, so agent-mode joins show the STT model and bidi joins show the realtime voice model. |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -91,8 +91,12 @@ describe("command-analysis risks", () => {
|
91 | 91 | it("detects env split-string flag forms", () => { |
92 | 92 | expect(detectEnvSplitStringFlag(["env", "-S", "sh -c id"])).toBe("-S"); |
93 | 93 | expect(detectEnvSplitStringFlag(["env", "-Ssh -c id"])).toBe("-S"); |
| 94 | +expect(detectEnvSplitStringFlag(["env", "-iS", "sh -c id"])).toBe("-S"); |
| 95 | +expect(detectEnvSplitStringFlag(["env", "-iSsh -c id"])).toBe("-S"); |
| 96 | +expect(detectEnvSplitStringFlag(["env", "-is", "sh -c id"])).toBe("-s"); |
94 | 97 | expect(detectEnvSplitStringFlag(["env", "--split-string=sh -c id"])).toBe("--split-string"); |
95 | 98 | expect(detectEnvSplitStringFlag(["env", "sh", "-c", "id"])).toBeNull(); |
| 99 | +expect(detectEnvSplitStringFlag(["env", "-XSsh -c id"])).toBeNull(); |
96 | 100 | }); |
97 | 101 | |
98 | 102 | it("detects shell wrappers carried through prefix commands", () => { |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -2,6 +2,7 @@ import { splitShellArgs } from "../../utils/shell-argv.js";
|
2 | 2 | import { |
3 | 3 | COMMAND_CARRIER_EXECUTABLES, |
4 | 4 | isEnvAssignmentToken, |
| 5 | +parseEnvInvocationPrelude, |
5 | 6 | resolveCarrierCommandArgv, |
6 | 7 | SOURCE_EXECUTABLES, |
7 | 8 | } from "../command-carriers.js"; |
@@ -169,14 +170,31 @@ export function detectEnvSplitStringFlag(argv: string[]): string | null {
|
169 | 170 | if (normalizeExecutableToken(argv[0] ?? "") !== "env") { |
170 | 171 | return null; |
171 | 172 | } |
172 | | -for (const arg of argv.slice(1)) { |
| 173 | +const parsed = parseEnvInvocationPrelude(argv); |
| 174 | +if (!parsed?.splitArgv) { |
| 175 | +return null; |
| 176 | +} |
| 177 | +for (const arg of argv.slice(1, parsed.commandIndex)) { |
173 | 178 | const token = arg.trim(); |
174 | | -if (token === "-S" || token === "--split-string") { |
| 179 | +if (token === "-S" || token === "-s") { |
175 | 180 | return token; |
176 | 181 | } |
| 182 | +if (token === "--split-string") { |
| 183 | +return "--split-string"; |
| 184 | +} |
177 | 185 | if (token.startsWith("--split-string=") || (token.startsWith("-S") && token.length > 2)) { |
178 | 186 | return token.startsWith("--") ? "--split-string" : "-S"; |
179 | 187 | } |
| 188 | +if (token.startsWith("-") && !token.startsWith("--")) { |
| 189 | +for (const option of token.slice(1)) { |
| 190 | +if (option === "S") { |
| 191 | +return "-S"; |
| 192 | +} |
| 193 | +if (option === "s") { |
| 194 | +return "-s"; |
| 195 | +} |
| 196 | +} |
| 197 | +} |
180 | 198 | } |
181 | 199 | return null; |
182 | 200 | } |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -602,6 +602,10 @@ describe("command explainer tree-sitter runtime", () => {
|
602 | 602 | expect(envSplitString.risks).toContainEqual( |
603 | 603 | expect.objectContaining({ kind: "command-carrier", command: "env", flag: "-S" }), |
604 | 604 | ); |
| 605 | +const envCombinedSplitString = await explainShellCommand("env -iS 'sh -c \"id\"'"); |
| 606 | +expect(envCombinedSplitString.risks).toContainEqual( |
| 607 | +expect.objectContaining({ kind: "command-carrier", command: "env", flag: "-S" }), |
| 608 | +); |
605 | 609 | |
606 | 610 | for (const command of [ |
607 | 611 | 'env python -c "print(1)"', |
|
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。