惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

B
Blog RSS Feed
B
Blog
N
Netflix TechBlog - Medium
量子位
月光博客
月光博客
博客园_首页
博客园 - Franky
酷 壳 – CoolShell
酷 壳 – CoolShell
Last Week in AI
Last Week in AI
T
The Blog of Author Tim Ferriss
Hugging Face - Blog
Hugging Face - Blog
雷峰网
雷峰网
M
MIT News - Artificial intelligence
J
Java Code Geeks
大猫的无限游戏
大猫的无限游戏
D
DataBreaches.Net
腾讯CDC
Engineering at Meta
Engineering at Meta
云风的 BLOG
云风的 BLOG
L
LangChain Blog
GbyAI
GbyAI
IT之家
IT之家
Y
Y Combinator Blog
人人都是产品经理
人人都是产品经理

Recent Commits to openclaw:main

test: merge chat side-result checks · openclaw/openclaw@ddd2c2a test: merge cron history checks · openclaw/openclaw@f7eb746 test: merge responsive navigation shell checks · openclaw/openclaw@c2e4b47 docs(changelog): add codex oauth fixes · openclaw/openclaw@628e6cd test: merge navigation routing cases · openclaw/openclaw@5d8cecb Tests: mock channel registry bundled fallback · openclaw/openclaw@2b08233 Secrets: avoid broad web search discovery for single plugin config · openclaw/openclaw@a464f59 test: merge config view browser checks · openclaw/openclaw@20cf511 fix(status): align oauth health with runtime · openclaw/openclaw@eed7116 feat: add macOS screen snapshots for monitor preview (#67954) thanks … · openclaw/openclaw@f377db1 fix: report shared auth scopes in hello-ok (#67810) thanks @BunsDev · openclaw/openclaw@0b6c39b Auto-reply: avoid eager bundled route fallback · openclaw/openclaw@3ea1bf4 Tests: narrow session binding contract setup · openclaw/openclaw@54e4e16 fix(macOS): enable undo/redo in webchat composer text input (#34962) · openclaw/openclaw@00951dc Tests: speed up channel setup promotion · openclaw/openclaw@82b529a Docs: refresh agent instructions · openclaw/openclaw@5775fe2 fix(auth): serialize OAuth refresh across agents to fix #26322 (#67876) · openclaw/openclaw@8e79080 test: allow ollama public surface boundary test · openclaw/openclaw@7d4f1a6 Docs: add test performance guardrails · openclaw/openclaw@89706d3 Tests: restore context-engine usage proof · openclaw/openclaw@e4c4f95 Tests: slim context engine runtime coverage · openclaw/openclaw@74c198f ci: retry failed custom checkouts · openclaw/openclaw@0ee5baf test: trim duplicate provider auth onboarding cases · openclaw/openclaw@1ffc02e matrix: fix sessions_spawn --thread subagent session spawning (#67643) · openclaw/openclaw@1ce2596 test: reduce auth choice fixture churn · openclaw/openclaw@857b9cd test: mock health status config boundaries · openclaw/openclaw@9d5ab4a test: mock onboard config io boundary · openclaw/openclaw@299694d test: mock legacy state plugin boundaries · openclaw/openclaw@2713089 test: mock channel install boundaries · openclaw/openclaw@b945248 test: mock doctor preview channel boundaries · openclaw/openclaw@b1a3ad4
refactor: share command payload extraction · openclaw/ope...
steipete · 2026-05-04 · via Recent Commits to openclaw:main

@@ -1,5 +1,6 @@

11

import path from "node:path";

22

import type { AgentToolResult } from "@mariozechner/pi-agent-core";

3+

import { buildCommandPayloadCandidates } from "../infra/command-analysis/risks.js";

34

import { analyzeShellCommand } from "../infra/exec-approvals-analysis.js";

45

import {

56

type ExecAsk,

@@ -16,7 +17,6 @@ import {

1617

getShellPathFromLoginShell,

1718

resolveShellEnvFallbackTimeoutMs,

1819

} from "../infra/shell-env.js";

19-

import { extractShellWrapperInlineCommand } from "../infra/shell-wrapper-resolution.js";

2020

import { logInfo } from "../logger.js";

2121

import { parseAgentSessionKey, resolveAgentIdFromSessionKey } from "../routing/session-key.js";

2222

import { createLazyImportLoader } from "../shared/lazy-promise.js";

@@ -1140,207 +1140,17 @@ function parseOpenClawChannelsLoginShellCommand(raw: string): boolean {

11401140

}

1141114111421142

function rejectUnsafeControlShellCommand(command: string): void {

1143-

const isEnvAssignmentToken = (token: string): boolean =>

1144-

/^[A-Za-z_][A-Za-z0-9_]*=.*$/u.test(token);

1145-

const commandStandaloneOptions = new Set(["-p", "-v", "-V"]);

1146-

const envOptionsWithValues = new Set([

1147-

"-C",

1148-

"-S",

1149-

"-u",

1150-

"--argv0",

1151-

"--block-signal",

1152-

"--chdir",

1153-

"--default-signal",

1154-

"--ignore-signal",

1155-

"--split-string",

1156-

"--unset",

1157-

]);

1158-

const execOptionsWithValues = new Set(["-a"]);

1159-

const execStandaloneOptions = new Set(["-c", "-l"]);

1160-

const sudoOptionsWithValues = new Set([

1161-

"-C",

1162-

"-D",

1163-

"-g",

1164-

"-p",

1165-

"-R",

1166-

"-T",

1167-

"-U",

1168-

"-u",

1169-

"--chdir",

1170-

"--close-from",

1171-

"--group",

1172-

"--host",

1173-

"--other-user",

1174-

"--prompt",

1175-

"--role",

1176-

"--type",

1177-

"--user",

1178-

]);

1179-

const sudoStandaloneOptions = new Set(["-A", "-E", "--askpass", "--preserve-env"]);

1180-

const extractEnvSplitStringPayload = (argv: string[]): string[] => {

1181-

const remaining = [...argv];

1182-

while (remaining[0] && isEnvAssignmentToken(remaining[0])) {

1183-

remaining.shift();

1184-

}

1185-

if (remaining[0] !== "env") {

1186-

return [];

1187-

}

1188-

remaining.shift();

1189-

const payloads: string[] = [];

1190-

while (remaining.length > 0) {

1191-

while (remaining[0] && isEnvAssignmentToken(remaining[0])) {

1192-

remaining.shift();

1193-

}

1194-

const token: string | undefined = remaining[0];

1195-

if (!token) {

1196-

break;

1197-

}

1198-

if (token === "--") {

1199-

remaining.shift();

1200-

continue;

1201-

}

1202-

if (!token.startsWith("-") || token === "-") {

1203-

break;

1204-

}

1205-

const option = remaining.shift()!;

1206-

const normalized = option.split("=", 1)[0];

1207-

if (normalized === "-S" || normalized === "--split-string") {

1208-

const value = option.includes("=")

1209-

? option.slice(option.indexOf("=") + 1)

1210-

: remaining.shift();

1211-

if (value?.trim()) {

1212-

payloads.push(value);

1213-

}

1214-

continue;

1215-

}

1216-

if (envOptionsWithValues.has(normalized) && !option.includes("=") && remaining[0]) {

1217-

remaining.shift();

1218-

}

1219-

}

1220-

return payloads;

1221-

};

1222-

const stripApprovalCommandPrefixes = (argv: string[]): string[] => {

1223-

const remaining = [...argv];

1224-

while (remaining.length > 0) {

1225-

while (remaining[0] && isEnvAssignmentToken(remaining[0])) {

1226-

remaining.shift();

1227-

}

1228-1229-

const token = remaining[0];

1230-

if (!token) {

1231-

break;

1232-

}

1233-

if (token === "--") {

1234-

remaining.shift();

1235-

continue;

1236-

}

1237-

if (token === "env") {

1238-

remaining.shift();

1239-

while (remaining.length > 0) {

1240-

while (remaining[0] && isEnvAssignmentToken(remaining[0])) {

1241-

remaining.shift();

1242-

}

1243-

const envToken = remaining[0];

1244-

if (!envToken) {

1245-

break;

1246-

}

1247-

if (envToken === "--") {

1248-

remaining.shift();

1249-

continue;

1250-

}

1251-

if (!envToken.startsWith("-") || envToken === "-") {

1252-

break;

1253-

}

1254-

const option = remaining.shift()!;

1255-

const normalized = option.split("=", 1)[0];

1256-

if (envOptionsWithValues.has(normalized) && !option.includes("=") && remaining[0]) {

1257-

remaining.shift();

1258-

}

1259-

}

1260-

continue;

1261-

}

1262-

if (token === "command" || token === "builtin") {

1263-

remaining.shift();

1264-

while (remaining[0]?.startsWith("-")) {

1265-

const option = remaining.shift()!;

1266-

if (option === "--") {

1267-

break;

1268-

}

1269-

if (!commandStandaloneOptions.has(option.split("=", 1)[0])) {

1270-

continue;

1271-

}

1272-

}

1273-

continue;

1274-

}

1275-

if (token === "exec") {

1276-

remaining.shift();

1277-

while (remaining[0]?.startsWith("-")) {

1278-

const option = remaining.shift()!;

1279-

if (option === "--") {

1280-

break;

1281-

}

1282-

const normalized = option.split("=", 1)[0];

1283-

if (execStandaloneOptions.has(normalized)) {

1284-

continue;

1285-

}

1286-

if (execOptionsWithValues.has(normalized) && !option.includes("=") && remaining[0]) {

1287-

remaining.shift();

1288-

}

1289-

}

1290-

continue;

1291-

}

1292-

if (token === "sudo") {

1293-

remaining.shift();

1294-

while (remaining[0]?.startsWith("-")) {

1295-

const option = remaining.shift()!;

1296-

if (option === "--") {

1297-

break;

1298-

}

1299-

const normalized = option.split("=", 1)[0];

1300-

if (sudoStandaloneOptions.has(normalized)) {

1301-

continue;

1302-

}

1303-

if (sudoOptionsWithValues.has(normalized) && !option.includes("=") && remaining[0]) {

1304-

remaining.shift();

1305-

}

1306-

}

1307-

continue;

1308-

}

1309-

break;

1310-

}

1311-

return remaining;

1312-

};

1313-

const buildCandidates = (argv: string[]): string[] => {

1314-

const envSplitCandidates = extractEnvSplitStringPayload(argv).flatMap((payload) => {

1315-

const innerArgv = splitShellArgs(payload);

1316-

return innerArgv ? buildCandidates(innerArgv) : [payload];

1317-

});

1318-

const stripped = stripApprovalCommandPrefixes(argv);

1319-

const shellWrapperPayload = extractShellWrapperInlineCommand(stripped);

1320-

const shellWrapperCandidates = shellWrapperPayload

1321-

? (() => {

1322-

const innerArgv = splitShellArgs(shellWrapperPayload);

1323-

return innerArgv ? buildCandidates(innerArgv) : [shellWrapperPayload];

1324-

})()

1325-

: [];

1326-

return [

1327-

...(stripped.length > 0 ? [stripped.join(" ")] : []),

1328-

...envSplitCandidates,

1329-

...shellWrapperCandidates,

1330-

];

1331-

};

1332-13331143

const rawCommand = command.trim();

13341144

const analysis = analyzeShellCommand({ command: rawCommand });

13351145

const candidates = analysis.ok

1336-

? analysis.segments.flatMap((segment) => buildCandidates(segment.argv))

1146+

? analysis.segments.flatMap((segment) => buildCommandPayloadCandidates(segment.argv))

13371147

: rawCommand

13381148

.split(/\r?\n/)

13391149

.map((line) => line.trim())

13401150

.filter(Boolean)

13411151

.flatMap((line) => {

13421152

const argv = splitShellArgs(line);

1343-

return argv ? buildCandidates(argv) : [line];

1153+

return argv ? buildCommandPayloadCandidates(argv) : [line];

13441154

});

13451155

for (const candidate of candidates) {

13461156

if (parseExecApprovalShellCommand(candidate)) {