import Foundation enum FileValidationError: LocalizedError { case pathTraversal(String) case emptyPath case exceedsMaxSize(Int, Int) case unsupportedBinaryExtension(String) case reservedSystemPath(String) var errorDescription: String? { switch self { case .pathTraversal(let p): return "Path traversal in '\(p)'" case .emptyPath: return "File path is empty" case .exceedsMaxSize(let a, let l): return "File \(a/1_048_576)MB exceeds \(l/1_048_576)MB limit" case .unsupportedBinaryExtension(let e): return ".\(e) is binary and cannot be text-edited" case .reservedSystemPath(let p): return "'\(p)' is a system path" } } } enum FileOperationValidator { private static let maxBytes: Int = 10 * 1_048_576 private static let binaryExtensions: Set = [ "png","jpg","jpeg","gif","webp","pdf","zip","tar","gz", "ipa","a","dylib","framework","app","xcassets","car","nib", "storyboard","mp3","mp4","mov" ] private static let reservedPrefixes = ["/System/","/usr/","/bin/","/sbin/","/private/var/db/"] static func validateSave(path: String, content: String) throws { try validatePath(path) guard content.utf8.count <= maxBytes else { throw FileValidationError.exceedsMaxSize(content.utf8.count, maxBytes) } } static func validatePath(_ path: String) throws { guard !path.isEmpty else { throw FileValidationError.emptyPath } if path.contains("../") || path.contains("/..") { throw FileValidationError.pathTraversal(path) } for prefix in reservedPrefixes where path.hasPrefix(prefix) { throw FileValidationError.reservedSystemPath(path) } let ext = (path as NSString).pathExtension.lowercased() if binaryExtensions.contains(ext) { throw FileValidationError.unsupportedBinaryExtension(ext) } } }