From e719af0508219c8f9c78da1d1a46fda08a73bf08 Mon Sep 17 00:00:00 2001 From: ToraDady Date: Mon, 27 Apr 2026 20:31:08 +0900 Subject: [PATCH] fix(careful): BSD sed compatibility for safe exception detection on macOS The sed regex in check-careful.sh uses \s+, which is a GNU sed extension not supported by BSD sed (macOS default). On macOS, this causes the RM_ARGS strip to fail silently, making rm -rf of safe exceptions (node_modules, .next, dist, etc.) trigger the destructive warning instead of being permitted as designed. Fix: replace \s+ with POSIX [[:space:]]+, which works on both GNU sed (Linux) and BSD sed (macOS). The existing test/hook-scripts.test.ts already documented this limitation via a detectSafeRmWorks() helper and a platform-conditional assertion ("if GNU sed: expect undefined, else: expect ask"). Now that the regex works on both platforms, this dead path is removed and the safe-exception tests assert the same expectation on every OS. Note: the grep regex in the same file also uses \s+, but BSD grep -E on macOS does support \s (verified via bash -x trace), so only the sed expression needs the fix. Discovered while translating the careful skill for a Japanese derivative project (uzustack). Reference: https://github.com/uzumaki-inc/uzustack/commit/bc67c8d --- careful/bin/check-careful.sh | 2 +- test/hook-scripts.test.ts | 22 ++-------------------- 2 files changed, 3 insertions(+), 21 deletions(-) diff --git a/careful/bin/check-careful.sh b/careful/bin/check-careful.sh index c8bc2c7ab..d9c39e48c 100755 --- a/careful/bin/check-careful.sh +++ b/careful/bin/check-careful.sh @@ -28,7 +28,7 @@ CMD_LOWER=$(printf '%s' "$CMD" | tr '[:upper:]' '[:lower:]') # --- Check for safe exceptions (rm -rf of build artifacts) --- if printf '%s' "$CMD" | grep -qE 'rm\s+(-[a-zA-Z]*r[a-zA-Z]*\s+|--recursive\s+)' 2>/dev/null; then SAFE_ONLY=true - RM_ARGS=$(printf '%s' "$CMD" | sed -E 's/.*rm\s+(-[a-zA-Z]+\s+)*//;s/--recursive\s*//') + RM_ARGS=$(printf '%s' "$CMD" | sed -E 's/.*rm[[:space:]]+(-[a-zA-Z]+[[:space:]]+)*//;s/--recursive[[:space:]]*//') for target in $RM_ARGS; do case "$target" in */node_modules|node_modules|*/\.next|\.next|*/dist|dist|*/__pycache__|__pycache__|*/\.cache|\.cache|*/build|build|*/\.turbo|\.turbo|*/coverage|coverage) diff --git a/test/hook-scripts.test.ts b/test/hook-scripts.test.ts index 850b5b983..f1ffe1239 100644 --- a/test/hook-scripts.test.ts +++ b/test/hook-scripts.test.ts @@ -56,13 +56,6 @@ function withFreezeDir(freezePath: string, fn: (stateDir: string) => void) { } } -// Detect whether the safe-rm-targets regex works on this platform. -// macOS sed -E does not support \s, so the safe exception check fails there. -function detectSafeRmWorks(): boolean { - const { output } = runHook(CAREFUL_SCRIPT, carefulInput('rm -rf node_modules')); - return output.permissionDecision === undefined; -} - // ============================================================ // check-careful.sh tests // ============================================================ @@ -88,24 +81,13 @@ describe('check-careful.sh', () => { test('rm -rf node_modules allows (safe exception)', () => { const { exitCode, output } = runHook(CAREFUL_SCRIPT, carefulInput('rm -rf node_modules')); expect(exitCode).toBe(0); - if (detectSafeRmWorks()) { - // GNU sed: safe exception triggers, allows through - expect(output.permissionDecision).toBeUndefined(); - } else { - // macOS sed: safe exception regex uses \\s which is unsupported, - // so the safe-targets check fails and the command warns - expect(output.permissionDecision).toBe('ask'); - } + expect(output.permissionDecision).toBeUndefined(); }); test('rm -rf .next dist allows (multiple safe targets)', () => { const { exitCode, output } = runHook(CAREFUL_SCRIPT, carefulInput('rm -rf .next dist')); expect(exitCode).toBe(0); - if (detectSafeRmWorks()) { - expect(output.permissionDecision).toBeUndefined(); - } else { - expect(output.permissionDecision).toBe('ask'); - } + expect(output.permissionDecision).toBeUndefined(); }); test('rm -rf node_modules /var/data warns (mixed safe+unsafe)', () => {