Skip to content

Commit c51c3bf

Browse files
gi11esclaude
andcommitted
feat: add FullDiskAccessChecker to probe FDA-protected paths
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
1 parent 65bc1d9 commit c51c3bf

File tree

3 files changed

+49
-0
lines changed

3 files changed

+49
-0
lines changed

Deckard.xcodeproj/project.pbxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828

2929
AABB0004AABB0004AABB0004 /* ShortcutNames.swift in Sources */ = {isa = PBXBuildFile; fileRef = AABB0005AABB0005AABB0005 /* ShortcutNames.swift */; };
3030
AABB0006AABB0006AABB0006 /* DeckardHooksInstaller.swift in Sources */ = {isa = PBXBuildFile; fileRef = AABB0007AABB0007AABB0007 /* DeckardHooksInstaller.swift */; };
31+
FDAC0001FDAC0001FDAC0001 /* FullDiskAccessChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDAC0002FDAC0002FDAC0002 /* FullDiskAccessChecker.swift */; };
32+
FDAC0003FDAC0003FDAC0003 /* FullDiskAccessCheckerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDAC0004FDAC0004FDAC0004 /* FullDiskAccessCheckerTests.swift */; };
3133
CC110001CC110001CC110001 /* ThemeColors.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC110002CC110002CC110002 /* ThemeColors.swift */; };
3234
TM000001TM000001TM000001 /* ThemeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = TM000002TM000002TM000002 /* ThemeManager.swift */; };
3335
TC000001TC000001TC000001 /* TerminalColorScheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = TC000002TC000002TC000002 /* TerminalColorScheme.swift */; };
@@ -78,6 +80,8 @@
7880
E5FD7F070DDB4216278EFDE6 /* ContextMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextMonitor.swift; sourceTree = "<group>"; };
7981
AABB0005AABB0005AABB0005 /* ShortcutNames.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortcutNames.swift; sourceTree = "<group>"; };
8082
AABB0007AABB0007AABB0007 /* DeckardHooksInstaller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeckardHooksInstaller.swift; sourceTree = "<group>"; };
83+
FDAC0002FDAC0002FDAC0002 /* FullDiskAccessChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullDiskAccessChecker.swift; sourceTree = "<group>"; };
84+
FDAC0004FDAC0004FDAC0004 /* FullDiskAccessCheckerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullDiskAccessCheckerTests.swift; sourceTree = "<group>"; };
8185
CC110002CC110002CC110002 /* ThemeColors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeColors.swift; sourceTree = "<group>"; };
8286
TM000002TM000002TM000002 /* ThemeManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeManager.swift; sourceTree = "<group>"; };
8387
TC000002TC000002TC000002 /* TerminalColorScheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalColorScheme.swift; sourceTree = "<group>"; };
@@ -237,6 +241,7 @@
237241
88AA91CDBC6DDE958543FB29 /* main.swift */,
238242
AABB0005AABB0005AABB0005 /* ShortcutNames.swift */,
239243
AABB0007AABB0007AABB0007 /* DeckardHooksInstaller.swift */,
244+
FDAC0002FDAC0002FDAC0002 /* FullDiskAccessChecker.swift */,
240245
);
241246
path = App;
242247
sourceTree = "<group>";
@@ -250,6 +255,7 @@
250255
AA1E0002AA1E0002AA1E0002 /* ControlSocketTests.swift */,
251256
AA1C0002AA1C0002AA1C0002 /* DeckardHooksInstallerTests.swift */,
252257
AA160002AA160002AA160002 /* DiagnosticLogTests.swift */,
258+
FDAC0004FDAC0004FDAC0004 /* FullDiskAccessCheckerTests.swift */,
253259
AA170002AA170002AA170002 /* HookHandlerTests.swift */,
254260
AA180002AA180002AA180002 /* ProcessMonitorTests.swift */,
255261
QA200005QA200005QA200005 /* QuotaMonitorTests.swift */,
@@ -406,6 +412,7 @@
406412
432752F9187D2E815F617B96 /* main.swift in Sources */,
407413
AABB0004AABB0004AABB0004 /* ShortcutNames.swift in Sources */,
408414
AABB0006AABB0006AABB0006 /* DeckardHooksInstaller.swift in Sources */,
415+
FDAC0001FDAC0001FDAC0001 /* FullDiskAccessChecker.swift in Sources */,
409416
CC110001CC110001CC110001 /* ThemeColors.swift in Sources */,
410417
TM000001TM000001TM000001 /* ThemeManager.swift in Sources */,
411418
TC000001TC000001TC000001 /* TerminalColorScheme.swift in Sources */,
@@ -428,6 +435,7 @@
428435
AA140001AA140001AA140001 /* ThemeManagerTests.swift in Sources */,
429436
AA150001AA150001AA150001 /* ControlMessageTests.swift in Sources */,
430437
AA160001AA160001AA160001 /* DiagnosticLogTests.swift in Sources */,
438+
FDAC0003FDAC0003FDAC0003 /* FullDiskAccessCheckerTests.swift in Sources */,
431439
AA170001AA170001AA170001 /* HookHandlerTests.swift in Sources */,
432440
AA180001AA180001AA180001 /* ProcessMonitorTests.swift in Sources */,
433441
AA190001AA190001AA190001 /* ContextMonitorTests.swift in Sources */,
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import AppKit
2+
3+
enum FullDiskAccessChecker {
4+
/// Probes known FDA-protected paths. Returns `true` when any path is
5+
/// readable (i.e. FDA has been granted). The probe itself causes macOS
6+
/// to register Deckard in System Settings > Full Disk Access.
7+
static func hasFullDiskAccess() -> Bool {
8+
let home = FileManager.default.homeDirectoryForCurrentUser.path
9+
let protectedPaths = [
10+
home + "/Library/Safari/Bookmarks.plist",
11+
home + "/Library/Safari/CloudTabs.db",
12+
home + "/Library/Mail",
13+
]
14+
return protectedPaths.contains {
15+
FileManager.default.isReadableFile(atPath: $0)
16+
}
17+
}
18+
19+
/// Opens System Settings directly to the Full Disk Access pane.
20+
static func openSettings() {
21+
guard let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles") else { return }
22+
NSWorkspace.shared.open(url)
23+
}
24+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import XCTest
2+
@testable import Deckard
3+
4+
final class FullDiskAccessCheckerTests: XCTestCase {
5+
func testHasFullDiskAccessReturnsBool() {
6+
// Smoke test — the function runs without crashing and returns a Bool.
7+
// The actual result depends on the test host's FDA status.
8+
let result = FullDiskAccessChecker.hasFullDiskAccess()
9+
XCTAssertNotNil(result as Bool)
10+
}
11+
12+
func testOpenSettingsURLIsValid() {
13+
// Verify the URL string parses correctly.
14+
let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles")
15+
XCTAssertNotNil(url)
16+
}
17+
}

0 commit comments

Comments
 (0)