Skip to content

Commit 169be1e

Browse files
committed
fix(timeout): inherit helper state and pass checks
1 parent 0d2f217 commit 169be1e

File tree

3 files changed

+21
-15
lines changed

3 files changed

+21
-15
lines changed

cmd/analyze/delete.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"os"
99
"os/exec"
1010
"path/filepath"
11+
"slices"
1112
"sort"
1213
"strings"
1314
"sync/atomic"
@@ -165,10 +166,8 @@ func validatePath(path string) error {
165166
return fmt.Errorf("path contains null bytes")
166167
}
167168
// Check for path traversal attempts (.. components).
168-
for _, component := range strings.Split(path, string(filepath.Separator)) {
169-
if component == ".." {
170-
return fmt.Errorf("path contains traversal components: %s", path)
171-
}
169+
if slices.Contains(strings.Split(path, string(filepath.Separator)), "..") {
170+
return fmt.Errorf("path contains traversal components: %s", path)
172171
}
173172
return nil
174173
}

cmd/status/metrics_disk.go

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -275,23 +275,22 @@ func getAPFSContainerFreeBytes(mountpoint string) (uint64, error) {
275275
}
276276

277277
const key = "<key>APFSContainerFree</key>"
278-
idx := strings.Index(out, key)
279-
if idx == -1 {
278+
_, rest, found := strings.Cut(out, key)
279+
if !found {
280280
return 0, fmt.Errorf("APFSContainerFree not found")
281281
}
282282

283-
rest := out[idx+len(key):]
284-
start := strings.Index(rest, "<integer>")
285-
if start == -1 {
283+
_, rest, found = strings.Cut(rest, "<integer>")
284+
if !found {
286285
return 0, fmt.Errorf("APFSContainerFree value not found")
287286
}
288-
rest = rest[start+len("<integer>"):]
289-
end := strings.Index(rest, "</integer>")
290-
if end == -1 {
287+
288+
value, _, found := strings.Cut(rest, "</integer>")
289+
if !found {
291290
return 0, fmt.Errorf("APFSContainerFree end tag not found")
292291
}
293292

294-
val, err := strconv.ParseUint(strings.TrimSpace(rest[:end]), 10, 64)
293+
val, err := strconv.ParseUint(strings.TrimSpace(value), 10, 64)
295294
if err != nil {
296295
return 0, fmt.Errorf("failed to parse APFSContainerFree: %v", err)
297296
}

lib/core/timeout.sh

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ if [[ -z "${MO_TIMEOUT_INITIALIZED:-}" ]]; then
5555
echo "[TIMEOUT] Install coreutils for better reliability: brew install coreutils" >&2
5656
fi
5757

58+
# Export so child processes inherit detected values and skip re-detection.
59+
# Without this, children that inherit MO_TIMEOUT_INITIALIZED=1 skip the init
60+
# block but have empty bin vars, forcing the slow shell fallback.
61+
export MO_TIMEOUT_BIN
62+
export MO_TIMEOUT_PERL_BIN
5863
export MO_TIMEOUT_INITIALIZED=1
5964
fi
6065

@@ -181,7 +186,10 @@ run_with_timeout() {
181186
"$@" &
182187
local cmd_pid=$!
183188

184-
# Start timeout killer in background
189+
# Start timeout killer in background.
190+
# Redirect all FDs to /dev/null so orphaned child processes (e.g. sleep $duration)
191+
# do not inherit open file descriptors from the caller and block output pipes
192+
# (notably bats output capture pipes that wait for all writers to close).
185193
(
186194
# Wait for timeout duration
187195
sleep "$duration"
@@ -200,7 +208,7 @@ run_with_timeout() {
200208
kill -KILL -"$cmd_pid" 2> /dev/null || kill -KILL "$cmd_pid" 2> /dev/null || true
201209
fi
202210
fi
203-
) &
211+
) < /dev/null > /dev/null 2>&1 &
204212
local killer_pid=$!
205213

206214
# Wait for command to complete

0 commit comments

Comments
 (0)