import CoreGraphics import Foundation /// Security utilities for AppleScript execution enum AppleScriptSecurity { /// Escapes a string for safe use in AppleScript /// /// This function properly escapes all special characters that could be used /// for AppleScript injection attacks, including: /// - Double quotes (") /// - Backslashes (\) /// - Newlines and carriage returns /// - Tabs /// - Other control characters /// /// - Parameter string: The string to escape /// - Returns: The escaped string safe for use in AppleScript static func escapeString(_ string: String) -> String { var escaped = string // Order matters: escape backslashes first escaped = escaped.replacingOccurrences(of: "\\", with: "\\\\") escaped = escaped.replacingOccurrences(of: "\"", with: "\\\"") escaped = escaped.replacingOccurrences(of: "\n", with: "\\n") escaped = escaped.replacingOccurrences(of: "\r", with: "\\r") escaped = escaped.replacingOccurrences(of: "\t", with: "\\t") // Remove any other control characters that could cause issues let controlCharacterSet = CharacterSet.controlCharacters escaped = escaped.components(separatedBy: controlCharacterSet) .joined(separator: " ") return escaped } /// Validates an identifier (like an application name) for safe use in AppleScript /// /// This function ensures the identifier only contains safe characters and /// isn't trying to inject AppleScript commands. /// /// - Parameter identifier: The identifier to validate /// - Returns: The validated identifier, or nil if invalid static func validateIdentifier(_ identifier: String) -> String? { // Allow alphanumeric, spaces, dots, hyphens, and underscores let allowedCharacterSet = CharacterSet(charactersIn: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 .-_") let identifierCharacterSet = CharacterSet(charactersIn: identifier) guard allowedCharacterSet.isSuperset(of: identifierCharacterSet) else { return nil } // Additional check: ensure it doesn't contain AppleScript keywords that could be dangerous let dangerousKeywords = ["tell", "end", "do", "script", "run", "activate", "quit", "delete", "set", "get"] let lowercased = identifier.lowercased() for keyword in dangerousKeywords where lowercased.contains(keyword) { return nil } return identifier } /// Escapes a numeric value for safe use in AppleScript /// /// - Parameter value: The numeric value /// - Returns: The string representation of the number static func escapeNumber(_ value: Int) -> String { String(value) } /// Escapes a numeric value for safe use in AppleScript /// /// - Parameter value: The numeric value (UInt32/CGWindowID) /// - Returns: The string representation of the number static func escapeNumber(_ value: UInt32) -> String { String(value) } /// Creates a safe AppleScript string literal /// /// - Parameter string: The string to make into a literal /// - Returns: A properly quoted and escaped AppleScript string literal static func createStringLiteral(_ string: String) -> String { "\"\(escapeString(string))\"" } }