mirror of
https://github.com/tw93/Mole.git
synced 2026-03-22 13:50:09 +00:00
docs: strengthen public security signals
This commit is contained in:
1
.github/CODEOWNERS
vendored
Normal file
1
.github/CODEOWNERS
vendored
Normal file
@@ -0,0 +1 @@
|
||||
* @tw93
|
||||
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -10,6 +10,8 @@ assignees: ''
|
||||
|
||||
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
|
||||
|
||||
1. Run command: `mo ...`
|
||||
|
||||
3
.github/ISSUE_TEMPLATE/config.yml
vendored
3
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,5 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
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
|
||||
url: https://t.me/+GclQS9ZnxyI2ODQ1
|
||||
about: Join our Telegram group for questions and discussions
|
||||
|
||||
10
.github/dependabot.yml
vendored
10
.github/dependabot.yml
vendored
@@ -4,8 +4,18 @@ updates:
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
labels:
|
||||
- "dependencies"
|
||||
reviewers:
|
||||
- "tw93"
|
||||
open-pull-requests-limit: 10
|
||||
|
||||
- package-ecosystem: "gomod"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
labels:
|
||||
- "dependencies"
|
||||
reviewers:
|
||||
- "tw93"
|
||||
open-pull-requests-limit: 10
|
||||
|
||||
18
.github/pull_request_template.md
vendored
Normal file
18
.github/pull_request_template.md
vendored
Normal 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
52
.github/workflows/codeql.yml
vendored
Normal 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 }}"
|
||||
30
.github/workflows/release.yml
vendored
30
.github/workflows/release.yml
vendored
@@ -6,7 +6,7 @@ on:
|
||||
- 'V*'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -58,6 +58,10 @@ jobs:
|
||||
name: Publish Release
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
attestations: write
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
|
||||
@@ -69,16 +73,32 @@ jobs:
|
||||
- name: Display structure of downloaded files
|
||||
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
|
||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
with:
|
||||
name: ${{ github.ref_name }}
|
||||
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
|
||||
draft: false
|
||||
prerelease: false
|
||||
|
||||
10
.github/workflows/test.yml
vendored
10
.github/workflows/test.yml
vendored
@@ -52,6 +52,9 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v4
|
||||
|
||||
- name: Install tools
|
||||
run: brew install bats-core
|
||||
|
||||
- name: Check for unsafe rm usage
|
||||
run: |
|
||||
echo "Checking for unsafe rm patterns..."
|
||||
@@ -86,3 +89,10 @@ jobs:
|
||||
exit 1
|
||||
fi
|
||||
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
|
||||
|
||||
20
README.md
20
README.md
@@ -74,10 +74,28 @@ mo purge --paths # Configure project scan directories
|
||||
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
|
||||
|
||||
- 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`.
|
||||
|
||||
## Features in Detail
|
||||
|
||||
76
SECURITY.md
Normal file
76
SECURITY.md
Normal 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).
|
||||
@@ -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.
|
||||
- Security-sensitive cleanup paths do not use raw `find ... -delete`.
|
||||
- Removal flows use guarded helpers such as `safe_remove()`, `safe_sudo_remove()`, `safe_find_delete()`, and `safe_sudo_find_delete()`.
|
||||
- destructive paths are validated before deletion
|
||||
- critical system roots and sensitive user-data categories are protected
|
||||
- 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
|
||||
/
|
||||
@@ -26,7 +67,7 @@ Blocked paths remain protected even with sudo, including:
|
||||
/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/var/tmp`
|
||||
@@ -37,23 +78,84 @@ Some subpaths under protected roots are explicitly allowlisted for bounded cache
|
||||
- `/private/var/db/powerlog`
|
||||
- `/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:
|
||||
- the app is not found by installed-app scanning and fallback checks, and
|
||||
- the target has been inactive for at least 30 days.
|
||||
- Claude VM bundles use a stricter app-specific window:
|
||||
- `~/Library/Application Support/Claude/vm_bundles/claudevm.bundle` must appear orphaned, and
|
||||
- it must be inactive for at least 7 days before cleanup.
|
||||
- Sensitive categories such as keychains, password-manager data, and protected app families are excluded from generic orphan cleanup.
|
||||
- system components such as Control Center, System Settings, TCC, Spotlight, Finder, and Dock-related state
|
||||
- keychains, password-manager data, tokens, credentials, and similar sensitive material
|
||||
- VPN and 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
|
||||
- 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`
|
||||
- `/System/Applications`
|
||||
@@ -61,140 +163,74 @@ Installed-app detection is broader than a simple `/Applications` scan and includ
|
||||
- Homebrew Caskroom locations
|
||||
- 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.
|
||||
- Deletion candidates are decoded and validated as absolute paths before removal.
|
||||
- Homebrew casks are preferentially removed with `brew uninstall --cask --zap`.
|
||||
- LaunchServices unregister and rebuild steps are skipped safely if `lsregister` is unavailable.
|
||||
- `--dry-run` previews are available for major destructive commands
|
||||
- interactive high-risk flows require explicit confirmation before deletion
|
||||
- purge marks recent projects conservatively and leaves them unselected by default
|
||||
- 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
|
||||
- protected vendor and build-output heuristics prevent broad accidental deletions
|
||||
- nested artifacts are filtered to avoid duplicate or parent-child over-deletion
|
||||
## Release Integrity and Continuous Security Signals
|
||||
|
||||
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`
|
||||
- `~/.rustup`
|
||||
- `~/.mix/archives`
|
||||
- `~/.stack/programs`
|
||||
Repository-level signals include:
|
||||
|
||||
## 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`
|
||||
- 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
|
||||
## Testing Coverage
|
||||
|
||||
## Analyzer
|
||||
|
||||
`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:
|
||||
There is no single `tests/security.bats` file. Instead, security-relevant behavior is covered by focused suites, including:
|
||||
|
||||
- `tests/core_safe_functions.bats`
|
||||
- `tests/clean_core.bats`
|
||||
- `tests/clean_user_core.bats`
|
||||
- `tests/clean_dev_caches.bats`
|
||||
- `tests/clean_system_maintenance.bats`
|
||||
- `tests/clean_apps.bats`
|
||||
- `tests/purge.bats`
|
||||
- `tests/core_safe_functions.bats`
|
||||
- `tests/installer.bats`
|
||||
- `tests/optimize.bats`
|
||||
|
||||
Local verification used for the current branch includes:
|
||||
Key coverage areas include:
|
||||
|
||||
```bash
|
||||
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
|
||||
bash -n lib/core/base.sh lib/clean/apps.sh tests/clean_apps.bats tests/optimize.bats
|
||||
```
|
||||
- path validation rejects empty, relative, traversal, and system paths
|
||||
- symlinked directories are rejected for destructive scans
|
||||
- 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
|
||||
|
||||
Primary Go dependencies are pinned in `go.mod`, including:
|
||||
|
||||
- `github.com/charmbracelet/bubbletea v1.3.10`
|
||||
- `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.
|
||||
- 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.
|
||||
- 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.
|
||||
- Time Machine safety windows are hour-based and intentionally conservative.
|
||||
- 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.
|
||||
- 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).
|
||||
|
||||
@@ -92,7 +92,10 @@ validate_path_for_deletion() {
|
||||
# Validate resolved target against protected paths
|
||||
if [[ -n "$resolved_target" ]]; then
|
||||
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"
|
||||
return 1
|
||||
;;
|
||||
|
||||
@@ -66,6 +66,9 @@ teardown() {
|
||||
}
|
||||
|
||||
@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'"
|
||||
[ "$status" -eq 1 ]
|
||||
|
||||
@@ -86,6 +89,15 @@ teardown() {
|
||||
[ "$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" {
|
||||
local test_file="$TEST_DIR/test_file.txt"
|
||||
echo "test" > "$test_file"
|
||||
@@ -134,6 +146,22 @@ teardown() {
|
||||
[ "$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" {
|
||||
local real_dir="$TEST_DIR/real"
|
||||
local link_dir="$TEST_DIR/link"
|
||||
|
||||
Reference in New Issue
Block a user