Files
steipete--codexbar/Tests/CodexBarTests/ClaudeOAuthCredentialsStoreSecurityCLIFallbackPolicyTests.swift
2026-07-13 12:22:33 +08:00

110 lines
5.1 KiB
Swift

import Foundation
import Testing
@testable import CodexBarCore
@Suite(.serialized)
struct ClaudeOAuthCredentialsStoreSecurityCLIFallbackPolicyTests {
private func makeCredentialsData(accessToken: String, expiresAt: Date) -> Data {
let millis = Int(expiresAt.timeIntervalSince1970 * 1000)
let json = """
{
"claudeAiOauth": {
"accessToken": "\(accessToken)",
"expiresAt": \(millis),
"scopes": ["user:profile"]
}
}
"""
return Data(json.utf8)
}
@Test
func `experimental reader blocks background fallback per stored policy`() {
let fallbackData = self.makeCredentialsData(
accessToken: "fallback-should-be-blocked",
expiresAt: Date(timeIntervalSinceNow: 3600))
let hasCredentials = KeychainAccessGate.withTaskOverrideForTesting(false) {
ClaudeOAuthKeychainReadStrategyPreference.withTaskOverrideForTesting(
.securityCLIExperimental,
operation: {
ClaudeOAuthKeychainPromptPreference.withTaskOverrideForTesting(
.onlyOnUserAction,
operation: {
ProviderInteractionContext.$current.withValue(.background) {
ClaudeOAuthCredentialsStore.withClaudeKeychainOverridesForTesting(
data: fallbackData,
fingerprint: nil)
{
ClaudeOAuthCredentialsStore.withSecurityCLIReadOverrideForTesting(
.nonZeroExit)
{
ClaudeOAuthCredentialsStore.hasClaudeKeychainCredentialsWithoutPrompt()
}
}
}
})
})
}
#expect(hasCredentials == false)
}
@Test
func `experimental reader sync from claude keychain without prompt background fallback blocked by stored policy`() {
let service = "com.steipete.codexbar.cache.tests.\(UUID().uuidString)"
KeychainCacheStore.withServiceOverrideForTesting(service) {
KeychainAccessGate.withTaskOverrideForTesting(false) {
KeychainCacheStore.setTestStoreForTesting(true)
defer { KeychainCacheStore.setTestStoreForTesting(false) }
ClaudeOAuthCredentialsStore.withIsolatedMemoryCacheForTesting {
ClaudeOAuthCredentialsStore.invalidateCache()
defer { ClaudeOAuthCredentialsStore.invalidateCache() }
let fallbackData = self.makeCredentialsData(
accessToken: "sync-fallback-should-be-blocked",
expiresAt: Date(timeIntervalSinceNow: 3600))
final class Counter: @unchecked Sendable {
var value = 0
}
let preflightCalls = Counter()
let preflightOverride: (String, String?) -> KeychainAccessPreflight.Outcome = { _, _ in
preflightCalls.value += 1
return .allowed
}
let synced = KeychainAccessPreflight.withCheckGenericPasswordOverrideForTesting(
preflightOverride,
operation: {
ClaudeOAuthKeychainReadStrategyPreference.withTaskOverrideForTesting(
.securityCLIExperimental,
operation: {
ClaudeOAuthKeychainPromptPreference.withTaskOverrideForTesting(
.onlyOnUserAction,
operation: {
ProviderInteractionContext.$current.withValue(.background) {
ClaudeOAuthCredentialsStore.withClaudeKeychainOverridesForTesting(
data: fallbackData,
fingerprint: nil)
{
ClaudeOAuthCredentialsStore.withSecurityCLIReadOverrideForTesting(
.timedOut)
{
ClaudeOAuthCredentialsStore
.syncFromClaudeKeychainWithoutPrompt(now: Date())
}
}
}
})
})
})
#expect(synced == false)
#expect(preflightCalls.value == 0)
}
}
}
}
}