mirror of
https://github.com/tw93/Mole.git
synced 2026-02-04 13:16:47 +00:00
feat: refine ZIP installer detection to handle many entries and app bundles, and update AI agent development guide
This commit is contained in:
114
AGENT.md
114
AGENT.md
@@ -1,114 +0,0 @@
|
|||||||
# Mole AI Agent Guide
|
|
||||||
|
|
||||||
Quick reference for AI assistants working on Mole (Mac system cleaner).
|
|
||||||
**Last updated**: 2026-01-04
|
|
||||||
|
|
||||||
## Safety Checklist
|
|
||||||
|
|
||||||
Before any operation:
|
|
||||||
|
|
||||||
- Use `safe_*` helpers (never raw `rm -rf` or `find -delete`)
|
|
||||||
- Check protection: `is_protected()`, `is_whitelisted()`
|
|
||||||
- Test first: `MO_DRY_RUN=1 ./mole clean`
|
|
||||||
- Validate syntax: `bash -n <file>`
|
|
||||||
- Run tests: `./scripts/test.sh`
|
|
||||||
|
|
||||||
## Never Do
|
|
||||||
|
|
||||||
- Raw deletions without `safe_*` helpers
|
|
||||||
- Remove `--prefix`/`--config` flags from install.sh
|
|
||||||
- Commit code unless explicitly requested
|
|
||||||
- Mix languages: Python in shell, shell in Go
|
|
||||||
- Delete without checking protection lists
|
|
||||||
|
|
||||||
## Architecture Quick Map
|
|
||||||
|
|
||||||
```
|
|
||||||
mole # CLI entrypoint (menu + routing)
|
|
||||||
├── bin/ # Commands: clean, uninstall, optimize, analyze, status
|
|
||||||
├── lib/ # Shell logic: core/, clean/, ui/
|
|
||||||
├── cmd/ # Go apps: analyze/, status/
|
|
||||||
├── tests/ # BATS integration tests
|
|
||||||
└── scripts/ # Build and test automation
|
|
||||||
```
|
|
||||||
|
|
||||||
**Decision Tree**:
|
|
||||||
|
|
||||||
- User cleanup logic → `lib/clean/<module>.sh`
|
|
||||||
- Command entry → `bin/<command>.sh`
|
|
||||||
- Core utils → `lib/core/<util>.sh`
|
|
||||||
- Performance tool → `cmd/<tool>/*.go`
|
|
||||||
- Tests → `tests/<test>.bats`
|
|
||||||
|
|
||||||
## Common Commands
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Validation (run before suggesting changes)
|
|
||||||
bash -n <file> # Syntax check
|
|
||||||
./scripts/test.sh # Full test suite
|
|
||||||
MO_DRY_RUN=1 ./mole clean # Safe dry-run test
|
|
||||||
|
|
||||||
# Development
|
|
||||||
make build # Build Go binaries
|
|
||||||
go run ./cmd/analyze # Test without building
|
|
||||||
bats tests/clean.bats -f "name" # Specific test
|
|
||||||
|
|
||||||
# Debugging
|
|
||||||
MO_DEBUG=1 ./mole clean # Verbose output
|
|
||||||
```
|
|
||||||
|
|
||||||
## Code Style Rules
|
|
||||||
|
|
||||||
**Shell** (Bash 3.2 compatible):
|
|
||||||
|
|
||||||
- 2-space indent, quote variables: `"$var"`
|
|
||||||
- Use `[[` not `[`, prefer `$(cmd)` over backticks
|
|
||||||
- Comments: English, intent-focused, for safety boundaries only
|
|
||||||
- Entry scripts: 2-3 line header describing purpose
|
|
||||||
|
|
||||||
**Go**:
|
|
||||||
|
|
||||||
- Standard conventions: `gofmt`, `go vet`
|
|
||||||
- Never ignore errors
|
|
||||||
|
|
||||||
## Key Helpers
|
|
||||||
|
|
||||||
- `safe_rm <path>` - Protected deletion
|
|
||||||
- `safe_find_delete <base> <pattern> <days> <type>` - Safe find+delete
|
|
||||||
- `is_protected <path>` - Check system protection
|
|
||||||
- `is_whitelisted <name>` - Check user whitelist
|
|
||||||
- `log_info/success/warn/error <msg>` - Logging
|
|
||||||
|
|
||||||
## Workflow
|
|
||||||
|
|
||||||
1. **Read first**: Never propose changes to unread code
|
|
||||||
2. **Shell work**: Logic in `lib/`, called from `bin/`
|
|
||||||
3. **Go work**: Edit `cmd/<app>/*.go`
|
|
||||||
4. **Test**: Dry-run → BATS → full test suite
|
|
||||||
5. **Style**: Match existing file conventions (Bash 3.2)
|
|
||||||
|
|
||||||
## Example: Add New Cleanup
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 1. Create lib/clean/my_module.sh
|
|
||||||
clean_my_cache() {
|
|
||||||
local dir="$HOME/Library/Caches/MyApp"
|
|
||||||
[[ -d "$dir" ]] && ! is_whitelisted "my_app" || return
|
|
||||||
safe_find_delete "$dir" "*" "30" "f"
|
|
||||||
log_success "Cleaned MyApp cache"
|
|
||||||
}
|
|
||||||
|
|
||||||
# 2. Call from bin/clean.sh
|
|
||||||
source "${LIB_DIR}/clean/my_module.sh"
|
|
||||||
clean_my_cache
|
|
||||||
|
|
||||||
# 3. Test
|
|
||||||
bats tests/clean.bats -f "my_cache"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
- Tests fail → `bats tests/<file>.bats -f "test name"`
|
|
||||||
- Syntax error → `bash -n <file>`
|
|
||||||
- Permission denied → `./mole touchid`
|
|
||||||
- Cleanup not working → Check `is_protected()` or `~/.config/mole/whitelist`
|
|
||||||
374
AGENTS.md
Normal file
374
AGENTS.md
Normal file
@@ -0,0 +1,374 @@
|
|||||||
|
# AGENTS.md - Development Guide for Mole
|
||||||
|
|
||||||
|
This guide provides AI coding assistants with essential commands, patterns, and conventions for working in the Mole codebase.
|
||||||
|
|
||||||
|
**Quick reference**: Build/test commands • Safety rules • Architecture map • Code style
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Safety Checklist
|
||||||
|
|
||||||
|
Before any operation:
|
||||||
|
|
||||||
|
- Use `safe_*` helpers (never raw `rm -rf` or `find -delete`)
|
||||||
|
- Check protection: `is_protected()`, `is_whitelisted()`
|
||||||
|
- Test first: `MO_DRY_RUN=1 ./mole clean`
|
||||||
|
- Validate syntax: `bash -n <file>`
|
||||||
|
- Run tests: `./scripts/test.sh`
|
||||||
|
|
||||||
|
## NEVER Do These
|
||||||
|
|
||||||
|
- Run `rm -rf` or any raw deletion commands
|
||||||
|
- Delete files without checking protection lists
|
||||||
|
- Modify system-critical paths (e.g., `/System`, `/Library/Apple`)
|
||||||
|
- Remove installer flags `--prefix`/`--config` from `install.sh`
|
||||||
|
- Commit code changes unless explicitly requested
|
||||||
|
- Run destructive operations without dry-run validation
|
||||||
|
- Use raw `git` commands when `gh` CLI is available
|
||||||
|
|
||||||
|
## ALWAYS Do These
|
||||||
|
|
||||||
|
- Use `safe_*` helper functions for deletions (`safe_rm`, `safe_find_delete`)
|
||||||
|
- Respect whitelist files (e.g., `~/.config/mole/whitelist`)
|
||||||
|
- Check protection logic before cleanup operations
|
||||||
|
- Test with dry-run modes first
|
||||||
|
- Validate syntax before suggesting changes: `bash -n <file>`
|
||||||
|
- Use `gh` CLI for all GitHub operations (issues, PRs, releases, etc.)
|
||||||
|
- Never commit code unless explicitly requested by user
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick Reference
|
||||||
|
|
||||||
|
### Build Commands
|
||||||
|
```bash
|
||||||
|
# Build Go binaries for current platform
|
||||||
|
make build
|
||||||
|
|
||||||
|
# Build release binaries (cross-platform)
|
||||||
|
make release-amd64 # macOS Intel
|
||||||
|
make release-arm64 # macOS Apple Silicon
|
||||||
|
|
||||||
|
# Clean build artifacts
|
||||||
|
make clean
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test Commands
|
||||||
|
```bash
|
||||||
|
# Run full test suite (recommended before commits)
|
||||||
|
./scripts/test.sh
|
||||||
|
|
||||||
|
# Run specific BATS test file
|
||||||
|
bats tests/clean.bats
|
||||||
|
|
||||||
|
# Run specific test case by name
|
||||||
|
bats tests/clean.bats -f "should respect whitelist"
|
||||||
|
|
||||||
|
# Run Go tests only
|
||||||
|
go test -v ./cmd/...
|
||||||
|
|
||||||
|
# Run Go tests for specific package
|
||||||
|
go test -v ./cmd/analyze
|
||||||
|
|
||||||
|
# Shell syntax check
|
||||||
|
bash -n lib/clean/user.sh
|
||||||
|
bash -n mole
|
||||||
|
|
||||||
|
# Lint shell scripts
|
||||||
|
shellcheck --rcfile .shellcheckrc lib/**/*.sh bin/**/*.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Development Commands
|
||||||
|
```bash
|
||||||
|
# Test cleanup in dry-run mode
|
||||||
|
MO_DRY_RUN=1 ./mole clean
|
||||||
|
|
||||||
|
# Enable debug logging
|
||||||
|
MO_DEBUG=1 ./mole clean
|
||||||
|
|
||||||
|
# Test Go tool directly
|
||||||
|
go run ./cmd/analyze
|
||||||
|
|
||||||
|
# Test installation locally
|
||||||
|
./install.sh --prefix /usr/local/bin --config ~/.config/mole
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Architecture Quick Map
|
||||||
|
|
||||||
|
```
|
||||||
|
mole/ # Main CLI entrypoint (menu + routing)
|
||||||
|
├── mo # CLI alias wrapper
|
||||||
|
├── install.sh # Manual installer/updater (preserves --prefix/--config)
|
||||||
|
├── bin/ # Command entry points (thin wrappers)
|
||||||
|
│ ├── clean.sh # Deep cleanup orchestrator
|
||||||
|
│ ├── uninstall.sh # App removal with leftover detection
|
||||||
|
│ ├── optimize.sh # Cache rebuild + service refresh
|
||||||
|
│ ├── purge.sh # Aggressive cleanup mode
|
||||||
|
│ ├── touchid.sh # Touch ID sudo enabler
|
||||||
|
│ ├── analyze.sh # Disk usage explorer wrapper
|
||||||
|
│ └── status.sh # System health dashboard wrapper
|
||||||
|
├── lib/ # Reusable shell logic
|
||||||
|
│ ├── core/ # base.sh, log.sh, sudo.sh, ui.sh
|
||||||
|
│ ├── clean/ # Cleanup modules (user, apps, dev, caches, system)
|
||||||
|
│ └── ui/ # Confirmation dialogs, progress bars
|
||||||
|
├── cmd/ # Go applications
|
||||||
|
│ ├── analyze/ # Disk analysis tool
|
||||||
|
│ └── status/ # Real-time monitoring
|
||||||
|
├── scripts/ # Build and test automation
|
||||||
|
│ └── test.sh # Main test runner (shell + go + BATS)
|
||||||
|
└── tests/ # BATS integration tests
|
||||||
|
```
|
||||||
|
|
||||||
|
**Decision Tree**:
|
||||||
|
|
||||||
|
- User cleanup logic → `lib/clean/<module>.sh`
|
||||||
|
- Command entry → `bin/<command>.sh`
|
||||||
|
- Core utils → `lib/core/<util>.sh`
|
||||||
|
- Performance tool → `cmd/<tool>/*.go`
|
||||||
|
- Tests → `tests/<test>.bats`
|
||||||
|
|
||||||
|
### Language Stack
|
||||||
|
- **Shell (Bash 3.2)**: Core cleanup and system operations (`lib/`, `bin/`)
|
||||||
|
- **Go**: Performance-critical tools (`cmd/analyze/`, `cmd/status/`)
|
||||||
|
- **BATS**: Integration testing (`tests/`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Code Style Guidelines
|
||||||
|
|
||||||
|
### Shell Scripts
|
||||||
|
- **Indentation**: 4 spaces (configured in .editorconfig)
|
||||||
|
- **Variables**: `lowercase_with_underscores`
|
||||||
|
- **Functions**: `verb_noun` format (e.g., `clean_caches`, `get_size`)
|
||||||
|
- **Constants**: `UPPERCASE_WITH_UNDERSCORES`
|
||||||
|
- **Quoting**: Always quote variables: `"$var"` not `$var`
|
||||||
|
- **Tests**: Use `[[` instead of `[`
|
||||||
|
- **Command substitution**: Use `$(command)` not backticks
|
||||||
|
- **Error handling**: Use `set -euo pipefail` at top of files
|
||||||
|
|
||||||
|
### Go Code
|
||||||
|
- **Formatting**: Follow standard Go conventions (`gofmt`, `go vet`)
|
||||||
|
- **Package docs**: Add package-level documentation for exported functions
|
||||||
|
- **Error handling**: Never ignore errors, always handle them explicitly
|
||||||
|
- **Build tags**: Use `//go:build darwin` for macOS-specific code
|
||||||
|
|
||||||
|
### Comments
|
||||||
|
- **Language**: English only
|
||||||
|
- **Focus**: Explain "why" not "what" (code should be self-documenting)
|
||||||
|
- **Safety**: Document safety boundaries explicitly
|
||||||
|
- **Non-obvious logic**: Explain workarounds or complex patterns
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Key Helper Functions
|
||||||
|
|
||||||
|
### Safety Helpers (lib/core/base.sh)
|
||||||
|
- `safe_rm <path>`: Safe deletion with validation
|
||||||
|
- `safe_find_delete <base> <pattern> <days> <type>`: Protected find+delete
|
||||||
|
- `is_protected <path>`: Check if path is system-protected
|
||||||
|
- `is_whitelisted <name>`: Check user whitelist
|
||||||
|
|
||||||
|
### Logging (lib/core/log.sh)
|
||||||
|
- `log_info <msg>`: Informational messages
|
||||||
|
- `log_success <msg>`: Success notifications
|
||||||
|
- `log_warn <msg>`: Warnings
|
||||||
|
- `log_error <msg>`: Error messages
|
||||||
|
- `debug <msg>`: Debug output (requires MO_DEBUG=1)
|
||||||
|
|
||||||
|
### UI Helpers (lib/core/ui.sh)
|
||||||
|
- `confirm <prompt>`: Yes/no confirmation
|
||||||
|
- `show_progress <current> <total> <msg>`: Progress display
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Testing Strategy
|
||||||
|
|
||||||
|
### Test Types
|
||||||
|
1. **Syntax Validation**: `bash -n <file>` - catches basic errors
|
||||||
|
2. **Unit Tests**: BATS tests for individual functions
|
||||||
|
3. **Integration Tests**: Full command execution with BATS
|
||||||
|
4. **Dry-run Tests**: `MO_DRY_RUN=1` to validate without deletion
|
||||||
|
5. **Go Tests**: `go test -v ./cmd/...`
|
||||||
|
|
||||||
|
### Test Environment Variables
|
||||||
|
- `MO_DRY_RUN=1`: Preview changes without execution
|
||||||
|
- `MO_DEBUG=1`: Enable detailed debug logging
|
||||||
|
- `BATS_FORMATTER=pretty`: Use pretty output for BATS (default)
|
||||||
|
- `BATS_FORMATTER=tap`: Use TAP output for CI
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Common Development Tasks
|
||||||
|
|
||||||
|
### Adding New Cleanup Module
|
||||||
|
1. Create `lib/clean/new_module.sh`
|
||||||
|
2. Implement cleanup logic using `safe_*` helpers
|
||||||
|
3. Source it in `bin/clean.sh`
|
||||||
|
4. Add protection checks for critical paths
|
||||||
|
5. Write BATS test in `tests/clean.bats`
|
||||||
|
6. Test with `MO_DRY_RUN=1` first
|
||||||
|
|
||||||
|
### Modifying Go Tools
|
||||||
|
1. Navigate to `cmd/<tool>/`
|
||||||
|
2. Make changes to Go files
|
||||||
|
3. Test with `go run .` or `make build && ./bin/<tool>-go`
|
||||||
|
4. Run `go test -v` for unit tests
|
||||||
|
5. Check integration: `./mole <command>`
|
||||||
|
|
||||||
|
### Debugging Issues
|
||||||
|
1. Enable debug mode: `MO_DEBUG=1 ./mole clean`
|
||||||
|
2. Check logs for error messages
|
||||||
|
3. Verify sudo permissions: `sudo -n true` or `./mole touchid`
|
||||||
|
4. Test individual functions in isolation
|
||||||
|
5. Use `shellcheck` for shell script issues
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Linting and Quality
|
||||||
|
|
||||||
|
### Shell Script Linting
|
||||||
|
- **Tool**: shellcheck with custom `.shellcheckrc`
|
||||||
|
- **Disabled rules**: SC2155, SC2034, SC2059, SC1091, SC2038
|
||||||
|
- **Command**: `shellcheck --rcfile .shellcheckrc lib/**/*.sh bin/**/*.sh`
|
||||||
|
|
||||||
|
### Go Code Quality
|
||||||
|
- **Tools**: `go vet`, `go fmt`, `go test`
|
||||||
|
- **Command**: `go vet ./cmd/... && go test ./cmd/...`
|
||||||
|
|
||||||
|
### CI/CD Pipeline
|
||||||
|
- **Triggers**: Push/PR to main, dev branches
|
||||||
|
- **Platforms**: macOS 14, macOS 15
|
||||||
|
- **Tools**: bats-core, shellcheck, Go 1.24.6
|
||||||
|
- **Security checks**: Unsafe rm usage, app protection, secret scanning
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## File Organization Patterns
|
||||||
|
|
||||||
|
### Shell Modules
|
||||||
|
- Entry scripts in `bin/` should be thin wrappers
|
||||||
|
- Reusable logic goes in `lib/`
|
||||||
|
- Core utilities in `lib/core/`
|
||||||
|
- Feature-specific modules in `lib/clean/`, `lib/ui/`, etc.
|
||||||
|
|
||||||
|
### Go Packages
|
||||||
|
- Each tool in its own `cmd/<tool>/` directory
|
||||||
|
- Main entry point in `main.go`
|
||||||
|
- Use standard Go project layout
|
||||||
|
- macOS-specific code guarded with build tags
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## GitHub Operations
|
||||||
|
|
||||||
|
### Use gh CLI for All GitHub Work
|
||||||
|
|
||||||
|
**Preferred Commands**:
|
||||||
|
```bash
|
||||||
|
# Issues
|
||||||
|
gh issue view 123 # View issue details
|
||||||
|
gh issue list # List issues
|
||||||
|
gh issue comment 123 "message" # Comment on issue
|
||||||
|
|
||||||
|
# Pull Requests
|
||||||
|
gh pr view # View current PR
|
||||||
|
gh pr diff # Show diff
|
||||||
|
gh pr list # List PRs
|
||||||
|
gh pr checkout 123 # Checkout PR branch
|
||||||
|
gh pr merge # Merge current PR
|
||||||
|
|
||||||
|
# Repository operations
|
||||||
|
gh release create v1.0.0 # Create release
|
||||||
|
gh repo view # Repository info
|
||||||
|
gh api repos/owner/repo/issues # Raw API access
|
||||||
|
```
|
||||||
|
|
||||||
|
**NEVER use raw git commands for GitHub operations** when `gh` is available:
|
||||||
|
- ❌ `git log --oneline origin/main..HEAD` → ✅ `gh pr view`
|
||||||
|
- ❌ `git remote get-url origin` → ✅ `gh repo view`
|
||||||
|
- ❌ Manual GitHub API curl commands → ✅ `gh api`
|
||||||
|
|
||||||
|
## Error Handling Patterns
|
||||||
|
|
||||||
|
### Shell Scripts
|
||||||
|
- Use `set -euo pipefail` for strict error handling
|
||||||
|
- Check command exit codes: `if command; then ...`
|
||||||
|
- Provide meaningful error messages with `log_error`
|
||||||
|
- Use cleanup traps for temporary resources
|
||||||
|
|
||||||
|
### Go Code
|
||||||
|
- Never ignore errors: `if err != nil { return err }`
|
||||||
|
- Use structured error messages
|
||||||
|
- Handle context cancellation appropriately
|
||||||
|
- Log errors with context information
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Performance Considerations
|
||||||
|
|
||||||
|
### Shell Optimization
|
||||||
|
- Use built-in shell operations over external commands
|
||||||
|
- Prefer `find -delete` over `-exec rm`
|
||||||
|
- Minimize subprocess creation
|
||||||
|
- Use appropriate timeout mechanisms
|
||||||
|
|
||||||
|
### Go Optimization
|
||||||
|
- Use concurrency for I/O-bound operations
|
||||||
|
- Implement proper caching for expensive operations
|
||||||
|
- Profile memory usage in scanning operations
|
||||||
|
- Use efficient data structures for large datasets
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Security Best Practices
|
||||||
|
|
||||||
|
### Path Validation
|
||||||
|
- Always validate user-provided paths
|
||||||
|
- Check against protection lists before operations
|
||||||
|
- Use absolute paths to prevent directory traversal
|
||||||
|
- Implement proper sandboxing for destructive operations
|
||||||
|
|
||||||
|
### Permission Management
|
||||||
|
- Request sudo only when necessary
|
||||||
|
- Use `sudo -n true` to check sudo availability
|
||||||
|
- Implement proper Touch ID integration
|
||||||
|
- Respect user whitelist configurations
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Common Pitfalls to Avoid
|
||||||
|
|
||||||
|
1. **Over-engineering**: Keep solutions simple. Don't add abstractions for one-time operations.
|
||||||
|
2. **Premature optimization**: Focus on correctness first, performance second.
|
||||||
|
3. **Assuming paths exist**: Always check before operating on files/directories.
|
||||||
|
4. **Ignoring protection logic**: User data loss is unacceptable.
|
||||||
|
5. **Breaking updates**: Keep `--prefix`/`--config` flags in `install.sh`.
|
||||||
|
6. **Platform assumptions**: Code must work on all supported macOS versions (10.13+).
|
||||||
|
7. **Silent failures**: Always log errors and provide actionable messages.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Communication Style
|
||||||
|
|
||||||
|
- Be concise and technical
|
||||||
|
- Explain safety implications upfront
|
||||||
|
- Show before/after for significant changes
|
||||||
|
- Provide file:line references for code locations
|
||||||
|
- Suggest testing steps for validation
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
- Main script: `mole` (menu + routing logic)
|
||||||
|
- Protection lists: Check `is_protected()` implementations
|
||||||
|
- User config: `~/.config/mole/`
|
||||||
|
- Test directory: `tests/`
|
||||||
|
- Build scripts: `scripts/`
|
||||||
|
- Documentation: `README.md`, `CONTRIBUTING.md`, `SECURITY_AUDIT.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Remember**: When in doubt, err on the side of safety. It's better to clean less than to risk user data.
|
||||||
@@ -43,7 +43,7 @@ readonly INSTALLER_SCAN_PATHS=(
|
|||||||
"$HOME/Library/Application Support/Telegram Desktop"
|
"$HOME/Library/Application Support/Telegram Desktop"
|
||||||
"$HOME/Downloads/Telegram Desktop"
|
"$HOME/Downloads/Telegram Desktop"
|
||||||
)
|
)
|
||||||
readonly MAX_ZIP_ENTRIES=5
|
readonly MAX_ZIP_ENTRIES=50
|
||||||
ZIP_LIST_CMD=()
|
ZIP_LIST_CMD=()
|
||||||
IN_ALT_SCREEN=0
|
IN_ALT_SCREEN=0
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ fi
|
|||||||
|
|
||||||
TERMINAL_WIDTH=0
|
TERMINAL_WIDTH=0
|
||||||
|
|
||||||
# Check for installer payloads inside ZIP - single pass, fused size and pattern check
|
# Check for installer payloads inside ZIP - check first N entries for installer patterns
|
||||||
is_installer_zip() {
|
is_installer_zip() {
|
||||||
local zip="$1"
|
local zip="$1"
|
||||||
local cap="$MAX_ZIP_ENTRIES"
|
local cap="$MAX_ZIP_ENTRIES"
|
||||||
@@ -63,13 +63,10 @@ is_installer_zip() {
|
|||||||
[[ ${#ZIP_LIST_CMD[@]} -gt 0 ]] || return 1
|
[[ ${#ZIP_LIST_CMD[@]} -gt 0 ]] || return 1
|
||||||
|
|
||||||
if ! "${ZIP_LIST_CMD[@]}" "$zip" 2> /dev/null |
|
if ! "${ZIP_LIST_CMD[@]}" "$zip" 2> /dev/null |
|
||||||
head -n $((cap + 1)) |
|
head -n "$cap" |
|
||||||
awk -v cap="$cap" '
|
awk '
|
||||||
/\.(app|pkg|dmg|xip)(\/|$)/ { found=1 }
|
/\.(app|pkg|dmg|xip)(\/|$)/ { found=1; exit 0 }
|
||||||
END {
|
END { exit found ? 0 : 1 }
|
||||||
if (NR > cap) exit 1
|
|
||||||
exit found ? 0 : 1
|
|
||||||
}
|
|
||||||
'; then
|
'; then
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -71,12 +71,12 @@ require_unzip_support() {
|
|||||||
|
|
||||||
# Test ZIP installer detection
|
# Test ZIP installer detection
|
||||||
|
|
||||||
@test "is_installer_zip: rejects ZIP with installer content but too many entries" {
|
@test "is_installer_zip: detects ZIP with installer content even with many entries" {
|
||||||
if ! require_zip_support; then
|
if ! require_zip_support; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create a ZIP with too many files (exceeds MAX_ZIP_ENTRIES=5)
|
# Create a ZIP with many files (more than old MAX_ZIP_ENTRIES=5)
|
||||||
# Include a .app file to have installer content
|
# Include a .app file to have installer content
|
||||||
mkdir -p "$HOME/Downloads/large-app"
|
mkdir -p "$HOME/Downloads/large-app"
|
||||||
touch "$HOME/Downloads/large-app/MyApp.app"
|
touch "$HOME/Downloads/large-app/MyApp.app"
|
||||||
@@ -96,7 +96,7 @@ require_unzip_support() {
|
|||||||
' bash "$PROJECT_ROOT/bin/installer.sh"
|
' bash "$PROJECT_ROOT/bin/installer.sh"
|
||||||
|
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
[[ "$output" == "NOT_INSTALLER" ]]
|
[[ "$output" == "INSTALLER" ]]
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "is_installer_zip: detects ZIP with app content" {
|
@test "is_installer_zip: detects ZIP with app content" {
|
||||||
@@ -122,6 +122,71 @@ require_unzip_support() {
|
|||||||
[[ "$output" == "INSTALLER" ]]
|
[[ "$output" == "INSTALLER" ]]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@test "is_installer_zip: rejects ZIP when installer pattern appears after MAX_ZIP_ENTRIES" {
|
||||||
|
if ! require_zip_support; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create a ZIP where .app appears after the 50th entry
|
||||||
|
mkdir -p "$HOME/Downloads/deep-content"
|
||||||
|
# Create 51 regular files first
|
||||||
|
for i in {1..51}; do
|
||||||
|
touch "$HOME/Downloads/deep-content/file$i.txt"
|
||||||
|
done
|
||||||
|
# Add .app file at the end (52nd entry)
|
||||||
|
touch "$HOME/Downloads/deep-content/MyApp.app"
|
||||||
|
(cd "$HOME/Downloads" && zip -q -r deep.zip deep-content)
|
||||||
|
|
||||||
|
run bash -euo pipefail -c '
|
||||||
|
export MOLE_TEST_MODE=1
|
||||||
|
source "$1"
|
||||||
|
if is_installer_zip "'"$HOME/Downloads/deep.zip"'"; then
|
||||||
|
echo "INSTALLER"
|
||||||
|
else
|
||||||
|
echo "NOT_INSTALLER"
|
||||||
|
fi
|
||||||
|
' bash "$PROJECT_ROOT/bin/installer.sh"
|
||||||
|
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[[ "$output" == "NOT_INSTALLER" ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "is_installer_zip: detects ZIP with real app bundle structure" {
|
||||||
|
if ! require_zip_support; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create a realistic .app bundle structure (directory, not just a file)
|
||||||
|
mkdir -p "$HOME/Downloads/RealApp.app/Contents/MacOS"
|
||||||
|
mkdir -p "$HOME/Downloads/RealApp.app/Contents/Resources"
|
||||||
|
echo "#!/bin/bash" > "$HOME/Downloads/RealApp.app/Contents/MacOS/RealApp"
|
||||||
|
chmod +x "$HOME/Downloads/RealApp.app/Contents/MacOS/RealApp"
|
||||||
|
cat > "$HOME/Downloads/RealApp.app/Contents/Info.plist" << 'EOF'
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>RealApp</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
|
EOF
|
||||||
|
(cd "$HOME/Downloads" && zip -q -r realapp.zip RealApp.app)
|
||||||
|
|
||||||
|
run bash -euo pipefail -c '
|
||||||
|
export MOLE_TEST_MODE=1
|
||||||
|
source "$1"
|
||||||
|
if is_installer_zip "'"$HOME/Downloads/realapp.zip"'"; then
|
||||||
|
echo "INSTALLER"
|
||||||
|
else
|
||||||
|
echo "NOT_INSTALLER"
|
||||||
|
fi
|
||||||
|
' bash "$PROJECT_ROOT/bin/installer.sh"
|
||||||
|
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[[ "$output" == "INSTALLER" ]]
|
||||||
|
}
|
||||||
|
|
||||||
@test "is_installer_zip: rejects ZIP with only regular files" {
|
@test "is_installer_zip: rejects ZIP with only regular files" {
|
||||||
if ! require_zip_support; then
|
if ! require_zip_support; then
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
Reference in New Issue
Block a user