1
0
mirror of https://github.com/tw93/Mole.git synced 2026-03-22 20:15:07 +00:00

fix(timeout): inherit helper state and pass checks

This commit is contained in:
Tw93
2026-03-14 08:32:11 +08:00
parent 0d2f217f28
commit 169be1e1f2
3 changed files with 21 additions and 15 deletions

View File

@@ -8,6 +8,7 @@ import (
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"slices"
"sort" "sort"
"strings" "strings"
"sync/atomic" "sync/atomic"
@@ -165,10 +166,8 @@ func validatePath(path string) error {
return fmt.Errorf("path contains null bytes") return fmt.Errorf("path contains null bytes")
} }
// Check for path traversal attempts (.. components). // Check for path traversal attempts (.. components).
for _, component := range strings.Split(path, string(filepath.Separator)) { if slices.Contains(strings.Split(path, string(filepath.Separator)), "..") {
if component == ".." { return fmt.Errorf("path contains traversal components: %s", path)
return fmt.Errorf("path contains traversal components: %s", path)
}
} }
return nil return nil
} }

View File

@@ -275,23 +275,22 @@ func getAPFSContainerFreeBytes(mountpoint string) (uint64, error) {
} }
const key = "<key>APFSContainerFree</key>" const key = "<key>APFSContainerFree</key>"
idx := strings.Index(out, key) _, rest, found := strings.Cut(out, key)
if idx == -1 { if !found {
return 0, fmt.Errorf("APFSContainerFree not found") return 0, fmt.Errorf("APFSContainerFree not found")
} }
rest := out[idx+len(key):] _, rest, found = strings.Cut(rest, "<integer>")
start := strings.Index(rest, "<integer>") if !found {
if start == -1 {
return 0, fmt.Errorf("APFSContainerFree value not found") return 0, fmt.Errorf("APFSContainerFree value not found")
} }
rest = rest[start+len("<integer>"):]
end := strings.Index(rest, "</integer>") value, _, found := strings.Cut(rest, "</integer>")
if end == -1 { if !found {
return 0, fmt.Errorf("APFSContainerFree end tag not found") return 0, fmt.Errorf("APFSContainerFree end tag not found")
} }
val, err := strconv.ParseUint(strings.TrimSpace(rest[:end]), 10, 64) val, err := strconv.ParseUint(strings.TrimSpace(value), 10, 64)
if err != nil { if err != nil {
return 0, fmt.Errorf("failed to parse APFSContainerFree: %v", err) return 0, fmt.Errorf("failed to parse APFSContainerFree: %v", err)
} }

View File

@@ -55,6 +55,11 @@ if [[ -z "${MO_TIMEOUT_INITIALIZED:-}" ]]; then
echo "[TIMEOUT] Install coreutils for better reliability: brew install coreutils" >&2 echo "[TIMEOUT] Install coreutils for better reliability: brew install coreutils" >&2
fi fi
# Export so child processes inherit detected values and skip re-detection.
# Without this, children that inherit MO_TIMEOUT_INITIALIZED=1 skip the init
# block but have empty bin vars, forcing the slow shell fallback.
export MO_TIMEOUT_BIN
export MO_TIMEOUT_PERL_BIN
export MO_TIMEOUT_INITIALIZED=1 export MO_TIMEOUT_INITIALIZED=1
fi fi
@@ -181,7 +186,10 @@ run_with_timeout() {
"$@" & "$@" &
local cmd_pid=$! local cmd_pid=$!
# Start timeout killer in background # Start timeout killer in background.
# Redirect all FDs to /dev/null so orphaned child processes (e.g. sleep $duration)
# do not inherit open file descriptors from the caller and block output pipes
# (notably bats output capture pipes that wait for all writers to close).
( (
# Wait for timeout duration # Wait for timeout duration
sleep "$duration" sleep "$duration"
@@ -200,7 +208,7 @@ run_with_timeout() {
kill -KILL -"$cmd_pid" 2> /dev/null || kill -KILL "$cmd_pid" 2> /dev/null || true kill -KILL -"$cmd_pid" 2> /dev/null || kill -KILL "$cmd_pid" 2> /dev/null || true
fi fi
fi fi
) & ) < /dev/null > /dev/null 2>&1 &
local killer_pid=$! local killer_pid=$!
# Wait for command to complete # Wait for command to complete