51 lines
2 KiB
Swift
51 lines
2 KiB
Swift
|
|
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<String> = [
|
||
|
|
"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)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|