1
0
mirror of https://github.com/tw93/Mole.git synced 2026-03-22 18:30:08 +00:00

docs: strengthen public security signals

This commit is contained in:
Tw93
2026-03-10 15:27:24 +08:00
parent a34cdee809
commit af84d6f4be
13 changed files with 417 additions and 140 deletions

1
.github/CODEOWNERS vendored Normal file
View File

@@ -0,0 +1 @@
* @tw93

View File

@@ -10,6 +10,8 @@ assignees: ''
A clear and concise description of what the bug is. We suggest using English for better global understanding. A clear and concise description of what the bug is. We suggest using English for better global understanding.
If you believe the issue may allow unsafe deletion, path validation bypass, privilege boundary bypass, or release/install integrity issues, do not file a public bug report. Report it privately using the contact details in `SECURITY.md`.
## Steps to reproduce ## Steps to reproduce
1. Run command: `mo ...` 1. Run command: `mo ...`

View File

@@ -1,5 +1,8 @@
blank_issues_enabled: false blank_issues_enabled: false
contact_links: contact_links:
- name: Private Security Report
url: mailto:hitw93@gmail.com?subject=Mole%20security%20report
about: Report a suspected vulnerability privately instead of opening a public issue
- name: Telegram Community - name: Telegram Community
url: https://t.me/+GclQS9ZnxyI2ODQ1 url: https://t.me/+GclQS9ZnxyI2ODQ1
about: Join our Telegram group for questions and discussions about: Join our Telegram group for questions and discussions

View File

@@ -4,8 +4,18 @@ updates:
directory: "/" directory: "/"
schedule: schedule:
interval: "weekly" interval: "weekly"
labels:
- "dependencies"
reviewers:
- "tw93"
open-pull-requests-limit: 10
- package-ecosystem: "gomod" - package-ecosystem: "gomod"
directory: "/" directory: "/"
schedule: schedule:
interval: "weekly" interval: "weekly"
labels:
- "dependencies"
reviewers:
- "tw93"
open-pull-requests-limit: 10

18
.github/pull_request_template.md vendored Normal file
View File

@@ -0,0 +1,18 @@
## Summary
- Describe the change.
## Safety Review
- Does this change affect cleanup, uninstall, optimize, installer, remove, analyze delete, update, or install behavior?
- Does this change affect path validation, protected directories, symlink handling, sudo boundaries, or release/install integrity?
- If yes, describe the new boundary or risk change clearly.
## Tests
- List the automated tests you ran.
- List any manual checks for high-risk paths or destructive flows.
## Safety-related changes
- None.

52
.github/workflows/codeql.yml vendored Normal file
View File

@@ -0,0 +1,52 @@
name: CodeQL
on:
push:
branches: [main, dev]
pull_request:
branches: [main, dev]
schedule:
- cron: '17 3 * * 1'
permissions:
contents: read
security-events: write
jobs:
analyze:
name: Analyze (${{ matrix.language }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- language: go
build-mode: manual
- language: actions
build-mode: none
steps:
- name: Checkout
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v4
- name: Set up Go
if: matrix.language == 'go'
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v5
with:
go-version: "1.24.6"
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
queries: security-extended
- name: Build for CodeQL
if: matrix.build-mode == 'manual'
run: make build
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
with:
category: "/language:${{ matrix.language }}"

View File

@@ -6,7 +6,7 @@ on:
- 'V*' - 'V*'
permissions: permissions:
contents: write contents: read
jobs: jobs:
build: build:
@@ -58,6 +58,10 @@ jobs:
name: Publish Release name: Publish Release
needs: build needs: build
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
contents: write
attestations: write
id-token: write
steps: steps:
- name: Download all artifacts - name: Download all artifacts
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
@@ -69,16 +73,32 @@ jobs:
- name: Display structure of downloaded files - name: Display structure of downloaded files
run: ls -R bin/ run: ls -R bin/
- name: Generate release checksums
run: |
cd bin
mapfile -t release_files < <(find . -maxdepth 1 -type f -printf '%P\n' | sort)
if [[ ${#release_files[@]} -eq 0 ]]; then
echo "No release assets found"
exit 1
fi
sha256sum "${release_files[@]}" > SHA256SUMS
cat SHA256SUMS
- name: Generate artifact attestation
uses: actions/attest-build-provenance@v3
with:
subject-path: |
bin/analyze-darwin-*
bin/status-darwin-*
bin/binaries-darwin-*.tar.gz
bin/SHA256SUMS
- name: Create Release - name: Create Release
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2 uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2
if: startsWith(github.ref, 'refs/tags/') if: startsWith(github.ref, 'refs/tags/')
with: with:
name: ${{ github.ref_name }} name: ${{ github.ref_name }}
files: bin/* files: bin/*
body: |
Release assets are ready.
Final curated release notes should be applied with `gh release edit` after workflow verification.
generate_release_notes: false generate_release_notes: false
draft: false draft: false
prerelease: false prerelease: false

View File

@@ -52,6 +52,9 @@ jobs:
steps: steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v4 - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v4
- name: Install tools
run: brew install bats-core
- name: Check for unsafe rm usage - name: Check for unsafe rm usage
run: | run: |
echo "Checking for unsafe rm patterns..." echo "Checking for unsafe rm patterns..."
@@ -86,3 +89,10 @@ jobs:
exit 1 exit 1
fi fi
echo "✓ No secrets found" echo "✓ No secrets found"
- name: Run high-risk path regression tests
env:
BATS_FORMATTER: tap
LANG: en_US.UTF-8
LC_ALL: en_US.UTF-8
run: bats tests/core_safe_functions.bats tests/purge.bats tests/installer.bats

View File

@@ -74,10 +74,28 @@ mo purge --paths # Configure project scan directories
mo analyze /Volumes # Analyze external drives only mo analyze /Volumes # Analyze external drives only
``` ```
## Security & Safety Design
Mole is a local system maintenance tool. Commands such as `clean`, `uninstall`, `purge`, `installer`, `remove`, and parts of `optimize` can perform destructive local operations.
Mole is designed with safety-first defaults for local system maintenance.
- Destructive operations are guarded by path validation, protected directory rules, conservative cleanup boundaries, and explicit confirmation where appropriate.
- Mole prioritizes bounded cleanup over aggressive cleanup.
- High-risk paths, sensitive data categories, system locations, and sudo flows have explicit protection boundaries.
- When uncertainty exists, the tool should refuse, skip, or require stronger confirmation instead of widening deletion scope.
- `mo analyze` is intentionally safer than cleanup flows for ad hoc deletion because it moves files to Trash through Finder instead of directly deleting them.
- Release assets are published with SHA-256 checksums, curated safety notes, and GitHub artifact attestations.
Review these documents before using high-risk commands:
- [SECURITY.md](SECURITY.md)
- [SECURITY_AUDIT.md](SECURITY_AUDIT.md)
## Tips ## Tips
- Video tutorial: Watch the [Mole tutorial video](https://www.youtube.com/watch?v=UEe9-w4CcQ0), thanks to PAPAYA 電腦教室. - Video tutorial: Watch the [Mole tutorial video](https://www.youtube.com/watch?v=UEe9-w4CcQ0), thanks to PAPAYA 電腦教室.
- Safety and logs: Deletions are permanent. Review with `--dry-run` first, and add `--debug` when needed. File operations are logged to `~/.config/mole/operations.log`. Disable with `MO_NO_OPLOG=1`. See [Security Audit](SECURITY_AUDIT.md). - Safety and logs: `clean`, `uninstall`, `purge`, `installer`, and `remove` are destructive. Review with `--dry-run` first, and add `--debug` when needed. File operations are logged to `~/.config/mole/operations.log`. Disable with `MO_NO_OPLOG=1`. Review [SECURITY.md](SECURITY.md) and [SECURITY_AUDIT.md](SECURITY_AUDIT.md).
- Navigation: Mole supports arrow keys and Vim bindings `h/j/k/l`. - Navigation: Mole supports arrow keys and Vim bindings `h/j/k/l`.
## Features in Detail ## Features in Detail

76
SECURITY.md Normal file
View File

@@ -0,0 +1,76 @@
# Security Policy
Mole is a local system maintenance tool. It includes high-risk operations such as cleanup, uninstall, optimization, and artifact removal. We treat safety boundaries, deletion logic, and release integrity as security-sensitive areas.
## Reporting a Vulnerability
Please report suspected security issues privately.
- Email: `hitw93@gmail.com`
- Subject line: `Mole security report`
Do not open a public GitHub issue for an unpatched vulnerability.
If GitHub Security Advisories private reporting is enabled for the repository, you may use that channel instead of email.
Include as much of the following as possible:
- Mole version and install method
- macOS version
- Exact command or workflow involved
- Reproduction steps or proof of concept
- Whether the issue involves deletion boundaries, symlinks, sudo, path validation, or release/install integrity
## Response Expectations
- We aim to acknowledge new reports within 7 calendar days.
- We aim to provide a status update within 30 days if a fix or mitigation is not yet available.
- We will coordinate disclosure after a fix, mitigation, or clear user guidance is ready.
Response times are best-effort for a maintainer-led open source project, but security reports are prioritized over normal bug reports.
## Supported Versions
Security fixes are only guaranteed for:
- The latest published release
- The current `main` branch
Older releases may not receive security fixes. Users running high-risk commands should stay current.
## What We Consider a Security Issue
Examples of security-relevant issues include:
- Path validation bypasses
- Deletion outside intended cleanup boundaries
- Unsafe handling of symlinks or path traversal
- Unexpected privilege escalation or unsafe sudo behavior
- Sensitive data removal that bypasses documented protections
- Release, installation, update, or checksum integrity issues
- Vulnerabilities in logic that can cause unintended destructive behavior
## What Usually Does Not Qualify
The following are usually normal bugs, feature requests, or documentation issues rather than security issues:
- Cleanup misses that leave recoverable junk behind
- False negatives where Mole refuses to clean something
- Cosmetic UI problems
- Requests for broader or more aggressive cleanup behavior
- Compatibility issues without a plausible security impact
If you are unsure whether something is security-relevant, report it privately first.
## Security-Focused Areas in Mole
The project pays particular attention to:
- Destructive command boundaries
- Path validation and protected-directory rules
- Sudo and privilege boundaries
- Symlink and path traversal handling
- Sensitive data exclusions
- Packaging, release artifacts, checksums, and update/install flows
For the current technical design and known limitations, see [SECURITY_AUDIT.md](SECURITY_AUDIT.md).

View File

@@ -1,18 +1,59 @@
# Mole Security Reference # Mole Security Audit
Version 1.30.0 | 2026-03-08 This document describes the security-relevant behavior of the current `main` branch. It is intended as a public description of Mole's safety boundaries, destructive-operation controls, release integrity signals, and known limitations.
This document describes the security-relevant behavior of the current codebase on `main`. ## Executive Summary
## Path Validation Mole is a local system maintenance tool. Its main risk surface is not remote code execution; it is unintended local damage caused by cleanup, uninstall, optimize, purge, installer cleanup, or other destructive operations.
All destructive file operations go through `lib/core/file_ops.sh`. The project is designed around safety-first defaults:
- `validate_path_for_deletion()` rejects empty paths, relative paths, traversal segments such as `/../`, and control characters. - destructive paths are validated before deletion
- Security-sensitive cleanup paths do not use raw `find ... -delete`. - critical system roots and sensitive user-data categories are protected
- Removal flows use guarded helpers such as `safe_remove()`, `safe_sudo_remove()`, `safe_find_delete()`, and `safe_sudo_find_delete()`. - sudo use is bounded and additional restrictions apply when elevated deletion is required
- symlink handling is conservative
- preview, confirmation, timeout, and operation logging are used to make destructive behavior more visible and auditable
Blocked paths remain protected even with sudo, including: Mole prioritizes bounded cleanup over aggressive cleanup. When uncertainty exists, the tool should refuse, skip, or require stronger confirmation instead of widening deletion scope.
The project continues to strengthen:
- release integrity and public security signals
- targeted regression coverage for high-risk paths
- clearer documentation for privilege boundaries and known limitations
## Threat Surface
The highest-risk areas in Mole are:
- direct file and directory deletion
- recursive cleanup across common user and system cache locations
- uninstall flows that combine app removal with remnant cleanup
- project artifact purge for large dependency/build directories
- elevated cleanup paths that require sudo
- release, install, and update trust signals for distributed artifacts
`mo analyze` is intentionally lower-risk than cleanup flows:
- it does not require sudo
- it respects normal user permissions and SIP
- delete actions require explicit confirmation
- deletion routes through Finder Trash behavior rather than direct permanent removal
## Destructive Operation Boundaries
All destructive shell file operations are routed through guarded helpers in `lib/core/file_ops.sh`.
Core controls include:
- `validate_path_for_deletion()` rejects empty paths
- relative paths are rejected
- path traversal segments such as `..` as a path component are rejected
- paths containing control characters are rejected
- raw `find ... -delete` is avoided for security-sensitive cleanup logic
- removal flows use guarded helpers such as `safe_remove()`, `safe_sudo_remove()`, `safe_find_delete()`, and `safe_sudo_find_delete()`
Blocked paths remain protected even with sudo. Examples include:
```text ```text
/ /
@@ -26,7 +67,7 @@ Blocked paths remain protected even with sudo, including:
/Library/Extensions /Library/Extensions
``` ```
Some subpaths under protected roots are explicitly allowlisted for bounded cache and log cleanup, for example: Some subpaths under otherwise protected roots are explicitly allowlisted for bounded cleanup where the project intentionally supports cache/log maintenance. Examples include:
- `/private/tmp` - `/private/tmp`
- `/private/var/tmp` - `/private/var/tmp`
@@ -37,23 +78,84 @@ Some subpaths under protected roots are explicitly allowlisted for bounded cache
- `/private/var/db/powerlog` - `/private/var/db/powerlog`
- `/private/var/db/reportmemoryexception` - `/private/var/db/reportmemoryexception`
When running with sudo, symlinked targets are validated before deletion and system-target symlinks are refused. This design keeps cleanup scoped to known-safe maintenance targets instead of broad root-level deletion patterns.
## Cleanup Rules ## Protected Directories and Categories
### Orphan Detection Mole has explicit protected-path and protected-category logic in addition to root-path blocking.
Orphaned app data is handled in `lib/clean/apps.sh`. Protected or conservatively handled categories include:
- Generic orphaned app data requires both: - system components such as Control Center, System Settings, TCC, Spotlight, Finder, and Dock-related state
- the app is not found by installed-app scanning and fallback checks, and - keychains, password-manager data, tokens, credentials, and similar sensitive material
- the target has been inactive for at least 30 days. - VPN and proxy tools such as Shadowsocks, V2Ray, Clash, and Tailscale
- Claude VM bundles use a stricter app-specific window: - AI tools in generic protected-data logic, including Cursor, Claude, ChatGPT, and Ollama
- `~/Library/Application Support/Claude/vm_bundles/claudevm.bundle` must appear orphaned, and - `~/Library/Messages/Attachments`
- it must be inactive for at least 7 days before cleanup. - browser history and cookies
- Sensitive categories such as keychains, password-manager data, and protected app families are excluded from generic orphan cleanup. - Time Machine data while backup state is active or ambiguous
- `com.apple.*` LaunchAgents and LaunchDaemons
- iCloud-synced `Mobile Documents` data
Installed-app detection is broader than a simple `/Applications` scan and includes: Project purge also uses conservative heuristics:
- purge targets must be inside configured project boundaries
- direct-child artifact cleanup is only allowed in single-project mode
- recently modified artifacts are treated as recent for 7 days
- nested artifacts are filtered to avoid parent-child over-deletion
- protected vendor/build-output heuristics block ambiguous directories
Developer cleanup also preserves high-value state. Examples intentionally left alone include:
- `~/.cargo/bin`
- `~/.rustup`
- `~/.mix/archives`
- `~/.stack/programs`
## Symlink and Path Traversal Handling
Symlink behavior is intentionally conservative.
- path validation checks symlink targets before deletion
- symlinks pointing at protected system targets are rejected
- `safe_sudo_remove()` refuses to sudo-delete symlinks
- `safe_find_delete()` and `safe_sudo_find_delete()` refuse to scan symlinked base directories
- installer discovery avoids treating symlinked installer files as deletion candidates
- analyzer scanning skips following symlinks to unexpected targets
Path traversal handling is also explicit:
- non-absolute paths are rejected for destructive helpers
- `..` is rejected when it appears as a path component
- legitimate names containing `..` inside a single path element remain allowed to avoid false positives for real application data
## Privilege Escalation and Sudo Boundaries
Mole uses sudo for a subset of system-maintenance paths, but elevated behavior is still bounded by validation and protected-path rules.
Key properties:
- sudo access is explicitly requested instead of assumed
- non-interactive preview remains conservative when sudo is unavailable
- protected roots remain blocked even when sudo is available
- sudo deletion uses the same path validation gate as non-sudo deletion
- sudo cleanup skips or reports denied operations instead of widening scope
- authentication, SIP/MDM, and read-only filesystem failures are classified separately in file-operation results
When sudo is denied or unavailable, Mole prefers skipping privileged cleanup to forcing execution through unsafe fallback behavior.
## Sensitive Data Exclusions
Mole is not intended to aggressively delete high-value user data.
Examples of conservative handling include:
- sensitive app families are excluded from generic orphan cleanup
- orphaned app data waits for inactivity windows before cleanup
- Claude VM orphan cleanup uses a separate stricter rule
- uninstall file lists are decoded and revalidated before removal
- reverse-DNS bundle ID validation is required before LaunchAgent and LaunchDaemon pattern matching
Installed-app detection is broader than a single `/Applications` scan and includes:
- `/Applications` - `/Applications`
- `/System/Applications` - `/System/Applications`
@@ -61,140 +163,74 @@ Installed-app detection is broader than a simple `/Applications` scan and includ
- Homebrew Caskroom locations - Homebrew Caskroom locations
- Setapp application paths - Setapp application paths
Spotlight fallback checks are bounded with short timeouts to avoid hangs. This reduces the risk of incorrectly classifying active software as orphaned data.
### Uninstall Matching ## Dry-Run, Confirmation, and Audit Logging
App uninstall behavior is implemented in `lib/uninstall/batch.sh` and related helpers. Mole exposes multiple safety controls before and during destructive actions:
- LaunchAgent and LaunchDaemon lookups require a valid reverse-DNS bundle identifier. - `--dry-run` previews are available for major destructive commands
- Deletion candidates are decoded and validated as absolute paths before removal. - interactive high-risk flows require explicit confirmation before deletion
- Homebrew casks are preferentially removed with `brew uninstall --cask --zap`. - purge marks recent projects conservatively and leaves them unselected by default
- LaunchServices unregister and rebuild steps are skipped safely if `lsregister` is unavailable. - analyzer delete uses Finder Trash rather than direct permanent removal
- operation logs are written to `~/.config/mole/operations.log` unless disabled with `MO_NO_OPLOG=1`
- timeouts bound external commands so stalled discovery or uninstall operations do not silently hang the entire flow
### Developer and Project Cleanup Relevant timeout behavior includes:
Project artifact cleanup in `lib/clean/project.sh` protects recently modified targets: - orphan and Spotlight checks: 2s
- LaunchServices rebuild during uninstall: bounded 10s and 15s steps
- Homebrew uninstall cask flow: 300s by default, extended for large apps when needed
- project scans and sizing operations: bounded to avoid whole-home stalls
- recently modified project artifacts are treated as recent for 7 days ## Release Integrity and Continuous Security Signals
- protected vendor and build-output heuristics prevent broad accidental deletions
- nested artifacts are filtered to avoid duplicate or parent-child over-deletion
Developer-cache cleanup preserves toolchains and other high-value state. Examples intentionally left alone include: Mole treats release trust as part of its security posture, not just a packaging detail.
- `~/.cargo/bin` Repository-level signals include:
- `~/.rustup`
- `~/.mix/archives`
- `~/.stack/programs`
## Protected Categories - weekly Dependabot updates for Go modules and GitHub Actions
- CI checks for unsafe `rm -rf` usage patterns and core protection behavior
- targeted tests for path validation, purge boundaries, symlink behavior, dry-run flows, and destructive helpers
- CodeQL scanning for Go and GitHub Actions workflows
- curated changelog-driven release notes with a dedicated `Safety-related changes` section
- published SHA-256 checksums for release assets
- GitHub artifact attestations for release assets
Protected or conservatively handled categories include: These controls do not eliminate all supply-chain risk, but they make release changes easier to review and verify.
- system components such as Control Center, System Settings, TCC, Spotlight, and `/Library/Updates` ## Testing Coverage
- password managers and keychain-related data
- VPN / proxy tools such as Shadowsocks, V2Ray, Clash, and Tailscale
- AI tools in generic protected-data logic, including Cursor, Claude, ChatGPT, and Ollama
- `~/Library/Messages/Attachments`
- browser history and cookies
- Time Machine data while backup state is active or ambiguous
- `com.apple.*` LaunchAgents and LaunchDaemons
## Analyzer There is no single `tests/security.bats` file. Instead, security-relevant behavior is covered by focused suites, including:
`mo analyze` is intentionally lower-risk than cleanup flows:
- it does not require sudo
- it respects normal user permissions and SIP
- interactive deletion requires an extra confirmation sequence
- deletions route through Trash/Finder behavior rather than direct permanent removal
Code lives under `cmd/analyze/*.go`.
## Timeouts and Hang Resistance
`lib/core/timeout.sh` uses this fallback order:
1. `gtimeout` / `timeout`
2. a Perl helper with process-group cleanup
3. a shell fallback
Current notable timeouts in security-relevant paths:
- orphan/Spotlight `mdfind` checks: 2s
- LaunchServices rebuild during uninstall: 10s / 15s bounded steps
- Homebrew uninstall cask flow: 300s default, extended to 600s or 900s for large apps
- Application Support sizing: direct file `stat`, bounded `du` for directories
Additional safety behavior:
- `brew_uninstall_cask()` treats exit code `124` as timeout failure and returns failure immediately
- font cache rebuild is skipped while browsers are running
- project-cache discovery and scans use strict timeouts to avoid whole-home stalls
## User Configuration
Protected paths can be added to `~/.config/mole/whitelist`, one path per line.
Example:
```bash
/Users/me/important-cache
~/Library/Application Support/MyApp
```
Exact path protection is preferred over pattern-style broad deletion rules.
Use `--dry-run` before destructive operations when validating new cleanup behavior.
## Testing
There is no dedicated `tests/security.bats`. Security-relevant behavior is covered by targeted BATS suites, including:
- `tests/core_safe_functions.bats`
- `tests/clean_core.bats` - `tests/clean_core.bats`
- `tests/clean_user_core.bats` - `tests/clean_user_core.bats`
- `tests/clean_dev_caches.bats` - `tests/clean_dev_caches.bats`
- `tests/clean_system_maintenance.bats` - `tests/clean_system_maintenance.bats`
- `tests/clean_apps.bats` - `tests/clean_apps.bats`
- `tests/purge.bats` - `tests/purge.bats`
- `tests/core_safe_functions.bats` - `tests/installer.bats`
- `tests/optimize.bats` - `tests/optimize.bats`
Local verification used for the current branch includes: Key coverage areas include:
```bash - path validation rejects empty, relative, traversal, and system paths
bats tests/clean_core.bats tests/clean_user_core.bats tests/clean_dev_caches.bats tests/clean_system_maintenance.bats tests/purge.bats tests/core_safe_functions.bats tests/clean_apps.bats tests/optimize.bats - symlinked directories are rejected for destructive scans
bash -n lib/core/base.sh lib/clean/apps.sh tests/clean_apps.bats tests/optimize.bats - purge protects shallow or ambiguous paths and filters nested artifacts
``` - dry-run flows preview actions without applying them
- confirmation flows exist for high-risk interactive operations
CI additionally runs shell and Go validation on push. ## Known Limitations and Future Work
## Dependencies - Cleanup is destructive. Most cleanup and uninstall flows do not provide undo.
- `mo analyze` delete is safer because it uses Trash, but other cleanup flows are permanent once confirmed.
Primary Go dependencies are pinned in `go.mod`, including: - Generic orphan data waits 30 days before cleanup; this is conservative but heuristic.
- Claude VM orphan cleanup waits 7 days before cleanup; this is also heuristic.
- `github.com/charmbracelet/bubbletea v1.3.10` - Time Machine safety windows are hour-based and intentionally conservative.
- `github.com/charmbracelet/lipgloss v1.1.0`
- `github.com/shirou/gopsutil/v4 v4.26.2`
- `github.com/cespare/xxhash/v2 v2.3.0`
System tooling relies mainly on Apple-provided binaries and standard macOS utilities such as:
- `tmutil`
- `diskutil`
- `plutil`
- `launchctl`
- `osascript`
- `find`
- `stat`
Dependency vulnerability status should be checked separately from this document.
## Limitations
- Cleanup is destructive. There is no undo.
- Generic orphan data waits 30 days before automatic cleanup.
- Claude VM orphan cleanup waits 7 days before automatic cleanup.
- Time Machine safety windows are hour-based, not day-based, and remain more conservative.
- Localized app names may still be missed in some heuristic paths, though bundle IDs are preferred where available. - Localized app names may still be missed in some heuristic paths, though bundle IDs are preferred where available.
- Users who want immediate removal of app data should use explicit uninstall flows rather than waiting for orphan cleanup. - Users who want immediate removal of app data should use explicit uninstall flows rather than waiting for orphan cleanup.
- Release signing and provenance signals are improving, but downstream package-manager trust also depends on external distribution infrastructure.
- Planned follow-up work includes stronger destructive-command threat modeling, more regression coverage for high-risk paths, and continued hardening of release integrity and disclosure workflow.
For reporting procedures and supported versions, see [SECURITY.md](SECURITY.md).

View File

@@ -92,7 +92,10 @@ validate_path_for_deletion() {
# Validate resolved target against protected paths # Validate resolved target against protected paths
if [[ -n "$resolved_target" ]]; then if [[ -n "$resolved_target" ]]; then
case "$resolved_target" in case "$resolved_target" in
/System/* | /usr/bin/* | /usr/lib/* | /bin/* | /sbin/* | /private/etc/*) / | /System | /System/* | /bin | /bin/* | /sbin | /sbin/* | \
/usr | /usr/bin | /usr/bin/* | /usr/lib | /usr/lib/* | \
/etc | /etc/* | /private/etc | /private/etc/* | \
/Library/Extensions | /Library/Extensions/*)
log_error "Symlink points to protected system path: $path -> $resolved_target" log_error "Symlink points to protected system path: $path -> $resolved_target"
return 1 return 1
;; ;;

View File

@@ -66,6 +66,9 @@ teardown() {
} }
@test "validate_path_for_deletion rejects system directories" { @test "validate_path_for_deletion rejects system directories" {
run bash -c "source '$PROJECT_ROOT/lib/core/common.sh'; validate_path_for_deletion '/'"
[ "$status" -eq 1 ]
run bash -c "source '$PROJECT_ROOT/lib/core/common.sh'; validate_path_for_deletion '/System'" run bash -c "source '$PROJECT_ROOT/lib/core/common.sh'; validate_path_for_deletion '/System'"
[ "$status" -eq 1 ] [ "$status" -eq 1 ]
@@ -86,6 +89,15 @@ teardown() {
[ "$status" -eq 1 ] [ "$status" -eq 1 ]
} }
@test "validate_path_for_deletion rejects symlink to protected system path" {
local link_path="$TEST_DIR/system-link"
ln -s "/System" "$link_path"
run bash -c "source '$PROJECT_ROOT/lib/core/common.sh'; validate_path_for_deletion '$link_path' 2>&1"
[ "$status" -eq 1 ]
[[ "$output" == *"protected system path"* ]]
}
@test "safe_remove successfully removes file" { @test "safe_remove successfully removes file" {
local test_file="$TEST_DIR/test_file.txt" local test_file="$TEST_DIR/test_file.txt"
echo "test" > "$test_file" echo "test" > "$test_file"
@@ -134,6 +146,22 @@ teardown() {
[ "$status" -eq 1 ] [ "$status" -eq 1 ]
} }
@test "safe_sudo_remove refuses symlink paths" {
local target_dir="$TEST_DIR/real"
local link_dir="$TEST_DIR/link"
mkdir -p "$target_dir"
ln -s "$target_dir" "$link_dir"
run bash -c "
source '$PROJECT_ROOT/lib/core/common.sh'
sudo() { return 0; }
export -f sudo
safe_sudo_remove '$link_dir' 2>&1
"
[ "$status" -eq 1 ]
[[ "$output" == *"Refusing to sudo remove symlink"* ]]
}
@test "safe_find_delete rejects symlinked directory" { @test "safe_find_delete rejects symlinked directory" {
local real_dir="$TEST_DIR/real" local real_dir="$TEST_DIR/real"
local link_dir="$TEST_DIR/link" local link_dir="$TEST_DIR/link"