style: enforce safe CF casting

This commit is contained in:
Peter Steinberger 2025-11-13 01:30:56 +00:00
parent 7aa64423d8
commit eed1ead6fb
9 changed files with 51 additions and 145 deletions

View File

@ -34,15 +34,14 @@ extension AXUIElement {
AXAttributeNames.kAXFocusedApplicationAttribute as CFString,
&focusedApp)
guard error == .success, let app = focusedApp, CFGetTypeID(app) == AXUIElementGetTypeID(),
let axApp = app as? AXUIElement else {
guard error == .success, let app = focusedApp, CFGetTypeID(app) == AXUIElementGetTypeID() else {
let errorDetails = "AXError: \(error.rawValue) - \(error.localizedDescription)"
axErrorLog("Failed to get focused application: \(errorDetails)")
throw AccessibilityError.attributeNotReadable(
attribute: AXAttributeNames.kAXFocusedApplicationAttribute,
elementDescription: "SystemWideElement")
}
return axApp
return unsafeDowncast(app, to: AXUIElement.self)
}
/// Returns the frontmost application using NSWorkspace
@ -103,12 +102,11 @@ extension AXUIElement {
guard error == .success,
let window = focusedWindow,
CFGetTypeID(window) == AXUIElementGetTypeID(),
let axWindow = window as? AXUIElement else {
CFGetTypeID(window) == AXUIElementGetTypeID() else {
return nil
}
return axWindow
return unsafeDowncast(window, to: AXUIElement.self)
}
/// Returns the main window in the frontmost application
@ -126,12 +124,11 @@ extension AXUIElement {
guard error == .success,
let window = mainWindow,
CFGetTypeID(window) == AXUIElementGetTypeID(),
let axWindow = window as? AXUIElement else {
CFGetTypeID(window) == AXUIElementGetTypeID() else {
return nil
}
return axWindow
return unsafeDowncast(window, to: AXUIElement.self)
}
/// Returns all windows for the specified application

View File

@ -243,11 +243,13 @@ extension Element {
underlyingElement,
AXAttributeNames.kAXURLAttribute as CFString,
&value)
guard error == .success,
let cfURL = value as? CFURL else {
guard error == .success, let cfValue = value else {
return nil
}
return cfURL as URL
guard CFGetTypeID(cfValue) == CFURLGetTypeID() else {
return nil
}
return (cfValue as! CFURL) as URL
}
// MARK: - System-Wide Element Attributes

View File

@ -55,18 +55,9 @@ extension Element {
private func convertToString(_ cfValue: CFTypeRef, cfTypeID: CFTypeID) -> String? {
if cfTypeID == CFStringGetTypeID() {
guard let cfString = cfValue as? CFString else {
GlobalAXLogger.shared.log(
AXLogEntry(level: .warning, message: "Failed to cast CFTypeRef to CFString"))
return nil
}
return cfString as String
return (cfValue as! CFString) as String
} else if cfTypeID == CFAttributedStringGetTypeID() {
guard let attrString = cfValue as? NSAttributedString else {
GlobalAXLogger.shared.log(
AXLogEntry(level: .warning, message: "Failed to cast CFTypeRef to NSAttributedString"))
return nil
}
let attrString = cfValue as! NSAttributedString
return attrString.string
}
return nil
@ -74,11 +65,7 @@ extension Element {
private func convertToBool(_ cfValue: CFTypeRef, cfTypeID: CFTypeID) -> Bool? {
if cfTypeID == CFBooleanGetTypeID() {
guard let cfBool = cfValue as? CFBoolean else {
GlobalAXLogger.shared.log(
AXLogEntry(level: .warning, message: "Failed to cast CFTypeRef to CFBoolean"))
return nil
}
let cfBool = unsafeDowncast(cfValue, to: CFBoolean.self)
return CFBooleanGetValue(cfBool)
}
return nil
@ -86,14 +73,10 @@ extension Element {
private func convertToInt(_ cfValue: CFTypeRef, cfTypeID: CFTypeID) -> Int? {
if cfTypeID == CFNumberGetTypeID() {
guard let cfNumber = cfValue as? CFNumber else {
GlobalAXLogger.shared.log(
AXLogEntry(level: .warning, message: "Failed to cast CFTypeRef to CFNumber"))
return nil
}
var intValue = 0
let cfNumber = unsafeDowncast(cfValue, to: CFNumber.self)
var intValue = Int64(0)
if CFNumberGetValue(cfNumber, .sInt64Type, &intValue) {
return intValue
return Int(intValue)
}
}
return nil
@ -101,12 +84,7 @@ extension Element {
private func convertToAXUIElement(_ cfValue: CFTypeRef, cfTypeID: CFTypeID) -> AXUIElement? {
if cfTypeID == AXUIElementGetTypeID() {
guard let element = cfValue as? AXUIElement else {
GlobalAXLogger.shared.log(
AXLogEntry(level: .warning, message: "Failed to cast CFTypeRef to AXUIElement"))
return nil
}
return element
return unsafeDowncast(cfValue, to: AXUIElement.self)
}
return nil
}

View File

@ -22,11 +22,7 @@ func convertCFValueToSwift(_ cfValue: CFTypeRef?) -> Any? {
case CFDictionaryGetTypeID():
return convertCFDictionary(cfValue)
case AXUIElementGetTypeID():
guard let element = cfValue as? AXUIElement else {
axWarningLog("Expected AXUIElement but received \(cfValue)")
return nil
}
return element
return unsafeDowncast(cfValue, to: AXUIElement.self)
// Add other common CF types if necessary, e.g., CFURL, CFDate
default:
axDebugLog("Unhandled CFTypeRef in convertCFValueToSwift: typeID \(typeID). Value: \(cfValue)")

View File

@ -10,29 +10,18 @@ func formatRawCFValueForTextContent(_ rawValue: CFTypeRef?) async -> String {
guard let value = rawValue else { return AXMiscConstants.kAXNotAvailableString }
let typeID = CFGetTypeID(value)
if typeID == CFStringGetTypeID() {
guard let stringValue = value as? String else {
return AXMiscConstants.kAXNotAvailableString
}
return stringValue
return value as! String
} else if typeID == CFAttributedStringGetTypeID() {
guard let attributedString = value as? NSAttributedString else {
return AXMiscConstants.kAXNotAvailableString
}
let attributedString = value as! NSAttributedString
return attributedString.string
} else if typeID == AXValueGetTypeID() {
guard let axValue = value as? AXValue else {
return AXMiscConstants.kAXNotAvailableString
}
let axValue = unsafeDowncast(value, to: AXValue.self)
return formatAXValue(axValue, option: ValueFormatOption.smart)
} else if typeID == CFNumberGetTypeID() {
guard let number = value as? NSNumber else {
return AXMiscConstants.kAXNotAvailableString
}
let number = value as! NSNumber
return number.stringValue
} else if typeID == CFBooleanGetTypeID() {
guard let boolValue = value as? CFBoolean else {
return AXMiscConstants.kAXNotAvailableString
}
let boolValue = unsafeDowncast(value, to: CFBoolean.self)
return CFBooleanGetValue(boolValue) ? "true" : "false"
} else {
let typeDesc = CFCopyTypeIDDescription(typeID) as String? ?? "ComplexType"

View File

@ -137,7 +137,7 @@ func castToAXUIElementArray(_ value: Any, attr: String) -> [AXUIElement]? {
let result = anyArray.compactMap { item -> AXUIElement? in
guard let cfItem = item else { return nil }
if CFGetTypeID(cfItem as CFTypeRef) == AXUIElementGetTypeID() {
return cfItem as? AXUIElement
return unsafeDowncast(cfItem as AnyObject, to: AXUIElement.self)
}
return nil
}
@ -157,9 +157,7 @@ func castToElementArray(_ value: Any, attr: String) -> [Element]? {
let result = anyArray.compactMap { item -> Element? in
guard let cfItem = item else { return nil }
if CFGetTypeID(cfItem as CFTypeRef) == AXUIElementGetTypeID() {
guard let axElement = cfItem as? AXUIElement else {
return nil
}
let axElement = unsafeDowncast(cfItem as AnyObject, to: AXUIElement.self)
return Element(axElement) // Assumes Element initializer is public/internal
}
return nil
@ -239,7 +237,7 @@ func castToSpecialType<T>(_ value: Any, expectedType: T.Type, attr: String) -> T
@MainActor
func castToAXUIElement(_ value: Any, attr: String) -> AXUIElement? {
if let cfValue = value as CFTypeRef?, CFGetTypeID(cfValue) == AXUIElementGetTypeID() {
return cfValue as? AXUIElement
return unsafeDowncast(cfValue, to: AXUIElement.self)
}
let typeDescription = String(describing: type(of: value))
let valueDescription = String(describing: value)

View File

@ -39,30 +39,19 @@ private func formatCFTypeByID(
value,
option: option)
case AXValueGetTypeID():
guard let axValue = value as? AXValue else {
axWarningLog("formatCFTypeByID: Expected AXValue but received \(value).")
return "<Invalid AXValue>"
}
let axValue = unsafeDowncast(value, to: AXValue.self)
return formatAXValue(axValue, option: option)
case CFStringGetTypeID():
guard let stringValue = value as? String else {
return "<Invalid String>"
}
let stringValue = value as! String
return "\"\(escapeStringForDisplay(stringValue))\""
case CFAttributedStringGetTypeID():
guard let attributedString = value as? NSAttributedString else {
return "<Invalid AttributedString>"
}
let attributedString = value as! NSAttributedString
return "\"\(escapeStringForDisplay(attributedString.string))\""
case CFBooleanGetTypeID():
guard let boolValue = value as? CFBoolean else {
return "<Invalid Boolean>"
}
let boolValue = unsafeDowncast(value, to: CFBoolean.self)
return CFBooleanGetValue(boolValue) ? "true" : "false"
case CFNumberGetTypeID():
guard let number = value as? NSNumber else {
return "<Invalid Number>"
}
let number = value as! NSNumber
return number.stringValue
case CFArrayGetTypeID():
return formatCFArray(value, option: option)
@ -83,12 +72,9 @@ private func formatCFTypeByID(
@MainActor
private func formatAXUIElement(
_ value: CFTypeRef,
option: ValueFormatOption, // Changed from SimpleValueFormatOption
) -> String {
guard let axElement = value as? AXUIElement else {
axWarningLog("formatAXUIElement: Failed to cast CFTypeRef to AXUIElement.")
return "<Invalid AXUIElement>"
}
option: ValueFormatOption) -> String
{
let axElement = unsafeDowncast(value, to: AXUIElement.self)
let element = Element(axElement)
// Element.role() and .title() will use GlobalAXLogger internally
@ -108,11 +94,9 @@ private func formatAXUIElement(
@MainActor
private func formatCFArray(
_ value: CFTypeRef,
option: ValueFormatOption, // Changed from SimpleValueFormatOption
) -> String {
guard let cfArray = value as? CFArray else {
return "<Invalid Array>"
}
option: ValueFormatOption) -> String
{
let cfArray = unsafeDowncast(value, to: CFArray.self)
let count = CFArrayGetCount(cfArray)
// Adjust logic based on ValueFormatOption cases
@ -137,11 +121,9 @@ private func formatCFArray(
@MainActor
private func formatCFDictionary(
_ value: CFTypeRef,
option: ValueFormatOption, // Changed from SimpleValueFormatOption
) -> String {
guard let cfDict = value as? CFDictionary else {
return "<Invalid Dictionary>"
}
option: ValueFormatOption) -> String
{
let cfDict = unsafeDowncast(value, to: CFDictionary.self)
let count = CFDictionaryGetCount(cfDict)
// Adjust logic based on ValueFormatOption cases

View File

@ -43,10 +43,7 @@ public func getAXValueTypeForAttribute(element: Element, attributeName: String)
return nil
}
guard let axValue = rawValue as? AXValue else {
axWarningLog("getAXValueTypeForAttribute: Expected AXValue but received \(rawValue).")
return nil
}
let axValue = unsafeDowncast(rawValue, to: AXValue.self)
return AXValueGetType(axValue)
}
@ -85,13 +82,7 @@ private func convertStringValue(
switch typeID {
case AXValueGetTypeID():
guard let axValue = currentValue as? AXValue else {
let detail = "Attribute '\(attributeName)' reported AXValue type but casting failed."
axErrorLog(detail, file: #file, function: #function, line: #line)
throw AccessibilityError.attributeUnsupported(
attribute: detail,
elementDescription: element.briefDescription())
}
let axValue = unsafeDowncast(currentValue, to: AXValue.self)
let axValueType = AXValueGetType(axValue)
axDebugLog(
"Attribute '\(attributeName)' is AXValue of type: \(stringFromAXValueType(axValueType))",

View File

@ -28,37 +28,19 @@ enum ValueUnwrapper {
{
switch typeID {
case ApplicationServices.AXUIElementGetTypeID():
guard let element = value as? AXUIElement else {
axWarningLog("Failed to cast CFTypeRef to AXUIElement.")
return nil
}
return element
return unsafeDowncast(value, to: AXUIElement.self)
case ApplicationServices.AXValueGetTypeID():
return self.unwrapAXValue(value)
case CFStringGetTypeID():
guard let cfString = value as? CFString else {
axWarningLog("Failed to cast CFTypeRef to CFString.")
return nil
}
return cfString as String
return (value as! CFString) as String
case CFAttributedStringGetTypeID():
guard let attributedString = value as? NSAttributedString else {
axWarningLog("Failed to cast CFTypeRef to NSAttributedString.")
return nil
}
let attributedString = value as! NSAttributedString
return attributedString.string
case CFBooleanGetTypeID():
guard let cfBool = value as? CFBoolean else {
axWarningLog("Failed to cast CFTypeRef to CFBoolean.")
return nil
}
let cfBool = value as! CFBoolean
return CFBooleanGetValue(cfBool)
case CFNumberGetTypeID():
guard let number = value as? NSNumber else {
axWarningLog("Failed to cast CFTypeRef to NSNumber.")
return nil
}
return number
return value as! NSNumber
case CFArrayGetTypeID():
return self.unwrapCFArray(value)
case CFDictionaryGetTypeID():
@ -75,10 +57,7 @@ enum ValueUnwrapper {
private static func unwrapAXValue(
_ value: CFTypeRef) -> Any?
{
guard let axVal = value as? AXValue else {
axWarningLog("Failed to cast CFTypeRef to AXValue.")
return nil
}
let axVal = unsafeDowncast(value, to: AXValue.self)
let axValueType = axVal.valueType
// Log the AXValueType
@ -109,10 +88,7 @@ enum ValueUnwrapper {
private static func unwrapCFArray(
_ value: CFTypeRef) -> [Any?]
{
guard let cfArray = value as? CFArray else {
axWarningLog("Failed to cast CFTypeRef to CFArray.")
return []
}
let cfArray = unsafeDowncast(value, to: CFArray.self)
var swiftArray: [Any?] = []
for index in 0..<CFArrayGetCount(cfArray) {
@ -130,10 +106,7 @@ enum ValueUnwrapper {
private static func unwrapCFDictionary(
_ value: CFTypeRef) -> [String: Any?]
{
guard let cfDict = value as? CFDictionary else {
axWarningLog("Failed to cast CFTypeRef to CFDictionary.")
return [:]
}
let cfDict = unsafeDowncast(value, to: CFDictionary.self)
var swiftDict: [String: Any?] = [:]
if let nsDict = cfDict as? [String: AnyObject] {