mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-27 09:02:15 +00:00
Exec approvals: reject wrapper carrier allow-always targets (#55947)
* Exec approvals: reject wrapper carrier allow-always targets Co-authored-by: nexrin <268879349+nexrin@users.noreply.github.com> * Tests: add shell wrapper carrier follow-up assertion --------- Co-authored-by: nexrin <268879349+nexrin@users.noreply.github.com>
This commit is contained in:
@@ -652,4 +652,60 @@ $0 \\"$1\\"" touch {marker}`,
|
||||
persistedPattern: benign,
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects positional carrier when carried executable is a dispatch wrapper", () => {
|
||||
if (process.platform === "win32") {
|
||||
return;
|
||||
}
|
||||
const dir = makeTempDir();
|
||||
makeExecutable(dir, "env");
|
||||
const env = makePathEnv(dir);
|
||||
const safeBins = resolveSafeBins(undefined);
|
||||
|
||||
const { persisted } = resolvePersistedPatterns({
|
||||
command: `sh -lc '$0 "$@"' env echo SAFE`,
|
||||
dir,
|
||||
env,
|
||||
safeBins,
|
||||
});
|
||||
expect(persisted).toEqual([]);
|
||||
|
||||
const second = evaluateShellAllowlist({
|
||||
command: `sh -lc '$0 "$@"' env BASH_ENV=/tmp/payload.sh bash -lc 'id > /tmp/pwned'`,
|
||||
allowlist: persisted.map((pattern) => ({ pattern })),
|
||||
safeBins,
|
||||
cwd: dir,
|
||||
env,
|
||||
platform: process.platform,
|
||||
});
|
||||
expect(second.allowlistSatisfied).toBe(false);
|
||||
});
|
||||
|
||||
it("rejects positional carrier when carried executable is a shell wrapper", () => {
|
||||
if (process.platform === "win32") {
|
||||
return;
|
||||
}
|
||||
const dir = makeTempDir();
|
||||
makeExecutable(dir, "bash");
|
||||
const env = makePathEnv(dir);
|
||||
const safeBins = resolveSafeBins(undefined);
|
||||
|
||||
const { persisted } = resolvePersistedPatterns({
|
||||
command: `sh -lc '$0 "$@"' bash -lc 'echo safe'`,
|
||||
dir,
|
||||
env,
|
||||
safeBins,
|
||||
});
|
||||
expect(persisted).toEqual([]);
|
||||
|
||||
const second = evaluateShellAllowlist({
|
||||
command: `sh -lc '$0 "$@"' bash -lc 'id > /tmp/pwned'`,
|
||||
allowlist: persisted.map((pattern) => ({ pattern })),
|
||||
safeBins,
|
||||
cwd: dir,
|
||||
env,
|
||||
platform: process.platform,
|
||||
});
|
||||
expect(second.allowlistSatisfied).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import path from "node:path";
|
||||
import { isDispatchWrapperExecutable } from "./dispatch-wrapper-resolution.js";
|
||||
import {
|
||||
analyzeShellCommand,
|
||||
isWindowsPlatform,
|
||||
@@ -463,6 +464,13 @@ function resolveShellWrapperPositionalArgvCandidatePath(params: {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Reject wrapper targets carried through `$0 "$@"` because their trailing argv can
|
||||
// widen execution semantics beyond the original approved command.
|
||||
const carriedName = normalizeExecutableToken(carriedExecutable);
|
||||
if (isDispatchWrapperExecutable(carriedName) || isShellWrapperExecutable(carriedName)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const resolution = resolveCommandResolutionFromArgv([carriedExecutable], params.cwd, params.env);
|
||||
return resolveExecutionTargetCandidatePath(resolution, params.cwd);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user