fix(macos): harden exec approval socket auth

This commit is contained in:
Peter Steinberger
2026-03-16 23:00:14 -07:00
parent 2d100157bd
commit be2e6ca0f6
3 changed files with 37 additions and 1 deletions

View File

@@ -112,6 +112,7 @@ Docs: https://docs.openclaw.ai
- Control UI/overview: keep the language dropdown aligned with the persisted locale during dashboard startup so refreshing the page does not fall back to English before locale hydration completes. (#48019) Thanks @git-jxj.
- Agents/compaction: rerun transcript repair after `session.compact()` so orphaned `tool_result` blocks cannot survive compaction and break later Anthropic requests. (#16095) thanks @claw-sylphx.
- Agents/compaction: trigger overflow recovery from the tool-result guard once post-compaction context still exceeds the safe threshold, so long tool loops compact before the next model call hard-fails. (#29371) thanks @keshav55.
- macOS/exec approvals: harden exec-host request HMAC verification to use a timing-safe compare and keep malformed or truncated signatures fail-closed in focused IPC auth coverage.
## 2026.3.13

View File

@@ -89,6 +89,20 @@ private func readLineFromHandle(_ handle: FileHandle, maxBytes: Int) throws -> S
return String(data: lineData, encoding: .utf8)
}
func timingSafeHexStringEquals(_ lhs: String, _ rhs: String) -> Bool {
let lhsBytes = Array(lhs.utf8)
let rhsBytes = Array(rhs.utf8)
guard lhsBytes.count == rhsBytes.count else {
return false
}
var diff: UInt8 = 0
for index in lhsBytes.indices {
diff |= lhsBytes[index] ^ rhsBytes[index]
}
return diff == 0
}
enum ExecApprovalsSocketClient {
private struct TimeoutError: LocalizedError {
var message: String
@@ -854,7 +868,7 @@ private final class ExecApprovalsSocketServer: @unchecked Sendable {
error: ExecHostError(code: "INVALID_REQUEST", message: "expired request", reason: "ttl"))
}
let expected = self.hmacHex(nonce: request.nonce, ts: request.ts, requestJson: request.requestJson)
if expected != request.hmac {
if !timingSafeHexStringEquals(expected, request.hmac) {
return ExecHostResponse(
type: "exec-res",
id: request.id,

View File

@@ -0,0 +1,21 @@
import Testing
@testable import OpenClaw
struct ExecApprovalsSocketAuthTests {
@Test
func `timing safe hex compare matches equal strings`() {
#expect(timingSafeHexStringEquals(String(repeating: "a", count: 64), String(repeating: "a", count: 64)))
}
@Test
func `timing safe hex compare rejects mismatched strings`() {
let expected = String(repeating: "a", count: 63) + "b"
let provided = String(repeating: "a", count: 63) + "c"
#expect(!timingSafeHexStringEquals(expected, provided))
}
@Test
func `timing safe hex compare rejects different length strings`() {
#expect(!timingSafeHexStringEquals(String(repeating: "a", count: 64), "deadbeef"))
}
}