Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,7 @@
**Learning:** High-level Swift file writing APIs do not natively protect against malicious symlinks in untrusted directories, potentially allowing unintended files to be overwritten or appended to.
**Prevention:** Always use POSIX `open(2)` with `O_CREAT | O_WRONLY | O_APPEND | O_NOFOLLOW | O_CLOEXEC` to securely refuse symlink traversal, then wrap the resulting file descriptor in a `FileHandle`. Ensure directories are also securely created using `.posixPermissions`.

## 2026-05-02 - Insecure File Permissions Update
**Vulnerability:** Found insecure permission modification that could be redirected.
**Learning:** Permission modification APIs that use string paths can be redirected via symlinks.
**Prevention:** Always use POSIX open() with O_NOFOLLOW and apply permissions with fchmod().
20 changes: 16 additions & 4 deletions Sources/Cacheout/Headless/DaemonMode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -295,10 +295,22 @@ public actor DaemonMode: StatusSocket.DataSource {
withIntermediateDirectories: true,
attributes: [.posixPermissions: 0o700]
)
try FileManager.default.setAttributes(
[.posixPermissions: 0o700],
ofItemAtPath: config.stateDir.path
)
// SECURITY: Use open with O_NOFOLLOW and fchmod to prevent TOCTOU symlink attacks
// when setting permissions on the state directory.
struct SecureOperationError: Error {}
let dirFd = config.stateDir.withUnsafeFileSystemRepresentation { pathPtr -> Int32 in
guard let pathPtr = pathPtr else { return -1 }
return open(pathPtr, O_RDONLY | O_NOFOLLOW | O_DIRECTORY | O_CLOEXEC)
}
if dirFd >= 0 {
if fchmod(dirFd, 0o700) != 0 {
close(dirFd)
throw SecureOperationError()
}
close(dirFd)
} else {
throw SecureOperationError()
}
} catch {
logger.error("Failed to create/secure state directory: \(error.localizedDescription, privacy: .public)")
Foundation.exit(1)
Expand Down
Loading