fix(macos): address wrapper review feedback

This commit is contained in:
Nimrod Gutman 2026-03-19 14:27:05 +02:00
parent 5b704a6ea4
commit f55e51afb5
4 changed files with 39 additions and 11 deletions

View File

@ -12,11 +12,20 @@ struct ExecCommandResolution {
cwd: String?,
env: [String: String]?) -> ExecCommandResolution?
{
let effective = ExecWrapperResolution.unwrapDispatchWrappersForResolution(command)
guard let effectiveRaw = effective.first?.trimmingCharacters(in: .whitespacesAndNewlines), !effectiveRaw.isEmpty else {
return nil
}
let trimmedRaw = rawCommand?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
if !trimmedRaw.isEmpty, let token = self.parseFirstToken(trimmedRaw) {
return self.resolveExecutable(rawExecutable: token, cwd: cwd, env: env)
let normalizedToken = ExecWrapperResolution.normalizeExecutableToken(token)
let normalizedEffective = ExecWrapperResolution.normalizeExecutableToken(effectiveRaw)
if normalizedToken == normalizedEffective {
return self.resolveExecutable(rawExecutable: token, cwd: cwd, env: env)
}
}
return self.resolve(command: command, cwd: cwd, env: env)
return self.resolveExecutable(rawExecutable: effectiveRaw, cwd: cwd, env: env)
}
static func resolveForAllowlist(
@ -126,7 +135,7 @@ struct ExecCommandResolution {
patterns: inout [String],
seen: inout Set<String>)
{
guard depth < 3, !command.isEmpty else {
guard depth <= ExecWrapperResolution.maxWrapperDepth, !command.isEmpty else {
return
}

View File

@ -94,13 +94,7 @@ enum ExecSystemRunCommandValidator {
return normalizedRaw == previewCommand ? normalizedRaw : nil
}
private static func hasEnvManipulationBeforeShellWrapper(
_ argv: [String],
depth: Int = 0,
envManipulationSeen: Bool = false) -> Bool
{
_ = depth
_ = envManipulationSeen
private static func hasEnvManipulationBeforeShellWrapper(_ argv: [String]) -> Bool {
return ExecWrapperResolution.hasEnvManipulationBeforeShellWrapper(argv)
}

View File

@ -229,7 +229,6 @@ enum ExecWrapperResolution {
policyBlocked: true,
blockedWrapper: wrapper)
case let .unwrapped(wrapper, argv):
guard !argv.isEmpty else { break }
wrappers.append(wrapper)
if self.isSemanticDispatchWrapperUsage(wrapper: wrapper, argv: current) {
return DispatchWrapperExecutionPlan(

View File

@ -264,6 +264,19 @@ struct ExecAllowlistTests {
#expect(resolutions[1].executableName == "whoami")
}
@Test func `resolve for allowlist unwraps direct dispatch wrappers with canonical raw command`() {
let command = ["/usr/bin/nice", "/usr/bin/printf", "ok"]
let resolutions = ExecCommandResolution.resolveForAllowlist(
command: command,
rawCommand: "/usr/bin/nice /usr/bin/printf ok",
cwd: nil,
env: ["PATH": "/usr/bin:/bin"])
#expect(resolutions.count == 1)
#expect(resolutions[0].resolvedPath == "/usr/bin/printf")
#expect(resolutions[0].executableName == "printf")
}
@Test func `resolve for allowlist unwraps env dispatch wrappers inside shell segments`() {
let command = ["/bin/sh", "-lc", "env /usr/bin/touch /tmp/openclaw-allowlist-test"]
let resolutions = ExecCommandResolution.resolveForAllowlist(
@ -377,6 +390,19 @@ struct ExecAllowlistTests {
#expect(patterns.isEmpty)
}
@Test func `allow always patterns support max transparent wrapper depth`() throws {
let tmp = try makeTempDirForTests()
let whoami = tmp.appendingPathComponent("whoami")
try makeExecutableForTests(at: whoami)
let patterns = ExecCommandResolution.resolveAllowAlwaysPatterns(
command: ["nice", "nohup", "timeout", "5", "stdbuf", "-o", "L", "whoami"],
cwd: tmp.path,
env: ["PATH": "\(tmp.path):/usr/bin:/bin"])
#expect(patterns == [whoami.path])
}
@Test func `match all requires every segment to match`() {
let first = ExecCommandResolution(
rawExecutable: "echo",