mirror of
https://github.com/moltbot/moltbot.git
synced 2026-03-07 22:44:16 +00:00
fix(security): block startup-file env injection across host execution paths
This commit is contained in:
@@ -364,21 +364,6 @@ private enum ExecHostExecutor {
|
||||
let skillAllow: Bool
|
||||
}
|
||||
|
||||
private static let blockedEnvKeys: Set<String> = [
|
||||
"PATH",
|
||||
"NODE_OPTIONS",
|
||||
"PYTHONHOME",
|
||||
"PYTHONPATH",
|
||||
"PERL5LIB",
|
||||
"PERL5OPT",
|
||||
"RUBYOPT",
|
||||
]
|
||||
|
||||
private static let blockedEnvPrefixes: [String] = [
|
||||
"DYLD_",
|
||||
"LD_",
|
||||
]
|
||||
|
||||
static func handle(_ request: ExecHostRequest) async -> ExecHostResponse {
|
||||
let command = request.command.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
|
||||
guard !command.isEmpty else {
|
||||
@@ -580,18 +565,8 @@ private enum ExecHostExecutor {
|
||||
error: nil)
|
||||
}
|
||||
|
||||
private static func sanitizedEnv(_ overrides: [String: String]?) -> [String: String]? {
|
||||
guard let overrides else { return nil }
|
||||
var merged = ProcessInfo.processInfo.environment
|
||||
for (rawKey, value) in overrides {
|
||||
let key = rawKey.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !key.isEmpty else { continue }
|
||||
let upper = key.uppercased()
|
||||
if self.blockedEnvKeys.contains(upper) { continue }
|
||||
if self.blockedEnvPrefixes.contains(where: { upper.hasPrefix($0) }) { continue }
|
||||
merged[key] = value
|
||||
}
|
||||
return merged
|
||||
private static func sanitizedEnv(_ overrides: [String: String]?) -> [String: String] {
|
||||
HostEnvSanitizer.sanitize(overrides: overrides)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
54
apps/macos/Sources/OpenClaw/HostEnvSanitizer.swift
Normal file
54
apps/macos/Sources/OpenClaw/HostEnvSanitizer.swift
Normal file
@@ -0,0 +1,54 @@
|
||||
import Foundation
|
||||
|
||||
enum HostEnvSanitizer {
|
||||
private static let blockedKeys: Set<String> = [
|
||||
"NODE_OPTIONS",
|
||||
"NODE_PATH",
|
||||
"PYTHONHOME",
|
||||
"PYTHONPATH",
|
||||
"PERL5LIB",
|
||||
"PERL5OPT",
|
||||
"RUBYLIB",
|
||||
"RUBYOPT",
|
||||
"BASH_ENV",
|
||||
"ENV",
|
||||
"GCONV_PATH",
|
||||
"IFS",
|
||||
"SSLKEYLOGFILE",
|
||||
]
|
||||
|
||||
private static let blockedPrefixes: [String] = [
|
||||
"DYLD_",
|
||||
"LD_",
|
||||
"BASH_FUNC_",
|
||||
]
|
||||
|
||||
private static func isBlocked(_ upperKey: String) -> Bool {
|
||||
if self.blockedKeys.contains(upperKey) { return true }
|
||||
return self.blockedPrefixes.contains(where: { upperKey.hasPrefix($0) })
|
||||
}
|
||||
|
||||
static func sanitize(overrides: [String: String]?) -> [String: String] {
|
||||
var merged: [String: String] = [:]
|
||||
for (rawKey, value) in ProcessInfo.processInfo.environment {
|
||||
let key = rawKey.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !key.isEmpty else { continue }
|
||||
let upper = key.uppercased()
|
||||
if self.isBlocked(upper) { continue }
|
||||
merged[key] = value
|
||||
}
|
||||
|
||||
guard let overrides else { return merged }
|
||||
for (rawKey, value) in overrides {
|
||||
let key = rawKey.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !key.isEmpty else { continue }
|
||||
let upper = key.uppercased()
|
||||
// PATH is part of the security boundary (command resolution + safe-bin checks). Never
|
||||
// allow request-scoped PATH overrides from agents/gateways.
|
||||
if upper == "PATH" { continue }
|
||||
if self.isBlocked(upper) { continue }
|
||||
merged[key] = value
|
||||
}
|
||||
return merged
|
||||
}
|
||||
}
|
||||
@@ -862,33 +862,8 @@ extension MacNodeRuntime {
|
||||
UserDefaults.standard.object(forKey: cameraEnabledKey) as? Bool ?? false
|
||||
}
|
||||
|
||||
private static let blockedEnvKeys: Set<String> = [
|
||||
"PATH",
|
||||
"NODE_OPTIONS",
|
||||
"PYTHONHOME",
|
||||
"PYTHONPATH",
|
||||
"PERL5LIB",
|
||||
"PERL5OPT",
|
||||
"RUBYOPT",
|
||||
]
|
||||
|
||||
private static let blockedEnvPrefixes: [String] = [
|
||||
"DYLD_",
|
||||
"LD_",
|
||||
]
|
||||
|
||||
private static func sanitizedEnv(_ overrides: [String: String]?) -> [String: String]? {
|
||||
guard let overrides else { return nil }
|
||||
var merged = ProcessInfo.processInfo.environment
|
||||
for (rawKey, value) in overrides {
|
||||
let key = rawKey.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !key.isEmpty else { continue }
|
||||
let upper = key.uppercased()
|
||||
if self.blockedEnvKeys.contains(upper) { continue }
|
||||
if self.blockedEnvPrefixes.contains(where: { upper.hasPrefix($0) }) { continue }
|
||||
merged[key] = value
|
||||
}
|
||||
return merged
|
||||
private static func sanitizedEnv(_ overrides: [String: String]?) -> [String: String] {
|
||||
HostEnvSanitizer.sanitize(overrides: overrides)
|
||||
}
|
||||
|
||||
private nonisolated static func locationMode() -> OpenClawLocationMode {
|
||||
|
||||
Reference in New Issue
Block a user