mirror of
https://github.com/moltbot/moltbot.git
synced 2026-03-07 22:44:16 +00:00
test: add env -S allowlist bypass regressions
This commit is contained in:
@@ -16,7 +16,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Security/Config writes: block reserved prototype keys in account-id normalization and route account config resolution through own-key lookups, hardening `/allowlist` and account-scoped config paths against prototype-chain pollution.
|
||||
- Security/Exec: harden `safeBins` long-option validation by rejecting unknown/ambiguous GNU long-option abbreviations and denying sort filesystem-dependent flags (`--random-source`, `--temporary-directory`, `-T`), closing safe-bin denylist bypasses. Thanks @jiseoung.
|
||||
- Security/Channels: unify dangerous name-matching policy checks (`dangerouslyAllowNameMatching`) across core and extension channels, share mutable-allowlist detectors between `openclaw doctor` and `openclaw security audit`, and scan all configured accounts (not only the default account) in channel security audit findings.
|
||||
- Security/Exec approvals: enforce canonical wrapper execution plans across allowlist analysis and runtime execution (node host + gateway host), fail closed on semantic `env` wrapper usage, and reject unknown short safe-bin flags to prevent `env -S/--split-string` interpretation-mismatch bypasses. This ships in the next npm release. Thanks @jiseoung for reporting.
|
||||
- Security/Exec approvals: enforce canonical wrapper execution plans across allowlist analysis and runtime execution (node host + gateway host), fail closed on semantic `env` wrapper usage, and reject unknown short safe-bin flags to prevent `env -S/--split-string` interpretation-mismatch bypasses. This ships in the next npm release. Thanks @tdjackey for reporting.
|
||||
- Security/Image tool: enforce `tools.fs.workspaceOnly` for sandboxed `image` path resolution so mounted out-of-workspace paths are blocked before media bytes are loaded/sent to vision providers. This ships in the next npm release. Thanks @tdjackey for reporting.
|
||||
|
||||
## 2026.2.23 (Unreleased)
|
||||
|
||||
@@ -68,6 +68,9 @@ describe("browser control server", () => {
|
||||
cdpUrl: state.cdpBaseUrl,
|
||||
targetId: "abcd1234",
|
||||
url: "https://example.com",
|
||||
ssrfPolicy: {
|
||||
dangerouslyAllowPrivateNetwork: true,
|
||||
},
|
||||
});
|
||||
|
||||
const click = await postJson<{ ok: boolean }>(`${base}/act`, {
|
||||
|
||||
@@ -254,6 +254,35 @@ describe("exec approvals command resolution", () => {
|
||||
expect(resolution?.rawExecutable).toBe("/usr/bin/env");
|
||||
});
|
||||
|
||||
it("fails closed for env -S even when env itself is allowlisted", () => {
|
||||
const dir = makeTempDir();
|
||||
const binDir = path.join(dir, "bin");
|
||||
fs.mkdirSync(binDir, { recursive: true });
|
||||
const envName = process.platform === "win32" ? "env.exe" : "env";
|
||||
const envPath = path.join(binDir, envName);
|
||||
fs.writeFileSync(envPath, process.platform === "win32" ? "" : "#!/bin/sh\n");
|
||||
if (process.platform !== "win32") {
|
||||
fs.chmodSync(envPath, 0o755);
|
||||
}
|
||||
|
||||
const analysis = analyzeArgvCommand({
|
||||
argv: [envPath, "-S", 'sh -c "echo pwned"'],
|
||||
cwd: dir,
|
||||
env: makePathEnv(binDir),
|
||||
});
|
||||
const allowlistEval = evaluateExecAllowlist({
|
||||
analysis,
|
||||
allowlist: [{ pattern: envPath }],
|
||||
safeBins: normalizeSafeBins([]),
|
||||
cwd: dir,
|
||||
});
|
||||
|
||||
expect(analysis.ok).toBe(true);
|
||||
expect(analysis.segments[0]?.resolution?.policyBlocked).toBe(true);
|
||||
expect(allowlistEval.allowlistSatisfied).toBe(false);
|
||||
expect(allowlistEval.segmentSatisfiedBy).toEqual([null]);
|
||||
});
|
||||
|
||||
it("unwraps env wrapper with shell inner executable", () => {
|
||||
const resolution = resolveCommandResolutionFromArgv(["/usr/bin/env", "bash", "-lc", "echo hi"]);
|
||||
expect(resolution?.rawExecutable).toBe("bash");
|
||||
|
||||
@@ -150,7 +150,6 @@ describe("handleSystemRunInvoke mac app exec host routing", () => {
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("denies ./sh wrapper spoof in allowlist on-miss mode before execution", async () => {
|
||||
const marker = path.join(os.tmpdir(), `openclaw-wrapper-spoof-${process.pid}-${Date.now()}`);
|
||||
const runCommand = vi.fn(async () => {
|
||||
@@ -213,4 +212,21 @@ describe("handleSystemRunInvoke mac app exec host routing", () => {
|
||||
// no-op
|
||||
}
|
||||
});
|
||||
|
||||
it("denies env -S shell payloads in allowlist mode", async () => {
|
||||
const { runCommand, sendInvokeResult } = await runSystemInvoke({
|
||||
preferMacAppExecHost: false,
|
||||
security: "allowlist",
|
||||
command: ["env", "-S", 'sh -c "echo pwned"'],
|
||||
});
|
||||
expect(runCommand).not.toHaveBeenCalled();
|
||||
expect(sendInvokeResult).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
ok: false,
|
||||
error: expect.objectContaining({
|
||||
message: expect.stringContaining("allowlist miss"),
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user