Skip to content
Open
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 @@ -143,3 +143,7 @@ grep -nE "O_NOFOLLOW|O_CLOEXEC|fchmod|withUnsafeFileSystemRepresentation" <candi
**Vulnerability:** Calling `open(2)` with raw Swift `String` paths in `StatusSocket.swift` creates an unsafe filesystem representation.
**Learning:** Passing Swift `String` paths implicitly to C functions can result in memory issues or incorrect path resolution if the string is not null-terminated or is moved in memory.
**Prevention:** Always use `URL(fileURLWithPath:).withUnsafeFileSystemRepresentation` to obtain the correct C-string pointer when bridging file paths to POSIX APIs.
## 2026-05-04 - Fix fail-open POSIX permission handling
**Vulnerability:** Ignored return values from POSIX C APIs (`fchmod`, `fchmodat`) caused fail-open behavior.
**Learning:** While high-level Swift APIs inherently fail-closed by throwing errors, dropping down to C APIs to avoid TOCTOU introduces the risk of silently ignoring failures.
**Prevention:** Always explicitly check the integer return codes of POSIX C APIs and securely fail-closed on error.
5 changes: 4 additions & 1 deletion Sources/Cacheout/Headless/DaemonMode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -981,7 +981,10 @@ public actor DaemonMode: StatusSocket.DataSource {
guard let pathPtr = pathPtr else { return nil }
let fd = open(pathPtr, O_RDONLY | O_NOFOLLOW | O_CLOEXEC)
guard fd >= 0 else { return nil }
fchmod(fd, 0o600)
guard fchmod(fd, 0o600) == 0 else {
close(fd)
return nil
}
let handle = FileHandle(fileDescriptor: fd, closeOnDealloc: true)
return try? handle.readToEnd()
}
Expand Down
6 changes: 5 additions & 1 deletion Sources/Cacheout/Headless/StatusSocket.swift
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,11 @@ public final class StatusSocket: @unchecked Sendable {
}
if (sockStat.st_mode & 0o777) != 0o600 {
// umask should have set 0600; defense-in-depth chmod via no-follow.
_ = fchmodat(AT_FDCWD, socketPath, 0o600, AT_SYMLINK_NOFOLLOW)
guard fchmodat(AT_FDCWD, socketPath, 0o600, AT_SYMLINK_NOFOLLOW) == 0 else {
close(fd)
unlink(socketPath)
throw StatusSocketError.bindFailed(errno)
}
}

// Listen
Expand Down
Loading