diff --git a/SECURITY_AUDIT.md b/SECURITY_AUDIT.md index 9c7d0d5..ed2bf90 100644 --- a/SECURITY_AUDIT.md +++ b/SECURITY_AUDIT.md @@ -145,6 +145,14 @@ The analyzer (`mo analyze`) uses a distinct security model: **Code:** `lib/clean/apps.sh:orphan_detection()` +#### New Language Ecosystem Support (v1.19.1+) + +Added support for Elixir, Haskell, OCaml, and Editors (VS Code, Zed) with strict safety checks: + +- **Existence Checks:** Cleanup logic only runs if the tool is installed (directory exists). +- **Safe Targets:** Only volatile caches are cleaned (e.g., `~/.hex/cache`). +- **Protected Paths:** Critical directories like `~/.mix/archives` (installed tools) and `~/.stack/programs` (installed compilers) are explicitly **excluded**. + #### Active Uninstallation Heuristics For user-selected app removal: diff --git a/lib/clean/dev.sh b/lib/clean/dev.sh index bb519e7..fb703a9 100644 --- a/lib/clean/dev.sh +++ b/lib/clean/dev.sh @@ -255,6 +255,37 @@ clean_dev_network() { clean_sqlite_temp_files() { return 0 } +# Elixir/Erlang ecosystem. +clean_dev_elixir() { + if [[ -d ~/.mix ]] || [[ -d ~/.hex ]]; then + # safe_clean ~/.mix/archives/* "Mix cache" + safe_clean ~/.hex/cache/* "Hex cache" + fi +} +# Haskell ecosystem. +clean_dev_haskell() { + if [[ -d ~/.cabal ]] || [[ -d ~/.stack ]]; then + safe_clean ~/.cabal/packages/* "Cabal install cache" + # safe_clean ~/.stack/programs/* "Stack cache" + fi +} +# OCaml ecosystem. +clean_dev_ocaml() { + if [[ -d ~/.opam ]]; then + safe_clean ~/.opam/download-cache/* "Opam cache" + fi +} +# Editor caches. +clean_dev_editors() { + if [[ -d ~/Library/Caches/com.microsoft.VSCode ]] || [[ -d ~/Library/Application\ Support/Code ]]; then + safe_clean ~/Library/Caches/com.microsoft.VSCode/Cache/* "VS Code cached data" + safe_clean ~/Library/Application\ Support/Code/CachedData/* "VS Code cached data" + # safe_clean ~/Library/Application\ Support/Code/User/workspaceStorage/* "VS Code workspace storage" + fi + if [[ -d ~/Library/Caches/Zed ]]; then + safe_clean ~/Library/Caches/Zed/* "Zed cache" + fi +} # Main developer tools cleanup sequence. clean_developer_tools() { stop_section_spinner @@ -277,6 +308,10 @@ clean_developer_tools() { clean_dev_api_tools clean_dev_network clean_dev_misc + clean_dev_elixir + clean_dev_haskell + clean_dev_ocaml + clean_dev_editors safe_clean ~/Library/Caches/Homebrew/* "Homebrew cache" # Clean Homebrew locks without repeated sudo prompts. local brew_lock_dirs=( diff --git a/tests/dev_extended.bats b/tests/dev_extended.bats new file mode 100644 index 0000000..abf87c0 --- /dev/null +++ b/tests/dev_extended.bats @@ -0,0 +1,81 @@ +#!/usr/bin/env bats + +setup_file() { + PROJECT_ROOT="$(cd "${BATS_TEST_DIRNAME}/.." && pwd)" + export PROJECT_ROOT + + ORIGINAL_HOME="${HOME:-}" + export ORIGINAL_HOME + + HOME="$(mktemp -d "${BATS_TEST_DIRNAME}/tmp-dev-extended.XXXXXX")" + export HOME + + mkdir -p "$HOME" +} + +teardown_file() { + rm -rf "$HOME" + if [[ -n "${ORIGINAL_HOME:-}" ]]; then + export HOME="$ORIGINAL_HOME" + fi +} + +@test "clean_dev_elixir cleans mix and hex caches" { + mkdir -p "$HOME/.mix" "$HOME/.hex" + run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" bash --noprofile --norc <<'EOF' +set -euo pipefail +source "$PROJECT_ROOT/lib/core/common.sh" +source "$PROJECT_ROOT/lib/clean/dev.sh" +safe_clean() { echo "$2"; } +clean_dev_elixir +EOF + + [ "$status" -eq 0 ] + + [[ "$output" == *"Hex cache"* ]] +} + +@test "clean_dev_haskell cleans cabal install and stack caches" { + mkdir -p "$HOME/.cabal" "$HOME/.stack" + run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" bash --noprofile --norc <<'EOF' +set -euo pipefail +source "$PROJECT_ROOT/lib/core/common.sh" +source "$PROJECT_ROOT/lib/clean/dev.sh" +safe_clean() { echo "$2"; } +clean_dev_haskell +EOF + + [ "$status" -eq 0 ] + [[ "$output" == *"Cabal install cache"* ]] + +} + +@test "clean_dev_ocaml cleans opam cache" { + mkdir -p "$HOME/.opam" + run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" bash --noprofile --norc <<'EOF' +set -euo pipefail +source "$PROJECT_ROOT/lib/core/common.sh" +source "$PROJECT_ROOT/lib/clean/dev.sh" +safe_clean() { echo "$2"; } +clean_dev_ocaml +EOF + + [ "$status" -eq 0 ] + [[ "$output" == *"Opam cache"* ]] +} + +@test "clean_dev_editors cleans VS Code and Zed caches" { + mkdir -p "$HOME/Library/Caches/com.microsoft.VSCode" "$HOME/Library/Application Support/Code" "$HOME/Library/Caches/Zed" + run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" bash --noprofile --norc <<'EOF' +set -euo pipefail +source "$PROJECT_ROOT/lib/core/common.sh" +source "$PROJECT_ROOT/lib/clean/dev.sh" +safe_clean() { echo "$2"; } +clean_dev_editors +EOF + + [ "$status" -eq 0 ] + [[ "$output" == *"VS Code cached data"* ]] + + [[ "$output" == *"Zed cache"* ]] +}