fix(macos): harden exec approval socket auth
This commit is contained in:
parent
2d100157bd
commit
be2e6ca0f6
@ -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.
|
- 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: 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.
|
- 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
|
## 2026.3.13
|
||||||
|
|
||||||
|
|||||||
@ -89,6 +89,20 @@ private func readLineFromHandle(_ handle: FileHandle, maxBytes: Int) throws -> S
|
|||||||
return String(data: lineData, encoding: .utf8)
|
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 {
|
enum ExecApprovalsSocketClient {
|
||||||
private struct TimeoutError: LocalizedError {
|
private struct TimeoutError: LocalizedError {
|
||||||
var message: String
|
var message: String
|
||||||
@ -854,7 +868,7 @@ private final class ExecApprovalsSocketServer: @unchecked Sendable {
|
|||||||
error: ExecHostError(code: "INVALID_REQUEST", message: "expired request", reason: "ttl"))
|
error: ExecHostError(code: "INVALID_REQUEST", message: "expired request", reason: "ttl"))
|
||||||
}
|
}
|
||||||
let expected = self.hmacHex(nonce: request.nonce, ts: request.ts, requestJson: request.requestJson)
|
let expected = self.hmacHex(nonce: request.nonce, ts: request.ts, requestJson: request.requestJson)
|
||||||
if expected != request.hmac {
|
if !timingSafeHexStringEquals(expected, request.hmac) {
|
||||||
return ExecHostResponse(
|
return ExecHostResponse(
|
||||||
type: "exec-res",
|
type: "exec-res",
|
||||||
id: request.id,
|
id: request.id,
|
||||||
|
|||||||
@ -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"))
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user