Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions src/sandboxes/__tests__/appendNoCommitInstruction.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { describe, it, expect } from "vitest";
import { appendNoCommitInstruction, NO_COMMIT_INSTRUCTION } from "../appendNoCommitInstruction";

describe("appendNoCommitInstruction", () => {
it("appends the instruction to the value after --message", () => {
const result = appendNoCommitInstruction([
"agent",
"--agent",
"main",
"--message",
"fix the bug",
]);

expect(result).toEqual([
"agent",
"--agent",
"main",
"--message",
`fix the bug ${NO_COMMIT_INSTRUCTION}`,
]);
});

it("is idempotent — does not re-append when the instruction is already present", () => {
const original = [
"agent",
"--message",
`do something ${NO_COMMIT_INSTRUCTION}`,
];

expect(appendNoCommitInstruction(original)).toEqual(original);
});

it("leaves args unchanged when there is no --message flag", () => {
const original = ["status"];
expect(appendNoCommitInstruction(original)).toEqual(original);
});

it("leaves args unchanged when --message is the last arg (no value)", () => {
const original = ["agent", "--message"];
expect(appendNoCommitInstruction(original)).toEqual(original);
});

it("returns a new array rather than mutating the input", () => {
const original = ["--message", "hi"];
const result = appendNoCommitInstruction(original);
expect(result).not.toBe(original);
expect(original).toEqual(["--message", "hi"]);
});
});
28 changes: 28 additions & 0 deletions src/sandboxes/appendNoCommitInstruction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
export const NO_COMMIT_INSTRUCTION = "do not commit your changes.";

/**
* Hotfix guardrail: ensure the `--message` value passed to `openclaw agent`
* (or any tool that takes `--message <prompt>`) ends with the
* {@link NO_COMMIT_INSTRUCTION} text. The task runs in a sandbox that is
* git-aware, and we push sandbox changes to GitHub after the command — we
* do NOT want the agent itself creating commits during the run.
*
* @param args - The `args` payload passed to `sandbox.runCommand`.
* @returns A new args array with the instruction appended to the `--message`
* value if one is present and the instruction isn't already there.
* Otherwise the input is returned unchanged (as a copy).
*/
export function appendNoCommitInstruction(args: readonly string[]): string[] {
const out = [...args];
const messageIdx = out.indexOf("--message");
if (messageIdx === -1) return out;

const valueIdx = messageIdx + 1;
if (valueIdx >= out.length) return out;

const current = out[valueIdx];
if (current.includes(NO_COMMIT_INSTRUCTION)) return out;

out[valueIdx] = `${current} ${NO_COMMIT_INSTRUCTION}`;
Comment on lines +24 to +26
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

includes() does not enforce the required suffix behavior.

At Line [24], the early return triggers even when the instruction is only a substring, so the message may still not end with the required text.

Proposed fix
-  const current = out[valueIdx];
-  if (current.includes(NO_COMMIT_INSTRUCTION)) return out;
-
-  out[valueIdx] = `${current} ${NO_COMMIT_INSTRUCTION}`;
+  const current = out[valueIdx].trimEnd();
+  if (current.endsWith(NO_COMMIT_INSTRUCTION)) return out;
+
+  out[valueIdx] = current.length
+    ? `${current} ${NO_COMMIT_INSTRUCTION}`
+    : NO_COMMIT_INSTRUCTION;
   return out;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (current.includes(NO_COMMIT_INSTRUCTION)) return out;
out[valueIdx] = `${current} ${NO_COMMIT_INSTRUCTION}`;
const current = out[valueIdx].trimEnd();
if (current.endsWith(NO_COMMIT_INSTRUCTION)) return out;
out[valueIdx] = current.length
? `${current} ${NO_COMMIT_INSTRUCTION}`
: NO_COMMIT_INSTRUCTION;
return out;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/sandboxes/appendNoCommitInstruction.ts` around lines 24 - 26, The early
return uses current.includes(NO_COMMIT_INSTRUCTION) which matches substrings
anywhere; change the check to ensure the message actually ends with the
instruction (e.g., use current.trim().endsWith(NO_COMMIT_INSTRUCTION) or an
anchored regex) so you only skip when the instruction is the suffix. Keep the
append logic that sets out[valueIdx] = `${current} ${NO_COMMIT_INSTRUCTION}` but
ensure you trim/trail whitespace appropriately so you don't introduce double
spaces when adding the suffix.

return out;
}
3 changes: 2 additions & 1 deletion src/tasks/runSandboxCommandTask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { ensureOrgRepos } from "../sandboxes/ensureOrgRepos";
import { ensureSetupSandbox } from "../sandboxes/ensureSetupSandbox";
import { pushSandboxToGithub } from "../sandboxes/pushSandboxToGithub";
import { syncOrgRepos } from "../sandboxes/git/syncOrgRepos";
import { appendNoCommitInstruction } from "../sandboxes/appendNoCommitInstruction";
import {
runSandboxCommandPayloadSchema,
type SandboxResult,
Expand Down Expand Up @@ -76,7 +77,7 @@ export const runSandboxCommandTask = schemaTask({

const commandResult = await sandbox.runCommand({
cmd: command,
args: args || [],
args: appendNoCommitInstruction(args || []),
Comment on lines 78 to +80
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Limit prompt mutation to OpenClaw agent invocations only.

At Line [80], appendNoCommitInstruction is applied to every command’s args, not just openclaw agent. This can unexpectedly rewrite unrelated --message flags.

Proposed fix
-      const commandResult = await sandbox.runCommand({
+      const normalizedArgs = args ?? [];
+      const shouldAppendNoCommit =
+        command === "openclaw" && normalizedArgs[0] === "agent";
+
+      const commandResult = await sandbox.runCommand({
         cmd: command,
-        args: appendNoCommitInstruction(args || []),
+        args: shouldAppendNoCommit
+          ? appendNoCommitInstruction(normalizedArgs)
+          : normalizedArgs,
         cwd,
         env: getSandboxEnv(accountId),
       });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const commandResult = await sandbox.runCommand({
cmd: command,
args: args || [],
args: appendNoCommitInstruction(args || []),
const normalizedArgs = args ?? [];
const shouldAppendNoCommit =
command === "openclaw" && normalizedArgs[0] === "agent";
const commandResult = await sandbox.runCommand({
cmd: command,
args: shouldAppendNoCommit
? appendNoCommitInstruction(normalizedArgs)
: normalizedArgs,
cwd,
env: getSandboxEnv(accountId),
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/tasks/runSandboxCommandTask.ts` around lines 78 - 80, The code currently
applies appendNoCommitInstruction to every sandbox.runCommand invocation, which
can mutate unrelated commands; change the args passed to sandbox.runCommand so
appendNoCommitInstruction is only applied when invoking the OpenClaw agent
(check the command and first arg). Concretely, compute finalArgs = (command ===
'openclaw' && Array.isArray(args) && args[0] === 'agent') ?
appendNoCommitInstruction(args) : (args || []), then pass finalArgs into
sandbox.runCommand instead of always calling appendNoCommitInstruction(args ||
[]).

cwd,
env: getSandboxEnv(accountId),
});
Expand Down
Loading