77 lines
2.5 KiB
Swift
77 lines
2.5 KiB
Swift
import Foundation
|
|
|
|
enum OrphanSafetyPolicy {
|
|
private static let home = FileManager.default.homeDirectoryForCurrentUser.path
|
|
|
|
// Conservative allowlist: only volatile data directories.
|
|
static let allowedRoots: [String] = [
|
|
"\(home)/Library/Caches",
|
|
"\(home)/Library/Logs",
|
|
"\(home)/Library/Saved Application State",
|
|
"\(home)/Library/HTTPStorages",
|
|
"\(home)/Library/WebKit",
|
|
"\(home)/Library/Application Support/CrashReporter",
|
|
"/Library/Caches",
|
|
"/Library/Logs",
|
|
]
|
|
|
|
private static let blockedFragments: [String] = [
|
|
"/Library/Preferences",
|
|
"/Library/PreferencePanes",
|
|
"/Library/Containers",
|
|
"/Library/Group Containers",
|
|
"/Library/Application Scripts",
|
|
"/Library/LaunchAgents",
|
|
"/Library/LaunchDaemons",
|
|
"/Library/PrivilegedHelperTools",
|
|
"/Library/Keychains",
|
|
"/Library/Mail",
|
|
"/Library/Safari",
|
|
"/Library/Messages",
|
|
"/Library/Calendars",
|
|
"/Library/Accounts",
|
|
"/Library/Mobile Documents",
|
|
"/Library/CloudStorage",
|
|
]
|
|
|
|
static func isSafeCandidate(_ url: URL) -> Bool {
|
|
let path = normalizedPath(url)
|
|
let lowerPath = path.lowercased()
|
|
|
|
// Belt-and-suspenders: reject any high-risk home dotpath (defined in
|
|
// Conditions.swift). The allowedRoots filter below would catch most
|
|
// of these, but this early block keeps the rule obvious.
|
|
for root in highRiskHomeDotPaths {
|
|
if path == root || path.hasPrefix(root + "/") {
|
|
return false
|
|
}
|
|
}
|
|
|
|
// Require the match to land STRICTLY inside the allowed root, not at a
|
|
// sibling like /tmpfoo. Trailing "/" prevents hasPrefix from matching
|
|
// sibling directories whose names merely start with the root name.
|
|
guard allowedRoots.contains(where: { root in
|
|
let rootWithSlash = root.lowercased() + "/"
|
|
return lowerPath.hasPrefix(rootWithSlash)
|
|
}) else {
|
|
return false
|
|
}
|
|
|
|
if blockedFragments.contains(where: { lowerPath.contains($0.lowercased()) }) {
|
|
return false
|
|
}
|
|
|
|
let name = url.lastPathComponent.lowercased()
|
|
if name.hasPrefix("com.apple.") || name == ".globalpreferences.plist" {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
private static func normalizedPath(_ url: URL) -> String {
|
|
let standardized = url.standardizedFileURL
|
|
return standardized.resolvingSymlinksInPath().path
|
|
}
|
|
}
|