diff --git a/integrations/setup-quick-launchers.sh b/integrations/setup-quick-launchers.sh index f1abb13..86365ff 100644 --- a/integrations/setup-quick-launchers.sh +++ b/integrations/setup-quick-launchers.sh @@ -20,9 +20,9 @@ log_warn() { echo -e "${YELLOW}${ICON_WARN}${NC} $1"; } log_error() { echo -e "${RED}${ICON_ERR}${NC} $1"; } detect_mo() { - if command -v mo >/dev/null 2>&1; then + if command -v mo > /dev/null 2>&1; then command -v mo - elif command -v mole >/dev/null 2>&1; then + elif command -v mole > /dev/null 2>&1; then command -v mole else log_error "Mole not found. Install it first via Homebrew or ./install.sh." @@ -38,7 +38,7 @@ write_raycast_script() { local raw_cmd="\"${mo_bin}\" ${subcommand}" local cmd_escaped="${raw_cmd//\\/\\\\}" cmd_escaped="${cmd_escaped//\"/\\\"}" - cat > "$target" < "$target" << EOF #!/bin/bash # Required parameters: @@ -252,7 +252,7 @@ create_raycast_commands() { } uuid() { - if command -v uuidgen >/dev/null 2>&1; then + if command -v uuidgen > /dev/null 2>&1; then uuidgen else # Fallback pseudo UUID @@ -286,7 +286,7 @@ create_alfred_workflow() { local dir="$workflows_dir/$workflow_uid" mkdir -p "$dir" - cat > "$dir/info.plist" < "$dir/info.plist" << EOF diff --git a/mole b/mole index a636bde..352f966 100755 --- a/mole +++ b/mole @@ -343,7 +343,7 @@ remove_mole() { # Read single key IFS= read -r -s -n1 key || key="" - drain_pending_input # Clean up any escape sequence remnants + drain_pending_input # Clean up any escape sequence remnants case "$key" in $'\e') echo -e "${GRAY}Cancelled${NC}" diff --git a/scripts/check.sh b/scripts/check.sh index fe195e6..64ed74a 100755 --- a/scripts/check.sh +++ b/scripts/check.sh @@ -62,6 +62,64 @@ else echo -e "${YELLOW}⚠ bats not installed or no tests found, skipping${NC}\n" fi +# 4. Code optimization checks +echo -e "${YELLOW}4. Checking code optimizations...${NC}" +OPTIMIZATION_SCORE=0 +TOTAL_CHECKS=0 + +# Check 1: Simplified keyboard input (0.05s timeout) +((TOTAL_CHECKS++)) +if grep -q "read -r -s -n 1 -t 0.05" lib/common.sh; then + echo -e "${GREEN} ✓ Keyboard timeout optimized (0.05s)${NC}" + ((OPTIMIZATION_SCORE++)) +else + echo -e "${YELLOW} ⚠ Keyboard timeout not optimized${NC}" +fi + +# Check 2: Single-pass drain_pending_input +((TOTAL_CHECKS++)) +DRAIN_PASSES=$(grep -c "while IFS= read -r -s -n 1" lib/common.sh || echo 0) +if [[ $DRAIN_PASSES -eq 1 ]]; then + echo -e "${GREEN} ✓ drain_pending_input optimized (single-pass)${NC}" + ((OPTIMIZATION_SCORE++)) +else + echo -e "${YELLOW} ⚠ drain_pending_input has multiple passes${NC}" +fi + +# Check 3: Log rotation once per session +((TOTAL_CHECKS++)) +if grep -q "rotate_log_once" lib/common.sh && ! grep -q "rotate_log()" lib/common.sh | grep -v "rotate_log_once"; then + echo -e "${GREEN} ✓ Log rotation optimized (once per session)${NC}" + ((OPTIMIZATION_SCORE++)) +else + echo -e "${YELLOW} ⚠ Log rotation not optimized${NC}" +fi + +# Check 4: Simplified cache validation +((TOTAL_CHECKS++)) +if ! grep -q "cache_meta\|cache_dir_mtime" bin/uninstall.sh; then + echo -e "${GREEN} ✓ Cache validation simplified${NC}" + ((OPTIMIZATION_SCORE++)) +else + echo -e "${YELLOW} ⚠ Cache still uses redundant metadata${NC}" +fi + +# Check 5: Stricter path validation +((TOTAL_CHECKS++)) +if grep -q "Consecutive slashes" bin/clean.sh; then + echo -e "${GREEN} ✓ Path validation enhanced${NC}" + ((OPTIMIZATION_SCORE++)) +else + echo -e "${YELLOW} ⚠ Path validation not enhanced${NC}" +fi + +echo -e "${BLUE} Optimization score: $OPTIMIZATION_SCORE/$TOTAL_CHECKS${NC}\n" + # Summary echo -e "${GREEN}=== All Checks Completed ===${NC}" -echo -e "${GREEN}✓ Code quality checks passed!${NC}" +if [[ $OPTIMIZATION_SCORE -eq $TOTAL_CHECKS ]]; then + echo -e "${GREEN}✓ Code quality checks passed!${NC}" + echo -e "${GREEN}✓ All optimizations applied!${NC}" +else + echo -e "${YELLOW}⚠ Code quality checks passed, but some optimizations missing${NC}" +fi diff --git a/tests/common.bats b/tests/common.bats index 53e0619..4c8122a 100644 --- a/tests/common.bats +++ b/tests/common.bats @@ -79,6 +79,27 @@ teardown() { grep -q "ERROR: $message" "$log_file" } +@test "rotate_log_once only checks log size once per session" { + # Create a log file exceeding the max size + local log_file="$HOME/.config/mole/mole.log" + mkdir -p "$(dirname "$log_file")" + dd if=/dev/zero of="$log_file" bs=1024 count=1100 2> /dev/null + + # First call should rotate + HOME="$HOME" bash --noprofile --norc -c "source '$PROJECT_ROOT/lib/common.sh'" + [[ -f "${log_file}.old" ]] + + # Verify MOLE_LOG_ROTATED was set (rotation happened) + result=$(HOME="$HOME" MOLE_LOG_ROTATED=1 bash --noprofile --norc -c "source '$PROJECT_ROOT/lib/common.sh'; echo \$MOLE_LOG_ROTATED") + [[ "$result" == "1" ]] +} + +@test "drain_pending_input clears stdin buffer" { + # Test that drain_pending_input doesn't hang + result=$(echo -e "test\ninput" | HOME="$HOME" timeout 1 bash --noprofile --norc -c "source '$PROJECT_ROOT/lib/common.sh'; drain_pending_input; echo done") + [[ "$result" == "done" ]] +} + @test "bytes_to_human converts byte counts into readable units" { output="$( HOME="$HOME" bash --noprofile --norc << 'EOF' diff --git a/tests/whitelist.bats b/tests/whitelist.bats index f6beac3..f5f2d01 100644 --- a/tests/whitelist.bats +++ b/tests/whitelist.bats @@ -97,3 +97,49 @@ setup() { fi [ "$status" -ne 0 ] } + +@test "whitelist rejects paths with spaces" { + # Paths with spaces should be rejected by the new stricter validation + mkdir -p "$HOME/.config/mole" + echo "$HOME/.cache/invalid path" > "$WHITELIST_PATH" + + # Load whitelist - path with space should be rejected + warnings=$(HOME="$HOME" bash --noprofile --norc -c " + source '$PROJECT_ROOT/bin/clean.sh' + load_whitelist_file 2>&1 | grep -c 'Invalid path format' || echo 0 + ") + [ "$warnings" -ge 1 ] +} + +@test "whitelist rejects paths with consecutive slashes" { + mkdir -p "$HOME/.config/mole" + echo "$HOME/.cache//invalid" > "$WHITELIST_PATH" + + warnings=$(HOME="$HOME" bash --noprofile --norc -c " + source '$PROJECT_ROOT/bin/clean.sh' + load_whitelist_file 2>&1 | grep -c 'Consecutive slashes' || echo 0 + ") + [ "$warnings" -ge 1 ] +} + +@test "whitelist rejects system protected paths" { + mkdir -p "$HOME/.config/mole" + echo "/System/Library/test" > "$WHITELIST_PATH" + + warnings=$(HOME="$HOME" bash --noprofile --norc -c " + source '$PROJECT_ROOT/bin/clean.sh' + load_whitelist_file 2>&1 | grep -c 'Protected system path' || echo 0 + ") + [ "$warnings" -ge 1 ] +} + +@test "whitelist accepts valid paths with wildcards at end" { + mkdir -p "$HOME/.config/mole" + echo "$HOME/.cache/test/*" > "$WHITELIST_PATH" + + # Should be accepted without warnings + HOME="$HOME" bash --noprofile --norc -c " + source '$PROJECT_ROOT/bin/clean.sh' + load_whitelist_file + " 2>&1 | grep -q "Invalid path format" && exit 1 || exit 0 +}