Files
openclaw/src/agents/code-mode-shell-source.test.ts
Sasan 7599bbe636 fix(agents): stop code-mode shell-command retry storms (#113070)
Reject recognizable POSIX and Windows shell source before QuickJS execution while preserving valid JavaScript, TypeScript, syntax errors, standard globals, and real hoisted bindings. Add real-worker regression coverage and adversarial cross-platform stress proof. Fixes #113069.
2026-07-27 08:10:37 -04:00

217 lines
6.9 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
CODE_MODE_SHELL_SOURCE_ERROR,
isShellLikeCodeModeSource,
} from "./code-mode-shell-source.js";
describe("isShellLikeCodeModeSource", () => {
it.each([
"ls",
"ls -1",
"ls -la /workspace/",
"ls -la /workspace",
"ls /var/log",
"pwd",
"pwd;",
"pwd; // inspect the workspace",
"# inspect the workspace\npwd",
"# inspect the workspace\n# then report it\npwd",
"#!/bin/sh\npwd",
"#!/usr/bin/env bash\nset -euo pipefail\npwd",
"pwd\nls -la /workspace",
"pwd;ls -la /workspace",
"pwd && ls /workspace",
"pwd||ls /workspace",
"echo hello",
"cat /workspace/HEARTBEAT.md",
"find /workspace -maxdepth 1 -type f | head -50",
"echo listing; ls /workspace/ 2>&1 || echo failed",
"ls /workspace/ > /tmp/wlist.txt 2>&1; cat /tmp/wlist.txt",
"/bin/ls /workspace/",
"/usr/bin/find /workspace",
"/usr/local/bin/python3 script.py",
"sh -c 'ls /workspace/'",
"bash -lc 'pwd'",
"export FOO=bar",
"export PATH=/workspace/bin",
"env FOO=bar node script.js",
"NODE_ENV=test npm test",
"NODE_ENV=test\nnpm test",
"FOO=bar\r\n npm test",
"FOO=bar ./gradlew test",
"FOO=bar BAR=baz node --version",
'GREETING="hello world" npm test',
"GREETING='hello world' ./gradlew test",
"EMPTY= npm test",
"// inspect the workspace\npwd",
"/* inspect the workspace */ pwd;",
"// use a clean environment\nNODE_ENV=test npm test",
"git status",
'git status; const note = "git";',
'git status; const note = "function git";',
"ls -1; const metadata = { ls: true };",
"ls -1; const note = 'function ls';",
"ls -1; let ls = 7;",
"pwd; class/**/pwd {}",
"npm test",
"npx vitest run",
"pnpm test src/agents/code-mode.test.ts",
"rg code-mode src/agents",
"sudo ls /workspace",
"docker ps",
"kubectl get pods",
"make test",
"set -euo pipefail",
"exit",
"logout",
"if [ -d /workspace ]; then pwd; fi",
"if [[ -d /workspace ]]; then pwd; fi",
"while test -d /workspace; do pwd; done",
'for file in /workspace/*; do echo "$file"; done',
'for ((i=0; i<3; i++)); do echo "$i"; done',
"function task { pwd; }",
"source ./env",
"command ls",
"whoami",
"date",
"uname -a",
"go test ./...",
"cargo test",
"sort /workspace/file",
"wc -l file",
"jq . file.json",
"exec ls",
"cut -d : file",
"uniq -c file",
"custom-tool --format=json",
"some_cli /workspace/file",
"ls > output",
"ls>output",
"ls >output",
"ls >> output",
"ls>>output",
"ls 2> error",
"ls < input",
"cat<input",
"cat <input",
"python3 script.py",
"node script.js",
"powershell -Command Get-Location",
"pwsh -Command Get-Location",
"./script.sh --verbose",
"./gradlew test",
".\\gradlew.bat test",
".\\script.ps1",
"C:\\workspace\\run.cmd /q",
"C:\\workspace\\script.ps1 -Verbose",
"./configure --prefix=/workspace",
"../bin/task --verbose",
"../scripts/run.bash",
"/workspace/run.sh --verbose",
"/opt/homebrew/bin/tool --version",
"~/bin/task",
"~/scripts/run.zsh",
" ls -la /workspace/ ",
])("rejects the shell command %j", (source) => {
expect(isShellLikeCodeModeSource(source)).toBe(true);
});
it.each([
"",
" ",
"true",
"true;",
"false",
"false;",
"null;",
"42;",
'"ls /workspace";',
"return 7;",
"return -1;",
"return /foo/.test('foo');",
"return true || false;",
"Infinity -1; return 42;",
"eval; return typeof eval;",
"Date -1; return 7;",
"if (true) { return -1; }",
"while (false) { return 7; }",
"for (let i = 0; i < 3; i++) { if (i === 2) { return i; } }",
"function task() { return 7; } return task();",
"const value = ;",
"let value = ;",
"value || fallback",
"value && fallback",
"value | mask",
"value > limit",
"/workspace/.test(value)",
"/foo/.test('foo')",
"ls('workspace')",
"ls .call(null)",
"ls ?.('workspace')",
"ls = 7",
"ls + count",
"ls - count",
"ls -1; function ls() {}",
"ls -1; function/**/ls() {}",
"ls -la /workspace; function ls() {}",
"echo('hello')",
"echo `hello`; function echo(parts) { return parts[0]; }",
"pwd?.()",
"pwd; function pwd() {}",
"pwd\nfunction pwd() {}",
"pwd; var { pwd } = { pwd: 7 }; return pwd;",
"pwd; var [pwd] = [7]; return pwd;",
"pwd; var { nested: { pwd } } = { nested: { pwd: 7 } }; return pwd;",
"pwd; for (var pwd of [7]) {} return pwd;",
"pwd; var other = 1, pwd = 7; return pwd;",
"pwd; function* pwd() { yield 7; }",
"pwd; function/**/pwd() {}",
"pwd; function/**/\u002a/**/pwd() { yield 7; }",
"pwd; var/**/{ pwd } = { pwd: 7 }; return pwd;",
"node -version; function/**/node() {}; var version = 1;",
"ls > limit; function ls() {} var limit = 1;",
"test instanceof Function; function test() {}",
"git instanceof Function; function git() {}",
"export const answer = 7;",
"export abstract class Example {}",
"export import Alias = require('./module');",
"export declare namespace Example {}",
"export as namespace Example;",
"export = Example;",
"export async function read() {}",
"export { answer };",
"export * from './types';",
"export interface Result { value: number }",
"export enum Color { Red, Green }",
'const result = await tools.callValue("openclaw:core:exec", { command: "ls" }); return result;',
'return await tools.callValue("openclaw:core:read", { path: "/workspace" });',
"console.log(await tools.callValue('openclaw:core:read', { path: '/workspace' }));",
"// shell documentation: ls /workspace\nreturn 7;",
"// shell documentation: ls /workspace\nconst answer = 7; return answer;",
"/* typed module */ export interface Result { value: number }",
])("preserves the JavaScript or TypeScript source %j", (source) => {
expect(isShellLikeCodeModeSource(source)).toBe(false);
});
it.each([
{
source: "node -1; var node: number = 7;",
preparedSource: "node -1; var node = 7;",
},
{
source: "node --version; var node: number = 2, version = 1;",
preparedSource: "node--; version; var node = 2, version = 1;",
},
])("preserves transpiled command-like TypeScript: $source", ({ source, preparedSource }) => {
expect(isShellLikeCodeModeSource(source, preparedSource)).toBe(false);
});
it("explains how to execute a real catalog tool without retrying shell source", () => {
expect(CODE_MODE_SHELL_SOURCE_ERROR).toContain("JavaScript or TypeScript");
expect(CODE_MODE_SHELL_SOURCE_ERROR).toContain("not shell");
expect(CODE_MODE_SHELL_SOURCE_ERROR).toContain("tools.callValue");
expect(CODE_MODE_SHELL_SOURCE_ERROR).toContain("ALL_TOOLS");
expect(CODE_MODE_SHELL_SOURCE_ERROR).toContain("Do not retry");
});
});