fix(testkit): bound probe output#451
Conversation
📝 WalkthroughWalkthroughTestKitRunner now limits captured probe output and serialized JSON request bodies. Probe execution reports output-limit violations, terminates oversized processes, and preserves eligible timeout output. New tests cover exact-limit, oversized, timeout, termination, and request-body cases. ChangesTestKitRunner size limits
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant TestKitRunner
participant ProbeProcess
participant StdoutPipe
TestKitRunner->>ProbeProcess: Launch probe
ProbeProcess->>StdoutPipe: Produce stdout chunks
TestKitRunner->>StdoutPipe: Read bounded chunks
TestKitRunner->>ProbeProcess: Terminate on output overflow
TestKitRunner-->>TestKitRunner: Return ProbeRunResult
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
Sources/WhatCable/Services/TestKitRunner.swift (1)
290-294: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low valueTerminate the process immediately on read failure to avoid a timeout-driven delay.
If
pipe.fileHandleForReading.readthrows an error, thewhileloop exits, but the process may still be running. It will likely block once its stdout pipe buffer fills, causingprocess.waitUntilExit()to hang until the 30-second watchdog timer finally fires. Terminating the process here prevents this unnecessary delay.♻️ Proposed refactor
} catch { didFailReading = true Self.log.error("Failed to read probe output: \(error.localizedDescription)") + if process.isRunning { + process.terminate() + } } process.waitUntilExit()🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Sources/WhatCable/Services/TestKitRunner.swift` around lines 290 - 294, Update the read-error catch block in the probe-output loop to terminate the running process immediately after setting didFailReading, before process.waitUntilExit() is reached. Use the existing process instance and preserve the current error logging and failure-state handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@Sources/WhatCable/Services/TestKitRunner.swift`:
- Around line 295-298: Update the output conversion in TestKitRunner’s probe
result construction to use resilient UTF-8 decoding via String(decoding:as:)
instead of String(data:encoding:), while preserving the existing nil behavior
when didExceedOutputLimit or didFailReading is true. This must retain partial
output and replace any incomplete UTF-8 bytes rather than discarding the output.
---
Nitpick comments:
In `@Sources/WhatCable/Services/TestKitRunner.swift`:
- Around line 290-294: Update the read-error catch block in the probe-output
loop to terminate the running process immediately after setting didFailReading,
before process.waitUntilExit() is reached. Use the existing process instance and
preserve the current error logging and failure-state handling.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 5278953b-e838-4c2a-b700-0c8beacad29a
📒 Files selected for processing (2)
Sources/WhatCable/Services/TestKitRunner.swiftTests/WhatCableDarwinTests/TestKitRunnerOutputLimitTests.swift
|
Thanks @VailElla, this was a well-put-together PR. The unbounded Merged with a few adaptations on the way in. This repo is a mirror of a private development repo, so your change lands on main via a mirror commit rather than the squash directly, and the test file moved to a different test target to match the internal layout. While reviewing we also added SIGTERM-to-SIGKILL escalation (a probe that ignores SIGTERM could previously hold the runner hostage even with your cap in place), a per-chunk autorelease pool in the drain loop, and timing assertions on the watchdog tests. Ships in the next release. |
|
Thanks for the thoughtful review and for merging this! The additional hardening makes a lot of sense. Glad to see it shipping in the next release! |
Summary
Root cause
TestKitRunnerusedreadDataToEndOfFile(), retained all probe output, then copied it into aStringand JSON request body. The 30-second watchdog bounded runtime but not memory usage.Security invariant
No probe can cause the parent to retain more than 4 MiB of output or send more than an 8 MiB encoded request body. Oversized output is rejected rather than truncated and uploaded.
Validation
WhatCable: passedwhatcable-cli: passedresearch/customer-probesandresearch/dumpsare absent from this public mirrorReview