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 @@ -12,3 +12,7 @@
**Vulnerability:** A process can block (deadlock) when its stdout/stderr pipe fills before the parent reads it, because the child blocks on `write()` while the parent blocks on `waitUntilExit()`.
**Learning:** `pipe.fileHandleForReading.readDataToEndOfFile()` after `process.waitUntilExit()` is the deadlock pattern. Default macOS pipe buffer is ~64KB.
**Prevention:** Read the pipe before/concurrently-with waiting for exit. The simplest pattern is to perform the read inside the same background queue that calls `waitUntilExit()`, capturing the bytes for the caller to use after the dispatch group resolves.
## 2024-05-16 - Webhook Insecure HTTP Scheme Allowed
**Vulnerability:** Autopilot configuration allowed `http` schemes for webhook URLs, risking insecure unencrypted transmission of sensitive daemon alerts.
**Learning:** Validation logic incorrectly whitelisted both `http` and `https`, likely copy-pasted from generic URL validation instead of enforcing secure-by-default for webhooks.
**Prevention:** Always enforce `https` scheme for outbound webhooks handling alert payloads and align validation logic with parsing logic.
4 changes: 2 additions & 2 deletions Sources/Cacheout/Headless/StatusSocket.swift
Original file line number Diff line number Diff line change
Expand Up @@ -708,8 +708,8 @@ public enum AutopilotConfigValidator {
if let urlStr = webhook["url"] as? String {
if let url = URL(string: urlStr) {
let scheme = url.scheme?.lowercased() ?? ""
if scheme != "http" && scheme != "https" {
errors.append("webhook: url must use http or https scheme, got '\(scheme)'")
if scheme != "https" {
errors.append("webhook: url must use https scheme for security, got '\(scheme)'")
}
if url.host == nil || url.host?.isEmpty == true {
errors.append("webhook: url must be an absolute URL with a host")
Expand Down
3 changes: 2 additions & 1 deletion Sources/Cacheout/Headless/WebhookAlerter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,8 @@ extension WebhookAlerter.WebhookConfig {
public static func parse(from json: [String: Any]) -> WebhookAlerter.WebhookConfig? {
guard let webhook = json["webhook"] as? [String: Any],
let urlStr = webhook["url"] as? String,
let url = URL(string: urlStr) else {
let url = URL(string: urlStr),
url.scheme?.lowercased() == "https" else {
return nil
}
let format = webhook["format"] as? String ?? "generic"
Expand Down
12 changes: 12 additions & 0 deletions Tests/CacheoutTests/HeadlessTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,18 @@ final class AutopilotConfigValidatorTests: XCTestCase {
XCTAssertTrue(errors.contains { $0.contains("webhook: missing") && $0.contains("timeout_s") })
}

func testWebhookInsecureURLRejected() {
let json = """
{
"version": 1,
"enabled": true,
"webhook": {"url": "http://x.com", "format": "generic", "timeout_s": 5}
}
""".data(using: .utf8)!
let errors = AutopilotConfigValidator.validate(data: json)
XCTAssertTrue(errors.contains { $0.contains("https scheme") })
}

func testWebhookInvalidFormat() {
let json = """
{
Expand Down