Files
claude-code-haha/native/cu-helper/Tests/CuHelperTests/ResolvedTargetAuthorizationTests.swift
T
程序员阿江(Relakkes) 56c6a9aa8e feat(computer-use): align native app automation with Codex
Add a persistent isolated JavaScript worker for native app actions and batch
known operations without a model round trip between each input. Preserve
per-cell context, native errors, screenshot coordinates, and image types.

Align macOS gesture, key, inventory, capture, scroll, and clipboard behavior;
include a signed native receiver fixture and compiled sidecar regression tests.
Keep the Windows pixel route and fix cancellation with session-owned mouse
cleanup, lock revalidation, and portable signing-fixture tests.
2026-09-10 03:34:39 +08:00

210 lines
7.7 KiB
Swift

import XCTest
@testable import cc_haha_computer_use
final class ResolvedTargetAuthorizationTests: XCTestCase {
private let terminalIdentity = AXTreeProcessIdentity(
bundleID: "com.apple.Terminal",
executablePath: "/System/Applications/Utilities/Terminal.app/Contents/MacOS/Terminal",
launchTime: 100
)
private let calculatorIdentity = AXTreeProcessIdentity(
bundleID: "com.apple.calculator",
executablePath: "/System/Applications/Calculator.app/Contents/MacOS/Calculator",
launchTime: 200
)
private let chromeIdentity = AXTreeProcessIdentity(
bundleID: "com.google.Chrome",
executablePath: "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
launchTime: 300
)
func testChromePIDBundleNameAndPathSelectorsPreserveExactProvenIdentity() throws {
let chrome = AppTargetCandidate(
pid: 43,
bundleIdentifier: "com.google.Chrome",
bundleURL: URL(fileURLWithPath: "/Applications/Google Chrome.app"),
localizedName: "Google Chrome",
executableName: "Google Chrome"
)
let selectors: [AppTargetSelector] = [
.pid(43),
.bundleIdentifier("com.google.Chrome"),
.app("com.google.Chrome"),
.app("Google Chrome"),
.app("Google Chrome.app"),
.app("/Applications/Google Chrome.app"),
]
for selector in selectors {
let resolved = try XCTUnwrap(
AppTargetResolver.resolve(selector: selector, candidates: [chrome])
)
let target = try ResolvedTargetAuthorization.authorize(
resolved: resolved,
currentIdentity: chromeIdentity
)
XCTAssertEqual(target.pid, chrome.pid)
XCTAssertEqual(target.identity, chromeIdentity)
}
}
func testAllowingChromeStillRejectsUnprovenOrMismatchedIdentity() {
let unproven = AXTreeProcessIdentity(
bundleID: chromeIdentity.bundleID,
executablePath: chromeIdentity.executablePath,
launchTime: nil
)
XCTAssertThrowsError(try ResolvedTargetAuthorization.authorize(
pid: 43, identity: unproven, expectedBundleID: "com.google.Chrome"
)) {
XCTAssertEqual(($0 as? CUError)?.code, "app_denied")
}
XCTAssertThrowsError(try ResolvedTargetAuthorization.authorize(
pid: 43, identity: terminalIdentity, expectedBundleID: "com.google.Chrome"
)) {
XCTAssertEqual(($0 as? CUError)?.code, "stale_process")
}
}
func testNumericPIDCannotBypassDeniedResolvedBundle() {
XCTAssertThrowsError(
try ResolvedTargetAuthorization.authorize(
pid: 41,
identity: terminalIdentity,
expectedBundleID: nil
)
) {
XCTAssertEqual(($0 as? CUError)?.code, "app_denied")
}
}
func testPIDBundleAndLocalizedNameSelectorsAllAuthorizeActualResolvedBundle() throws {
let terminal = AppTargetCandidate(
pid: 41,
bundleIdentifier: "com.apple.Terminal",
bundleURL: URL(fileURLWithPath: "/System/Applications/Utilities/Terminal.app"),
localizedName: "终端",
executableName: "Terminal"
)
let selectors: [AppTargetSelector] = [
.pid(41),
.bundleIdentifier("com.apple.Terminal"),
.app("终端"),
]
for selector in selectors {
let resolved = try XCTUnwrap(
AppTargetResolver.resolve(selector: selector, candidates: [terminal])
)
XCTAssertThrowsError(
try ResolvedTargetAuthorization.authorize(
resolved: resolved,
currentIdentity: terminalIdentity
)
) {
XCTAssertEqual(($0 as? CUError)?.code, "app_denied")
}
}
}
func testWorktreePathResolutionStillReachesIntrinsicSelfControlDenial() throws {
let installed = AppTargetCandidate(
pid: 100,
bundleIdentifier: "com.claude-code-haha.desktop",
bundleURL: URL(fileURLWithPath: "/Applications/Claude Code Haha.app"),
localizedName: "Claude Code Haha",
executableName: "Claude Code Haha"
)
let worktree = AppTargetCandidate(
pid: 200,
bundleIdentifier: installed.bundleIdentifier,
bundleURL: URL(fileURLWithPath: "/Users/test/worktree/desktop/build-artifacts/macos-arm64/Claude Code Haha.app"),
localizedName: installed.localizedName,
executableName: installed.executableName
)
let resolved = try AppTargetResolver.match(
identifier: worktree.bundleURL!.path,
candidates: [installed, worktree]
)
let identity = AXTreeProcessIdentity(
bundleID: worktree.bundleIdentifier,
executablePath: worktree.bundleURL!.appendingPathComponent("Contents/MacOS/Claude Code Haha").path,
launchTime: 300
)
XCTAssertEqual(resolved.pid, worktree.pid)
XCTAssertThrowsError(
try ResolvedTargetAuthorization.authorize(
resolved: resolved,
currentIdentity: identity
)
) {
XCTAssertEqual(($0 as? CUError)?.code, "app_denied")
XCTAssertEqual(
($0 as? CUError)?.message,
"Computer Use is not allowed to use the app 'com.claude-code-haha.desktop' for safety reasons."
)
}
}
func testOmittedFrontmostAndLaunchedTargetsUseSameActualBundlePolicy() {
// Both paths ultimately produce this same resolved target shape. The
// authorizer intentionally has no selector-specific bypass.
let resolved = ResolvedAppTarget(
pid: 41,
bundleIdentifier: "com.apple.Terminal",
bundleURL: URL(fileURLWithPath: "/System/Applications/Utilities/Terminal.app")
)
for _ in ["omitted-frontmost", "launched-get-app-state"] {
XCTAssertThrowsError(
try ResolvedTargetAuthorization.authorize(
resolved: resolved,
currentIdentity: terminalIdentity
)
) {
XCTAssertEqual(($0 as? CUError)?.code, "app_denied")
}
}
}
func testAllowedResolvedTargetReturnsExactProvenProcessLifetime() throws {
let target = try ResolvedTargetAuthorization.authorize(
pid: 42,
identity: calculatorIdentity,
expectedBundleID: "com.apple.calculator"
)
XCTAssertEqual(target.pid, 42)
XCTAssertEqual(target.identity, calculatorIdentity)
}
func testMissingBundleAndResolverIdentityMismatchFailClosed() {
let missingBundle = AXTreeProcessIdentity(
bundleID: nil,
executablePath: "/Applications/Unknown.app/Contents/MacOS/Unknown",
launchTime: 100
)
XCTAssertThrowsError(
try ResolvedTargetAuthorization.authorize(
pid: 42,
identity: missingBundle,
expectedBundleID: nil
)
) {
XCTAssertEqual(($0 as? CUError)?.code, "app_denied")
}
XCTAssertThrowsError(
try ResolvedTargetAuthorization.authorize(
pid: 42,
identity: calculatorIdentity,
expectedBundleID: "com.example.reused"
)
) {
XCTAssertEqual(($0 as? CUError)?.code, "stale_process")
}
}
}