| import Foundation |
| import Testing |
| @testable import OpenClaw |
|
|
| @Suite(.serialized) |
| struct ExecApprovalsStoreRefactorTests { |
| private func withTempStateDir( |
| _ body: @escaping @Sendable (URL) async throws -> Void) async throws |
| { |
| let stateDir = FileManager().temporaryDirectory |
| .appendingPathComponent("openclaw-state-\(UUID().uuidString)", isDirectory: true) |
| defer { try? FileManager().removeItem(at: stateDir) } |
|
|
| try await TestIsolation.withEnvValues(["OPENCLAW_STATE_DIR": stateDir.path]) { |
| try await body(stateDir) |
| } |
| } |
|
|
| @Test |
| func `ensure file skips rewrite when unchanged`() async throws { |
| try await self.withTempStateDir { _ in |
| _ = ExecApprovalsStore.ensureFile() |
| let url = ExecApprovalsStore.fileURL() |
| let firstWriteDate = try Self.modificationDate(at: url) |
|
|
| try await Task.sleep(nanoseconds: 1_100_000_000) |
| _ = ExecApprovalsStore.ensureFile() |
| let secondWriteDate = try Self.modificationDate(at: url) |
|
|
| #expect(firstWriteDate == secondWriteDate) |
| } |
| } |
|
|
| @Test |
| func `update allowlist reports rejected basename pattern`() async throws { |
| try await self.withTempStateDir { _ in |
| let rejected = ExecApprovalsStore.updateAllowlist( |
| agentId: "main", |
| allowlist: [ |
| ExecAllowlistEntry(pattern: "echo"), |
| ExecAllowlistEntry(pattern: "/bin/echo"), |
| ]) |
| #expect(rejected.count == 1) |
| #expect(rejected.first?.reason == .missingPathComponent) |
| #expect(rejected.first?.pattern == "echo") |
|
|
| let resolved = ExecApprovalsStore.resolve(agentId: "main") |
| #expect(resolved.allowlist.map(\.pattern) == ["/bin/echo"]) |
| } |
| } |
|
|
| @Test |
| func `update allowlist migrates legacy pattern from resolved path`() async throws { |
| try await self.withTempStateDir { _ in |
| let rejected = ExecApprovalsStore.updateAllowlist( |
| agentId: "main", |
| allowlist: [ |
| ExecAllowlistEntry( |
| pattern: "echo", |
| lastUsedAt: nil, |
| lastUsedCommand: nil, |
| lastResolvedPath: " /usr/bin/echo "), |
| ]) |
| #expect(rejected.isEmpty) |
|
|
| let resolved = ExecApprovalsStore.resolve(agentId: "main") |
| #expect(resolved.allowlist.map(\.pattern) == ["/usr/bin/echo"]) |
| } |
| } |
|
|
| @Test |
| func `ensure file hardens state directory permissions`() async throws { |
| try await self.withTempStateDir { stateDir in |
| try FileManager().createDirectory(at: stateDir, withIntermediateDirectories: true) |
| try FileManager().setAttributes([.posixPermissions: 0o755], ofItemAtPath: stateDir.path) |
|
|
| _ = ExecApprovalsStore.ensureFile() |
| let attrs = try FileManager().attributesOfItem(atPath: stateDir.path) |
| let permissions = (attrs[.posixPermissions] as? NSNumber)?.intValue ?? -1 |
| #expect(permissions & 0o777 == 0o700) |
| } |
| } |
|
|
| private static func modificationDate(at url: URL) throws -> Date { |
| let attributes = try FileManager().attributesOfItem(atPath: url.path) |
| guard let date = attributes[.modificationDate] as? Date else { |
| struct MissingDateError: Error {} |
| throw MissingDateError() |
| } |
| return date |
| } |
| } |
|
|