diff --git a/.gitignore b/.gitignore index 45749a4..77aca5e 100644 --- a/.gitignore +++ b/.gitignore @@ -156,6 +156,9 @@ home/.claude/* .claude/skills/ .claude/settings.local.json +# permissions-manager skill generated output +doc/permissions/ + # vagrant .vagrant .vagrant/**/* diff --git a/claude/permissions.beans.json b/claude/permissions.beans.json new file mode 100644 index 0000000..165911d --- /dev/null +++ b/claude/permissions.beans.json @@ -0,0 +1,15 @@ +{ + "allow": [ + "Bash(beans create:*)", + "Bash(beans list:*)", + "Bash(beans query:*)", + "Bash(beans show:*)", + "Bash(beans update:*)", + "Bash(beans:*)" + ], + "ask": [ + "Bash(beans archive:*)", + "Bash(beans delete:*)" + ], + "deny": [] +} diff --git a/claude/permissions.colima.json b/claude/permissions.colima.json new file mode 100644 index 0000000..4c48d5f --- /dev/null +++ b/claude/permissions.colima.json @@ -0,0 +1,16 @@ +{ + "allow": [ + "Bash(colima delete:*)", + "Bash(colima kubernetes:*)", + "Bash(colima list:*)", + "Bash(colima ssh:*)", + "Bash(colima ssh-config:*)", + "Bash(colima start:*)", + "Bash(colima status:*)", + "Bash(colima stop:*)", + "Bash(colima template:*)", + "Bash(colima version:*)", + "Bash(colima:*)" + ], + "deny": [] +} diff --git a/claude/permissions.git.jsonc b/claude/permissions.git.jsonc index ecf0e40..c46da6b 100644 --- a/claude/permissions.git.jsonc +++ b/claude/permissions.git.jsonc @@ -24,6 +24,18 @@ "Bash(git merge:*)", "Bash(git rebase:*)", + // Safer force-push alternative + "Bash(git push --force-with-lease:*)", + + // Safe reset patterns - align with remote + "Bash(git reset --hard origin/*:*)", + "Bash(git reset --hard upstream/*:*)", + "Bash(git reset --hard HEAD:*)", + + // Clean dry-run (always safe) + "Bash(git clean -n:*)", + "Bash(git clean --dry-run:*)", + // Inspection and queries "Bash(git check-ignore:*)", "Bash(git config:*)", @@ -48,11 +60,24 @@ "Bash(git tag:*)", ], - // Destructive operations that require explicit approval - "deny": [ - "Bash(git clean -fd:*)", // Removes untracked files - "Bash(git push --force:*)", // Overwrites remote history - "Bash(git push -f:*)", // Same as above - "Bash(git reset --hard:*)", // Discards local changes + // Operations that warrant confirmation + "ask": [ + "Bash(git clean -fd:*)", // Removes untracked files + "Bash(git clean -fdx:*)", // Also removes ignored files + "Bash(git push --force:*)", // Force push (not to main/master) + "Bash(git push -f:*)", // Force push shorthand + "Bash(git reset --hard:*)" // Other hard resets not covered above ], + + // Destructive operations that should never be auto-allowed + "deny": [ + "Bash(git push --force origin main:*)", + "Bash(git push --force origin master:*)", + "Bash(git push -f origin main:*)", + "Bash(git push -f origin master:*)", + "Bash(git push --force upstream main:*)", + "Bash(git push --force upstream master:*)", + "Bash(git push -f upstream main:*)", + "Bash(git push -f upstream master:*)" + ] } diff --git a/claude/permissions.json b/claude/permissions.json index 111efc9..de3f31c 100644 --- a/claude/permissions.json +++ b/claude/permissions.json @@ -1,9 +1,77 @@ { - "allow": [], + "allow": [ + "Bash(rm -rf .DS_Store)", + "Bash(rm -rf .cache)", + "Bash(rm -rf .coverage)", + "Bash(rm -rf .gradle)", + "Bash(rm -rf .next)", + "Bash(rm -rf .pytest_cache)", + "Bash(rm -rf .rspec_cache)", + "Bash(rm -rf .tmp)", + "Bash(rm -rf ./build)", + "Bash(rm -rf ./coverage)", + "Bash(rm -rf ./dist)", + "Bash(rm -rf ./node_modules)", + "Bash(rm -rf ./out)", + "Bash(rm -rf ./pkg)", + "Bash(rm -rf ./target)", + "Bash(rm -rf ./temp)", + "Bash(rm -rf ./tmp)", + "Bash(rm -rf ./vendor)", + "Bash(rm -rf build)", + "Bash(rm -rf coverage)", + "Bash(rm -rf dist)", + "Bash(rm -rf node_modules)", + "Bash(rm -rf out)", + "Bash(rm -rf pkg)", + "Bash(rm -rf target)", + "Bash(rm -rf temp)", + "Bash(rm -rf tmp)", + "Bash(rm -rf vendor)", + "Bash(sudo docker:*)", + "Bash(sudo journalctl:*)", + "Bash(sudo systemctl disable:*)", + "Bash(sudo systemctl enable:*)", + "Bash(sudo systemctl reload:*)", + "Bash(sudo systemctl restart:*)", + "Bash(sudo systemctl start:*)", + "Bash(sudo systemctl status:*)", + "Bash(sudo systemctl stop:*)" + ], + "ask": [ + "Bash(sudo chmod:*)", + "Bash(sudo chown:*)", + "Bash(sudo chgrp:*)", + "Bash(sudo mkdir:*)", + "Bash(sudo mv:*)", + "Bash(sudo cp:*)", + "Bash(sudo ln:*)", + "Bash(sudo kill:*)", + "Bash(sudo killall:*)", + "Bash(sudo shutdown:*)", + "Bash(sudo reboot:*)", + "Bash(sudo halt:*)", + "Bash(sudo apt-get:*)", + "Bash(sudo yum:*)", + "Bash(sudo dnf:*)" + ], "deny": [ "Bash(curl * | bash)", "Bash(curl * | sh)", - "Bash(rm -rf:*)", - "Bash(sudo:*)" + "Bash(wget * | bash)", + "Bash(wget * | sh)", + "Bash(sudo rm -rf:*)", + "Bash(sudo dd:*)", + "Bash(sudo mkfs:*)", + "Bash(sudo fdisk:*)", + "Bash(sudo parted:*)", + "Bash(rm -rf /:*)", + "Bash(rm -rf /*:*)", + "Bash(rm -rf ~:*)", + "Bash(rm -rf ~/*:*)", + "Bash(rm -rf $HOME:*)", + "Bash(rm -rf .:*)", + "Bash(rm -rf ..:*)", + "Bash(rm -rf ../*:*)" ] } diff --git a/claude/permissions.mcp.json b/claude/permissions.mcp.json index 60ac692..50f5f11 100644 --- a/claude/permissions.mcp.json +++ b/claude/permissions.mcp.json @@ -6,5 +6,6 @@ "mcp__MCPProxy__retrieve_tools", "mcp__MCPProxy__upstream_servers" ], - "deny": ["mcp__MCPProxy__call_tool_destructive"] + "ask": ["mcp__MCPProxy__call_tool_destructive"], + "deny": [] } diff --git a/claude/permissions.shell.json b/claude/permissions.shell.json index e94ec09..87ee597 100644 --- a/claude/permissions.shell.json +++ b/claude/permissions.shell.json @@ -1,51 +1,83 @@ { "allow": [ - "Bash(claude-permissions)", + "Bash(awk:*)", + "Bash(base64:*)", + "Bash(basename:*)", + "Bash(bash:*)", + "Bash(cat:*)", + "Bash(chmod:*)", "Bash(claude-permissions --aggregate)", "Bash(claude-permissions --json)", "Bash(claude-permissions --locations)", "Bash(claude-permissions --raw)", "Bash(claude-permissions cleanup)", - "Bash(cat:*)", - "Bash(chmod:*)", + "Bash(claude-permissions)", "Bash(command -v:*)", "Bash(cp:*)", "Bash(curl:*)", + "Bash(cut:*)", + "Bash(date:*)", "Bash(diff:*)", "Bash(dirname:*)", "Bash(echo:*)", "Bash(env:*)", + "Bash(expr:*)", "Bash(fd:*)", "Bash(file:*)", "Bash(find:*)", "Bash(grep:*)", + "Bash(gzip:*)", "Bash(head:*)", + "Bash(hostname:*)", + "Bash(id:*)", "Bash(jq:*)", "Bash(ln:*)", "Bash(ls:*)", "Bash(lsof:*)", "Bash(mkdir:*)", "Bash(mv:*)", + "Bash(nc:*)", + "Bash(openssl:*)", + "Bash(pkill:*)", + "Bash(printenv:*)", + "Bash(ps:*)", "Bash(pwd:*)", "Bash(readlink:*)", "Bash(realpath:*)", "Bash(rm:*)", + "Bash(rsync:*)", "Bash(sed:*)", + "Bash(seq:*)", + "Bash(sha256sum:*)", + "Bash(shasum:*)", + "Bash(sleep:*)", "Bash(sort:*)", + "Bash(split:*)", + "Bash(sqlite3:*)", + "Bash(ssh:*)", "Bash(stat:*)", "Bash(tail:*)", "Bash(tar:*)", "Bash(tee:*)", "Bash(test:*)", + "Bash(time:*)", + "Bash(timeout:*)", "Bash(touch:*)", "Bash(tr:*)", "Bash(tree:*)", "Bash(type:*)", + "Bash(uname:*)", "Bash(uniq:*)", "Bash(unzip:*)", + "Bash(watch:*)", "Bash(wc:*)", "Bash(which:*)", - "Bash(xargs:*)" + "Bash(whoami:*)", + "Bash(xargs:*)", + "Bash(xz:*)", + "Bash(yamllint:*)", + "Bash(yes:*)", + "Bash(zip:*)" ], "ask": [ "Bash(claude-permissions cleanup --force)", diff --git a/claude/permissions.web.json b/claude/permissions.web.json index 52c61f1..b3b378c 100644 --- a/claude/permissions.web.json +++ b/claude/permissions.web.json @@ -3,6 +3,10 @@ "WebFetch(domain:code.claude.com)", "WebFetch(domain:docs.github.com)", "WebFetch(domain:github.com)", + "WebFetch(domain:hk.jdx.dev)", + "WebFetch(domain:karafka.io)", + "WebFetch(domain:lima-vm.io)", + "WebFetch(domain:mise.jdx.dev)", "WebFetch(domain:raw.githubusercontent.com)" ], "deny": [] diff --git a/claude/permissions.work.json b/claude/permissions.work.json index 106e8c4..2a9d3f8 100644 --- a/claude/permissions.work.json +++ b/claude/permissions.work.json @@ -4,7 +4,10 @@ "Bash(bin/rubocop:*)", "Bash(bundle exec rspec:*)", "Bash(bundle exec rubocop:*)", - "Bash(bundle install)" + "Bash(bundle install)", + "Bash(npx bktide:*)", + "Bash(npx bktide build:*)", + "Bash(npx bktide)" ], "deny": [] } diff --git a/home/.claude/skills/permissions-manager/.gitignore b/home/.claude/skills/permissions-manager/.gitignore new file mode 100644 index 0000000..4fa0be7 --- /dev/null +++ b/home/.claude/skills/permissions-manager/.gitignore @@ -0,0 +1,18 @@ +# Temporary analysis files +/tmp/ +*.tmp +*.temp + +# Generated recommendations (unless intentionally committed) +recommendations-*.md + +# Aggregated data (often contains private info) +aggregate-*.txt +permissions-aggregate-*.txt + +# Backup files +*.bak +*.backup + +# OS files +.DS_Store diff --git a/home/.claude/skills/permissions-manager/README.md b/home/.claude/skills/permissions-manager/README.md new file mode 100644 index 0000000..235494d --- /dev/null +++ b/home/.claude/skills/permissions-manager/README.md @@ -0,0 +1,325 @@ +# Permissions Manager Skill + +A Claude Code skill for analyzing, documenting, and maintaining permissions across projects. + +## Purpose + +This skill helps you: + +- **Analyze** permissions across all projects to find patterns +- **Generate** recommendations for promoting common permissions to global +- **Document** permission changes with redacted, timestamped reports +- **Apply** changes interactively with safety checks +- **Maintain** a clean, centralized permission configuration + +## Installation + +This skill is part of the dotfiles repository and is automatically installed when you run `install.sh`. It will be symlinked to `~/.claude/skills/permissions-manager/`. + +## Commands + +### `/permissions-manager analyze` + +Analyzes current permissions and generates a recommendations document. + +**What it does:** +1. Runs `claude-permissions --aggregate` to gather data +2. Analyzes frequency of each permission across projects +3. Identifies dangerous wildcard patterns +4. Generates timestamped recommendations document +5. Redacts all project names and private information + +**Output:** +- `doc/permissions/YYYY-MM-DD/` directory containing: + - `analysis.md` - Recommendations (safe to commit after redaction) + - `aggregate.txt` - Raw aggregated data + - `wildcards.txt` - Wildcard safety analysis +- Shows high/medium frequency patterns +- Lists security concerns +- Provides specific recommendations + +**Example:** +```bash +/permissions-manager analyze +``` + +### `/permissions-manager apply` + +Interactively applies recommended permission changes. + +**What it does:** +1. Shows you each recommended change +2. Asks for confirmation before applying +3. Updates permission files in `claude/` +4. Regenerates `~/.claude/settings.json` +5. Optionally runs cleanup to remove duplicates +6. Creates change summary document + +**Safety features:** +- Always asks before making changes +- Shows exactly what will change +- Can skip individual changes +- Creates timestamped summary +- Suggests commit message + +**Example:** +```bash +/permissions-manager apply +``` + +### `/permissions-manager review` + +Quick review of current permission state. + +**What it does:** +1. Shows current allow/ask/deny counts +2. Lists all permission files +3. Shows recent permission-related commits +4. Checks for common issues + +**Example:** +```bash +/permissions-manager review +``` + +## Privacy & Security + +### Redaction + +All generated documentation is **automatically redacted** to remove: + +- Project names → `Project-01`, `Project-02`, etc. +- File paths → ``, ``, `` +- Internal domains → `*.company.com` → `*.` +- API keys/tokens → `` + +This makes the documentation safe to commit to public repositories. + +### Security Analysis + +The skill automatically checks for: + +- **Dangerous wildcards**: `rm -rf path:*` that could match multiple targets +- **Overly broad permissions**: `sudo:*` without restrictions +- **Pipe-to-shell**: `curl * | bash` in allow list +- **Missing protections**: Force-push to main/master not denied + +## Workflow Example + +### Initial Analysis + +```bash +# In your dotfiles directory +/permissions-manager analyze +``` + +This creates `doc/permissions/2026-01-25/` with analysis files. + +### Review Recommendations + +Open the generated file and review: +- High-frequency patterns (4+ projects) +- Medium-frequency patterns (2-3 projects) +- Security improvements needed +- Suggested deny → ask changes + +### Apply Changes + +```bash +/permissions-manager apply +``` + +You'll be asked about each change: +``` +Add to permissions.shell.json: + - Bash(sqlite3:*) +Apply this change? (yes/no/skip all): yes + +Remove dangerous wildcard: + - Bash(rm -rf build:*) → Bash(rm -rf build) +Apply this change? (yes/no/skip all): yes +``` + +### Verify + +```bash +/permissions-manager review +``` + +Check that counts look reasonable and recent commit shows your changes. + +### Cleanup + +The skill will ask: +``` +Run cleanup to remove 37 duplicate permissions from 16 projects? +(yes/no): yes +``` + +### Commit + +The skill suggests a commit message: +``` +feat(claude): consolidate permissions based on frequency analysis + +Promoted 12 high-frequency permissions to global. +Removed 5 dangerous wildcards from rm commands. +Changed 3 deny rules to ask for better UX. + +Co-Authored-By: Claude Opus 4.5 +``` + +## Helper Scripts + +Located in `scripts/` directory: + +### aggregate-permissions.sh + +```bash +./scripts/aggregate-permissions.sh [output-file] +``` + +Runs `claude-permissions --aggregate` and saves output with stats. + +### redact-projects.sh + +```bash +./scripts/redact-projects.sh input-file [output-file] +``` + +Redacts all private information from a file. Uses project name mapping. + +### analyze-wildcards.sh + +```bash +./scripts/analyze-wildcards.sh [settings-file] +``` + +Analyzes wildcards in permissions, categorizing as dangerous/safe. + +### generate-recommendations.sh + +```bash +./scripts/generate-recommendations.sh aggregate-file [output-file] +``` + +Parses aggregated permissions and generates structured recommendations. + +## Integration with Dotfiles + +This skill integrates with: + +- **`claude/` directory**: Permission files +- **`claudeconfig.sh`**: Settings regeneration +- **`claude-permissions`**: Analysis tool +- **`.git`**: For commit suggestions + +## Configuration + +The skill uses these environment variables (optional): + +- `PERMISSIONS_THRESHOLD_HIGH`: Frequency for high-priority (default: 4) +- `PERMISSIONS_THRESHOLD_MEDIUM`: Frequency for medium-priority (default: 2) +- `PERMISSIONS_WORKSPACE`: Workspace directory (default: `~/workspace`) + +## Best Practices + +1. **Run analysis regularly**: After adding new projects or making changes +2. **Review before applying**: Not all recommendations should be followed +3. **Test after changes**: Ensure workflows still work +4. **Commit redacted docs**: They're safe for public repos +5. **Iterate**: Run cleanup periodically to stay centralized + +## Troubleshooting + +### "No recommendations found" + +- Check if `claude-permissions` is installed +- Verify you have project-local permissions to analyze +- Try running `claude-permissions --aggregate` manually + +### "Redaction failed" + +- Ensure `scripts/redact-projects.sh` is executable +- Check that workspace directory exists +- Verify perl is installed + +### "Apply failed" + +- Check file permissions in `claude/` directory +- Ensure `claudeconfig.sh` is executable +- Verify `jq` is installed +- Check git repo is clean (no merge conflicts) + +### "Settings not regenerated" + +- Run `./claudeconfig.sh` manually +- Check for errors in the output +- Verify all JSON files are valid + +## Examples + +### Full Workflow + +```bash +# 1. Analyze current state +/permissions-manager analyze +# Creates: doc/permissions/2026-01-25/ + +# 2. Review the recommendations +cat doc/permissions/2026-01-25/analysis.md + +# 3. Apply selected changes +/permissions-manager apply +# Interactive prompts guide you through + +# 4. Verify results +/permissions-manager review + +# 5. Check generated change summary +cat doc/permissions/2026-01-25/changes.md +``` + +### Quick Security Check + +```bash +# Just check for dangerous patterns +./scripts/analyze-wildcards.sh +``` + +### Manual Redaction + +```bash +# Redact any file +./scripts/redact-projects.sh doc/my-analysis.txt +# Creates: doc/my-analysis.txt.redacted +``` + +## Future Enhancements + +Potential improvements: + +- **Diff mode**: Show before/after side-by-side +- **Test mode**: Simulate permissions to see what would be blocked +- **Export/import**: Share permission configs between machines +- **Validation**: Detect conflicts between allow/deny +- **CI integration**: Run analysis in CI pipeline + +## See Also + +- [Claude Permissions Documentation](../../../claude/CLAUDE.md) +- [Safe Patterns Guide](docs/safe-patterns.md) +- [Wildcard Safety](docs/wildcard-safety.md) + +## Support + +This skill is part of your personal dotfiles. For issues: + +1. Check the troubleshooting section above +2. Review the generated logs +3. Test individual scripts manually +4. Check Claude Code logs: `~/.claude/logs/` + +## License + +Part of the dotfiles repository. Private use. diff --git a/home/.claude/skills/permissions-manager/docs/safe-patterns.md b/home/.claude/skills/permissions-manager/docs/safe-patterns.md new file mode 100644 index 0000000..7a8eb9e --- /dev/null +++ b/home/.claude/skills/permissions-manager/docs/safe-patterns.md @@ -0,0 +1,469 @@ +# Safe Patterns for "Dangerous" Operations + +## Philosophy + +Not all uses of potentially dangerous commands are actually dangerous. This document explores context-specific patterns that could be safely allowed. + +## rm -rf Pattern Analysis + +### ✅ Safe Patterns (Could be ALLOW) + +Common development cleanup operations: + +```json +{ + "allow": [ + "Bash(rm -rf node_modules:*)", + "Bash(rm -rf dist:*)", + "Bash(rm -rf build:*)", + "Bash(rm -rf target:*)", + "Bash(rm -rf .next:*)", + "Bash(rm -rf out:*)", + "Bash(rm -rf coverage:*)", + "Bash(rm -rf .coverage:*)", + "Bash(rm -rf .pytest_cache:*)", + "Bash(rm -rf .rspec_cache:*)", + "Bash(rm -rf tmp:*)", + "Bash(rm -rf .tmp:*)", + "Bash(rm -rf temp:*)", + "Bash(rm -rf .cache:*)", + "Bash(rm -rf vendor:*)", + "Bash(rm -rf .gradle:*)", + "Bash(rm -rf .DS_Store:*)", + "Bash(rm -rf *.log:*)" + ] +} +``` + +**Rationale:** These are: +- Relative paths (not absolute) +- Well-known build/cache/temp directories +- Easily regenerated +- Very commonly cleaned during development + +### ⚠️ Moderate Risk (Could be ASK) + +```json +{ + "ask": [ + "Bash(rm -rf test/*:*)", // Test fixtures + "Bash(rm -rf spec/*:*)", // Test fixtures + "Bash(rm -rf docs/*:*)", // Documentation + "Bash(rm -rf .git/objects:*)", // Git internals (sometimes needed for repairs) + "Bash(rm -rf pkg:*)", // Package output + "Bash(rm -rf vendor/bundle:*)" // Bundler cache + ] +} +``` + +**Rationale:** These could contain important content but are still project-relative. + +### 🚫 Dangerous Patterns (Keep as DENY) + +```json +{ + "deny": [ + "Bash(rm -rf /:*)", // Root filesystem + "Bash(rm -rf /*:*)", // All root contents + "Bash(rm -rf ~:*)", // Home directory + "Bash(rm -rf ~/*:*)", // Home contents + "Bash(rm -rf $HOME:*)", // Home via variable + "Bash(rm -rf .:*)", // Current directory (too broad) + "Bash(rm -rf ..:*)", // Parent directory + "Bash(rm -rf ../*:*)", // Parent contents + "Bash(rm -rf *:*)" // Everything in current dir (context-dependent) + ] +} +``` + +**Rationale:** These can cause catastrophic data loss regardless of context. + +## sudo Pattern Analysis + +### ✅ Safe Patterns (Could be ALLOW) + +Service and package management: + +```json +{ + "allow": [ + "Bash(sudo systemctl start:*)", + "Bash(sudo systemctl stop:*)", + "Bash(sudo systemctl restart:*)", + "Bash(sudo systemctl status:*)", + "Bash(sudo systemctl enable:*)", + "Bash(sudo systemctl disable:*)", + "Bash(sudo journalctl:*)", + "Bash(sudo docker:*)", + "Bash(sudo apt-get update:*)", + "Bash(sudo apt-get install:*)", + "Bash(sudo yum install:*)", + "Bash(sudo dnf install:*)" + ] +} +``` + +**Rationale:** These are standard operations that require elevation but aren't destructive. + +### ⚠️ Moderate Risk (Could be ASK) + +File permissions and selective operations: + +```json +{ + "ask": [ + "Bash(sudo chmod:*)", + "Bash(sudo chown:*)", + "Bash(sudo chgrp:*)", + "Bash(sudo mkdir:*)", + "Bash(sudo mv:*)", + "Bash(sudo cp:*)", + "Bash(sudo ln:*)", + "Bash(sudo kill:*)", + "Bash(sudo killall:*)", + "Bash(sudo shutdown:*)", + "Bash(sudo reboot:*)", + "Bash(sudo halt:*)" + ] +} +``` + +**Rationale:** Legitimate operations that warrant confirmation. + +### 🚫 Dangerous Patterns (Keep as DENY) + +```json +{ + "deny": [ + "Bash(sudo rm -rf:*)", // Destructive file operations + "Bash(sudo dd:*)", // Can destroy disks + "Bash(sudo mkfs:*)", // Formats filesystems + "Bash(sudo fdisk:*)", // Disk partitioning + "Bash(sudo parted:*)", // Disk partitioning + "Bash(sudo mount:*)", // Filesystem mounting (risky) + "Bash(sudo umount:*)" // Filesystem unmounting (risky) + ] +} +``` + +**Rationale:** These can cause irreversible system damage. + +## git push --force Pattern Analysis + +### ✅ Safe Patterns (Could be ALLOW) + +Force-push with safety rails: + +```json +{ + "allow": [ + "Bash(git push --force-with-lease:*)", // Safer alternative + "Bash(git push --force-with-lease origin HEAD:*)" + ] +} +``` + +**Rationale:** `--force-with-lease` checks that remote hasn't changed, preventing accidental overwrites of others' work. + +### ⚠️ Moderate Risk (Could be ASK) + +Feature branch force-pushes: + +```json +{ + "ask": [ + "Bash(git push --force:*)", + "Bash(git push -f:*)" + ] +} +``` + +**Rationale:** Sometimes necessary after rebasing, but should require confirmation to prevent accidents. + +### 🚫 Dangerous Patterns (Could add as specific DENY) + +```json +{ + "deny": [ + "Bash(git push --force origin main:*)", + "Bash(git push --force origin master:*)", + "Bash(git push -f origin main:*)", + "Bash(git push -f origin master:*)", + "Bash(git push --force upstream main:*)", + "Bash(git push --force upstream master:*)" + ] +} +``` + +**Rationale:** Force-pushing to main branches can break team workflows. Better to use GitHub/GitLab protections. + +## git reset --hard Pattern Analysis + +### ✅ Safe Patterns (Could be ALLOW) + +Resetting to specific known-good states: + +```json +{ + "allow": [ + "Bash(git reset --hard origin/*:*)", // Reset to remote state + "Bash(git reset --hard upstream/*:*)", // Reset to upstream + "Bash(git reset --hard HEAD:*)" // Discard working changes + ] +} +``` + +**Rationale:** These are common operations to align with remote or discard local experiments. + +### ⚠️ Moderate Risk (Could be ASK) + +Arbitrary resets: + +```json +{ + "ask": [ + "Bash(git reset --hard:*)" // Any reset not covered above + ] +} +``` + +**Rationale:** Might be resetting to arbitrary commits, worth confirming. + +## git clean Pattern Analysis + +### ✅ Safe Patterns (Could be ALLOW) + +Selective cleaning: + +```json +{ + "allow": [ + "Bash(git clean -fd node_modules:*)", + "Bash(git clean -fd dist:*)", + "Bash(git clean -fd build:*)", + "Bash(git clean -n:*)", // Dry-run (always safe) + "Bash(git clean --dry-run:*)" + ] +} +``` + +### ⚠️ Moderate Risk (Could be ASK) + +Broad cleaning: + +```json +{ + "ask": [ + "Bash(git clean -fd:*)", // Clean untracked files + "Bash(git clean -fdx:*)" // Clean including ignored + ] +} +``` + +**Rationale:** Can remove uncommitted work, but sometimes needed to return to clean state. + +## Pipe-to-Shell Pattern Analysis + +### 🚫 Almost Always Dangerous (Keep as DENY) + +```json +{ + "deny": [ + "Bash(curl * | bash)", + "Bash(curl * | sh)", + "Bash(wget * | bash)", + "Bash(wget * | sh)" + ] +} +``` + +**Exception case (could add specific ALLOW):** + +```json +{ + "allow": [ + // Only for well-known, trusted install scripts + "Bash(curl -sSL https://get.docker.com | sh)", + "Bash(curl -fsSL https://mise.jdx.dev/install.sh | sh)", + "Bash(curl https://sh.rustup.rs | sh)" + ] +} +``` + +**Rationale:** Pipe-to-shell is dangerous because you're executing code sight-unseen. Only allow specific, well-known installers if absolutely needed. Better to download first, inspect, then execute. + +## Recommended Implementation Strategy + +### Option 1: Granular Safe Lists (More Work, More Control) + +Create detailed allow/ask/deny patterns as shown above. This gives maximum control but requires maintaining more rules. + +**Pros:** +- Predictable behavior +- Clear intent +- Easy to audit + +**Cons:** +- More maintenance +- Can't anticipate every safe pattern +- Might get repetitive + +### Option 2: Pattern-Based Validation (More Complex, More Flexible) + +Use pattern matching to detect dangerous characteristics: + +```bash +# Pseudo-logic +if starts_with("/") or contains("~") or contains(".."): + deny +elif is_common_build_artifact: + allow +else: + ask +``` + +**Pros:** +- Fewer explicit rules +- Adapts to new patterns +- Less repetitive + +**Cons:** +- Requires custom validation logic +- Harder to reason about +- Claude Code might not support this level of pattern matching + +### Option 3: Hybrid Approach (Recommended) + +1. **ALLOW:** Very common safe patterns (node_modules, dist, build, etc.) +2. **ASK:** General patterns (most git operations, relative rm -rf) +3. **DENY:** Known dangerous patterns (absolute paths, system directories) + +This balances safety with usability. + +## Proposed Changes to Your Permissions + +### permissions.json + +```json +{ + "allow": [ + // Safe rm -rf patterns + "Bash(rm -rf node_modules:*)", + "Bash(rm -rf dist:*)", + "Bash(rm -rf build:*)", + "Bash(rm -rf target:*)", + "Bash(rm -rf .next:*)", + "Bash(rm -rf coverage:*)", + "Bash(rm -rf tmp:*)", + "Bash(rm -rf .cache:*)", + + // Safe sudo patterns + "Bash(sudo systemctl:*)", + "Bash(sudo journalctl:*)", + "Bash(sudo docker:*)" + ], + "ask": [ + // Moderate risk - warrant confirmation + "Bash(sudo chmod:*)", + "Bash(sudo chown:*)", + "Bash(sudo shutdown:*)", + "Bash(sudo reboot:*)" + ], + "deny": [ + // Keep existing dangerous patterns + "Bash(curl * | bash)", + "Bash(curl * | sh)", + + // Add specific dangerous sudo patterns + "Bash(sudo rm -rf:*)", + "Bash(sudo dd:*)", + "Bash(sudo mkfs:*)", + + // Add specific dangerous rm patterns + "Bash(rm -rf /:*)", + "Bash(rm -rf /*:*)", + "Bash(rm -rf ~:*)", + "Bash(rm -rf ~/*:*)", + "Bash(rm -rf $HOME:*)" + ] +} +``` + +### permissions.git.jsonc + +```json +{ + "allow": [ + // Keep all existing safe operations... + + // Add safer force-push alternative + "Bash(git push --force-with-lease:*)", + + // Add common reset patterns + "Bash(git reset --hard origin/*:*)", + "Bash(git reset --hard upstream/*:*)", + "Bash(git reset --hard HEAD:*)" + ], + "ask": [ + // Move from deny to ask + "Bash(git clean -fd:*)", + "Bash(git push --force:*)", + "Bash(git push -f:*)", + "Bash(git reset --hard:*)" // Catch-all for other resets + ], + "deny": [ + // Specific protections for main branches + "Bash(git push --force origin main:*)", + "Bash(git push --force origin master:*)", + "Bash(git push -f origin main:*)", + "Bash(git push -f origin master:*)" + ] +} +``` + +## Testing Strategy + +Before committing these changes: + +1. **Test safe patterns work:** + ```bash + # Should be auto-allowed + rm -rf node_modules + rm -rf dist + ``` + +2. **Test ask prompts appear:** + ```bash + # Should prompt + git push --force + sudo chmod 755 file + ``` + +3. **Test denials work:** + ```bash + # Should be blocked + curl http://example.com/script.sh | bash + rm -rf /tmp/test # Absolute path + ``` + +4. **Verify no regressions** in normal workflows + +## Questions for Decision + +1. **How aggressive should safe rm -rf patterns be?** + - Conservative: Only node_modules, dist, build + - Moderate: Add tmp, coverage, cache + - Aggressive: Most relative paths + +2. **Should sudo ever be auto-allowed?** + - My recommendation: Only for systemctl, journalctl + - Alternative: All sudo requires ask + +3. **Force-push protection:** + - Should we deny force-push to main/master? + - Or trust branch protection rules on GitHub? + - What about other important branches (develop, staging)? + +4. **Pattern explosion:** + - Are you OK maintaining more granular rules? + - Or prefer fewer, broader patterns with more asks? diff --git a/home/.claude/skills/permissions-manager/docs/wildcard-safety.md b/home/.claude/skills/permissions-manager/docs/wildcard-safety.md new file mode 100644 index 0000000..ea9eb49 --- /dev/null +++ b/home/.claude/skills/permissions-manager/docs/wildcard-safety.md @@ -0,0 +1,319 @@ +# Permission Wildcard Safety Analysis + +## The Problem with Trailing Wildcards + +### Current Pattern (Potentially Dangerous) + +```json +"Bash(rm -rf node_modules:*)" +``` + +This matches: +- ✅ `rm -rf node_modules` - Safe +- ✅ `rm -rf node_modules/` - Safe +- ⚠️ `rm -rf node_modules --verbose` - Probably safe +- 🚫 `rm -rf node_modules ../important-dir` - **DANGEROUS!** +- 🚫 `rm -rf node_modules /tmp/secrets` - **DANGEROUS!** + +The `:*` wildcard means "with any additional arguments", which could include additional paths to delete! + +### How Permission Matching Works + +Claude Code permission format: +``` +Bash(command args:wildcard) +``` + +- `command` - The actual command (rm, git, etc.) +- `args` - Specific arguments to match +- `:*` - Wildcard for additional arguments + +Examples: +- `Bash(git status:*)` - Matches `git status` with any extra args +- `Bash(npm install:*)` - Matches `npm install` plus anything after +- `Bash(rm -rf node_modules:*)` - Matches `rm -rf node_modules` plus anything after (DANGER!) + +## Safer Alternatives + +### Option 1: Exact Match (Most Restrictive) + +```json +{ + "allow": [ + "Bash(rm -rf node_modules)", + "Bash(rm -rf dist)", + "Bash(rm -rf build)" + ] +} +``` + +**Pros:** +- Maximum safety - only exact command is allowed +- No risk of additional paths being deleted + +**Cons:** +- Doesn't support useful flags like `-v` (verbose) +- Might not match if shell expands paths (e.g., `rm -rf ./node_modules`) + +### Option 2: Specific Safe Variations + +```json +{ + "allow": [ + "Bash(rm -rf node_modules)", + "Bash(rm -rf ./node_modules)", + "Bash(rm -rf node_modules/)", + "Bash(rm -rf ./node_modules/)", + "Bash(rm -rf dist)", + "Bash(rm -rf ./dist)", + "Bash(rm -rf build)", + "Bash(rm -rf ./build)" + ] +} +``` + +**Pros:** +- Covers common path variations +- Still very safe - no extra arguments + +**Cons:** +- More verbose +- Still doesn't support flags + +### Option 3: Pattern-Based Validation (Ideal but Complex) + +What we'd ideally want: +``` +Allow "rm -rf X" where X matches safe patterns and no additional args +``` + +This would require custom logic that Claude Code may not support. + +### Option 4: Keep Wildcards but Add Dangerous Patterns to Deny + +```json +{ + "allow": [ + "Bash(rm -rf node_modules:*)", + "Bash(rm -rf dist:*)" + ], + "deny": [ + "Bash(rm -rf node_modules /:*)", + "Bash(rm -rf node_modules ~:*)", + "Bash(rm -rf node_modules ..:*)", + "Bash(rm -rf node_modules /*:*)", + "Bash(rm -rf dist /:*)", + "Bash(rm -rf dist ~:*)" + // ... would need to enumerate many dangerous combinations + ] +} +``` + +**Pros:** +- Allows flags and variations + +**Cons:** +- Deny rules may not work as expected (allow might take precedence) +- Hard to enumerate all dangerous patterns +- Still leaves gaps + +## Real-World Risk Assessment + +### How likely is exploitation? + +**Accidental:** +- Low risk - Claude is unlikely to accidentally add dangerous extra paths +- Most commands are generated as single targets + +**Malicious:** +- Could a malicious prompt trick Claude into `rm -rf node_modules /important`? +- Possible, but would require specific prompt injection +- Multiple layers of defense (Claude's safety training, permission prompts) + +### Comparison with Other Permissions + +**Current wildcards that are probably fine:** +```json +"Bash(npm install:*)" // Extra args are package names - safe +"Bash(git status:*)" // Extra args are paths/flags - safe +"Bash(jq:*)" // Extra args are filters/files - safe +``` + +**Current wildcards that are concerning:** +```json +"Bash(rm -rf node_modules:*)" // Extra args could be more paths +"Bash(rm -rf dist:*)" // Same issue +"Bash(sudo systemctl:*)" // Extra args... probably ok? +``` + +## Recommendations + +### Conservative Approach (Recommended) + +Remove the trailing `:*` from rm commands: + +```json +{ + "allow": [ + "Bash(rm -rf node_modules)", + "Bash(rm -rf ./node_modules)", + "Bash(rm -rf dist)", + "Bash(rm -rf ./dist)", + "Bash(rm -rf build)", + "Bash(rm -rf ./build)", + "Bash(rm -rf target)", + "Bash(rm -rf ./target)", + "Bash(rm -rf coverage)", + "Bash(rm -rf ./coverage)", + "Bash(rm -rf tmp)", + "Bash(rm -rf ./tmp)", + "Bash(rm -rf temp)", + "Bash(rm -rf ./temp)", + "Bash(rm -rf .cache)", + "Bash(rm -rf .next)", + "Bash(rm -rf out)", + "Bash(rm -rf .pytest_cache)", + "Bash(rm -rf .rspec_cache)", + "Bash(rm -rf .DS_Store)", + "Bash(rm -rf .tmp)", + "Bash(rm -rf .coverage)", + "Bash(rm -rf .gradle)", + "Bash(rm -rf vendor)", + "Bash(rm -rf pkg)" + ] +} +``` + +**Rationale:** +- `rm` commands rarely need additional arguments beyond the path +- The safety benefit outweighs the minor inconvenience +- If you need verbose output, you can approve it when prompted + +### Moderate Approach + +Keep wildcards for commands where extra args are clearly safe: + +```json +{ + "allow": [ + // rm commands - no wildcards + "Bash(rm -rf node_modules)", + "Bash(rm -rf dist)", + + // Other commands - wildcards ok + "Bash(npm install:*)", // Extra args are packages + "Bash(git status:*)", // Extra args are paths + "Bash(sudo systemctl:*)" // Extra args are services + ] +} +``` + +### What About sudo Commands? + +Current patterns: +```json +"Bash(sudo systemctl start:*)", +"Bash(sudo systemctl stop:*)", +"Bash(sudo docker:*)" +``` + +**Analysis:** +- `sudo systemctl start:*` - Extra args are service names (safe) +- `sudo docker:*` - Extra args are docker commands (safe) +- These wildcards are probably fine + +## Testing the Risk + +You can test whether wildcards allow multiple paths: + +```bash +# If you have the current permissions: +# Does this get allowed or blocked? +rm -rf node_modules /tmp/test-danger +``` + +If it's **allowed**, the wildcards are dangerous. +If it's **blocked**, maybe Claude Code has additional parsing that prevents this. + +## Implementation Plan + +1. **Test current behavior** to understand how wildcards work +2. **If dangerous:** Update `permissions.json` to remove `:*` from rm commands +3. **Regenerate:** Run `./claudeconfig.sh` +4. **Validate:** Try common operations still work +5. **Monitor:** Watch for legitimate operations being blocked + +## Specific Changes Needed + +### File: `claude/permissions.json` + +**Current (potentially unsafe):** +```json +{ + "allow": [ + "Bash(rm -rf node_modules:*)", + "Bash(rm -rf dist:*)", + "Bash(rm -rf build:*)", + "Bash(rm -rf target:*)", + "Bash(rm -rf .next:*)", + "Bash(rm -rf out:*)", + "Bash(rm -rf coverage:*)", + "Bash(rm -rf .coverage:*)", + "Bash(rm -rf .pytest_cache:*)", + "Bash(rm -rf .rspec_cache:*)", + "Bash(rm -rf tmp:*)", + "Bash(rm -rf .tmp:*)", + "Bash(rm -rf temp:*)", + "Bash(rm -rf .cache:*)", + "Bash(rm -rf vendor:*)", + "Bash(rm -rf pkg:*)", + "Bash(rm -rf .gradle:*)", + "Bash(rm -rf .DS_Store:*)" + ] +} +``` + +**Proposed (safer):** +```json +{ + "allow": [ + "Bash(rm -rf node_modules)", + "Bash(rm -rf ./node_modules)", + "Bash(rm -rf dist)", + "Bash(rm -rf ./dist)", + "Bash(rm -rf build)", + "Bash(rm -rf ./build)", + "Bash(rm -rf target)", + "Bash(rm -rf ./target)", + "Bash(rm -rf .next)", + "Bash(rm -rf ./. next)", + "Bash(rm -rf out)", + "Bash(rm -rf ./out)", + "Bash(rm -rf coverage)", + "Bash(rm -rf ./coverage)", + "Bash(rm -rf .coverage)", + "Bash(rm -rf .pytest_cache)", + "Bash(rm -rf .rspec_cache)", + "Bash(rm -rf tmp)", + "Bash(rm -rf ./tmp)", + "Bash(rm -rf .tmp)", + "Bash(rm -rf temp)", + "Bash(rm -rf ./temp)", + "Bash(rm -rf .cache)", + "Bash(rm -rf vendor)", + "Bash(rm -rf ./vendor)", + "Bash(rm -rf pkg)", + "Bash(rm -rf ./pkg)", + "Bash(rm -rf .gradle)", + "Bash(rm -rf .DS_Store)" + ] +} +``` + +Note: Doubled entries for with/without `./` prefix to handle different shell behaviors. + +## Conclusion + +**The trailing wildcards on rm -rf commands are a potential security risk.** While the practical risk is low (Claude is unlikely to generate malicious commands), the defense-in-depth principle suggests removing them. + +**Recommendation:** Remove `:*` from all `rm -rf` permissions and instead enumerate the common path variations (`./node_modules` vs `node_modules`). diff --git a/home/.claude/skills/permissions-manager/prompt.md b/home/.claude/skills/permissions-manager/prompt.md new file mode 100644 index 0000000..14fe5f0 --- /dev/null +++ b/home/.claude/skills/permissions-manager/prompt.md @@ -0,0 +1,294 @@ +# Permissions Manager Skill + +You are a Claude Code permissions management assistant. Your role is to help analyze, document, and maintain Claude Code permissions across projects. + +## Core Principles + +1. **Security First**: Prefer exact matches over wildcards for destructive operations +2. **Three-Tier System**: Allow (safe), Ask (moderate risk), Deny (catastrophic) +3. **Privacy**: Redact project names and paths from documentation +4. **Timestamped Docs**: All generated documentation includes ISO timestamps + +## Commands + +### `/permissions-manager analyze` + +**Purpose**: Analyze current permissions and generate recommendations. + +**Workflow**: + +1. **Gather Data**: + ```bash + claude-permissions --aggregate > /tmp/permissions-analysis-$(date +%s).txt + ``` + +2. **Analyze Patterns**: + - Count frequency of each permission across projects + - Identify candidates for promotion to global (appearing 2+ times) + - Check for dangerous wildcards (`:*` on rm, sudo, etc.) + - Find missing ecosystem files for commonly-used tools + +3. **Generate Recommendations**: + - High priority: 4+ occurrences + - Medium priority: 2-3 occurrences + - Check for deny rules that could be ask + - Identify unsafe wildcard patterns + +4. **Create Documentation**: + - Generate analysis in timestamped directory: `doc/permissions/YYYY-MM-DD/` + - Files created: + - `analysis.md` - Main recommendations + - `aggregate.txt` - Raw aggregated data + - `wildcards.txt` - Wildcard safety analysis + - Include in analysis.md: + - Current permission stats (allow/ask/deny counts) + - Frequency analysis (REDACTED project names) + - Recommendations by priority + - Security concerns + - Proposed changes + - **IMPORTANT**: Redact all project names, replace with generic "Project A", "Project B", etc. + +5. **Output Format**: + ```markdown + # Claude Permissions Analysis + + **Generated**: YYYY-MM-DDTHH:MM:SSZ + **Analyzer Version**: 1.0.0 + + ## Current State + + | Metric | Count | + |--------|-------| + | Allow | XXX | + | Ask | XXX | + | Deny | XXX | + + ## High-Frequency Patterns (4+ occurrences) + + - `Bash(command:*)` - 5 occurrences across 5 projects + + ## Medium-Frequency Patterns (2-3 occurrences) + + - `Bash(tool:*)` - 3 occurrences across 3 projects + + ## Recommendations + + ### Promote to Global + + 1. Add to `permissions.shell.json`: + - `Bash(common-tool:*)` + + ### Security Improvements + + 1. Remove dangerous wildcards from: + - `Bash(rm -rf path:*)` → `Bash(rm -rf path)` + + ### Consider Ask Instead of Deny + + 1. `Bash(potentially-useful-command:*)` - appears in X projects + ``` + +### `/permissions-manager apply` + +**Purpose**: Interactively apply permission changes. + +**Workflow**: + +1. **Ask for confirmation** before making any changes: + - "Ready to apply permission changes? This will modify files in claude/ directory." + +2. **For each recommendation**: + - Show the specific change + - Ask: "Apply this change? (yes/no/skip all)" + - If yes: Make the change + - Track all changes made + +3. **After all changes**: + - Run `./claudeconfig.sh` to regenerate settings + - Run `claude-permissions cleanup --dry-run` to preview cleanup + - Ask: "Run cleanup? This will remove duplicate permissions from projects." + - If yes: Run `claude-permissions cleanup --force` + +4. **Create summary document**: + - Generate `doc/permissions/YYYY-MM-DD/changes.md` + - List all changes made + - Include before/after stats + - Note which files were modified/created + +5. **Suggest commit**: + - Show proposed commit message + - Ask if user wants to commit + +### `/permissions-manager review` + +**Purpose**: Review current permission configuration. + +**Workflow**: + +1. **Display current stats**: + ```bash + jq '.permissions | { + allow: (.allow | length), + ask: (.ask | length), + deny: (.deny | length) + }' ~/.claude/settings.json + ``` + +2. **List permission files**: + ```bash + ls -1 claude/permissions*.json* + ``` + +3. **Show recent changes**: + ```bash + git log --oneline --grep="permission" -5 + ``` + +4. **Check for issues**: + - Dangerous wildcards in allow list + - Overly broad deny rules + - Missing common ecosystems + +## Privacy & Redaction + +When generating documentation, **always redact** private information: + +- **Project names**: Replace with "Project A", "Project B", etc. +- **File paths**: Replace with relative paths or `/path` +- **Domain names**: Keep only well-known public domains +- **Command arguments**: Redact specific values, keep patterns + +**Example Redaction**: +``` +Before: gusto-app/api/users +After: Project-A/api/users + +Before: ~/workspace/client-project +After: + +Before: internal-tool.company.com +After: +``` + +## Wildcard Safety Analysis + +When reviewing permissions, flag these dangerous patterns: + +### Unsafe Wildcards + +1. **rm with wildcards**: `Bash(rm -rf target:*)` + - Risk: Could match `rm -rf target /important` + - Fix: Use exact match `Bash(rm -rf target)` + +2. **sudo with broad wildcards**: `Bash(sudo:*)` + - Risk: Allows any sudo command + - Fix: Be specific about allowed sudo operations + +3. **Pipe to shell**: `Bash(curl * | bash)` + - Risk: Executes arbitrary remote code + - Fix: Always deny, or allow specific trusted sources only + +### Safe Wildcards + +1. **Tools with arguments**: `Bash(npm install:*)` + - Safe: Extra args are package names + - OK: Wildcard is appropriate here + +2. **Read-only operations**: `Bash(git log:*)` + - Safe: Read-only, extra args are filters + - OK: Wildcard is appropriate here + +## Helper Scripts + +The skill includes these helper scripts in `scripts/`: + +- `aggregate-permissions.sh` - Gather permission data +- `redact-projects.sh` - Redact private information from docs +- `analyze-wildcards.sh` - Find potentially dangerous wildcards +- `generate-recommendations.sh` - Create recommendation list + +## File Structure + +``` +home/.claude/skills/permissions-manager/ +├── skill.json # Skill manifest +├── prompt.md # This file +├── scripts/ +│ ├── aggregate-permissions.sh +│ ├── redact-projects.sh +│ ├── analyze-wildcards.sh +│ └── generate-recommendations.sh +└── README.md # User documentation +``` + +## Usage Examples + +### Full Analysis Workflow + +```bash +/permissions-manager analyze +# Reviews permissions, generates recommendations doc + +/permissions-manager apply +# Interactively applies recommended changes + +/permissions-manager review +# Shows current state after changes +``` + +### Quick Check + +```bash +/permissions-manager review +# Just see current stats and recent changes +``` + +## Best Practices + +1. **Run analysis before major changes**: Understand the current state +2. **Review recommendations carefully**: Not all high-frequency patterns should be promoted +3. **Test after applying**: Ensure common workflows still work +4. **Commit docs**: Analysis docs (redacted) are safe to commit +5. **Iterate**: Run cleanup periodically to keep permissions centralized + +## Integration with Dotfiles + +This skill is part of the dotfiles repository and: +- Lives in `home/.claude/skills/permissions-manager/` +- Gets symlinked to `~/.claude/skills/permissions-manager/` during install +- Works with the permission system defined in `claude/` +- Uses `claudeconfig.sh` for regeneration +- Integrates with `claude-permissions` tool + +## Security Notes + +- Never auto-apply changes without user confirmation +- Always show what will change before modifying files +- Preserve local-only settings (awsAuthRefresh, env) +- Backup settings.json before major changes +- Keep deny rules for truly dangerous operations + +## Troubleshooting + +**Issue**: Analysis shows no recommendations +- Check if `claude-permissions` is installed +- Verify you have project-local permissions to analyze + +**Issue**: Redaction not working +- Ensure `scripts/redact-projects.sh` is executable +- Check that git repo list is accessible + +**Issue**: Apply fails +- Check file permissions in `claude/` directory +- Verify `claudeconfig.sh` is executable +- Ensure `jq` is installed + +## Future Enhancements + +Potential improvements for this skill: + +1. **Machine learning**: Detect permission patterns automatically +2. **Diff visualization**: Show before/after permission diffs +3. **Test mode**: Dry-run permissions to see what would be blocked +4. **Integration**: Sync with CI/CD permission policies +5. **Validation**: Check for conflicts between allow/deny rules diff --git a/home/.claude/skills/permissions-manager/scripts/aggregate-permissions.sh b/home/.claude/skills/permissions-manager/scripts/aggregate-permissions.sh new file mode 100755 index 0000000..515b2bf --- /dev/null +++ b/home/.claude/skills/permissions-manager/scripts/aggregate-permissions.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +# Aggregate permissions from all projects +set -euo pipefail + +TIMESTAMP=$(date +%s) +OUTPUT_FILE="${1:-/tmp/permissions-aggregate-${TIMESTAMP}.txt}" + +echo "Aggregating permissions from all projects..." +echo "Output: $OUTPUT_FILE" + +# Run claude-permissions with all relevant flags +claude-permissions --aggregate > "$OUTPUT_FILE" + +echo "✓ Aggregation complete" +echo "Total lines: $(wc -l < "$OUTPUT_FILE")" +echo "" +echo "Quick stats:" +echo " Allow entries: $(grep -c "^[[:space:]]*[0-9]x " "$OUTPUT_FILE" | head -1 || echo 0)" +echo " Deny entries: $(awk '/^## deny/,/^## ask/ {print}' "$OUTPUT_FILE" | grep -c "^[[:space:]]*[0-9]x " || echo 0)" +echo " Ask entries: $(awk '/^## ask/,/^$/ {print}' "$OUTPUT_FILE" | grep -c "^[[:space:]]*[0-9]x " || echo 0)" diff --git a/home/.claude/skills/permissions-manager/scripts/analyze-wildcards.sh b/home/.claude/skills/permissions-manager/scripts/analyze-wildcards.sh new file mode 100755 index 0000000..499f746 --- /dev/null +++ b/home/.claude/skills/permissions-manager/scripts/analyze-wildcards.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env bash +# Analyze permissions for potentially dangerous wildcards +set -euo pipefail + +SETTINGS_FILE="${1:-${HOME}/.claude/settings.json}" + +if [[ ! -f "$SETTINGS_FILE" ]]; then + echo "Error: Settings file not found: $SETTINGS_FILE" + exit 1 +fi + +echo "Analyzing wildcards in: $SETTINGS_FILE" +echo "" + +# Check for dangerous patterns +echo "=== DANGEROUS WILDCARDS ===" +echo "" + +# rm -rf with wildcards +echo "## rm -rf with wildcards (potential multi-path deletion):" +jq -r '.permissions.allow[]? | select(contains("rm -rf") and contains(":*"))' "$SETTINGS_FILE" || echo " (none found)" +echo "" + +# sudo with broad wildcards +echo "## sudo with broad wildcards:" +jq -r '.permissions.allow[]? | select(startswith("Bash(sudo") and endswith(":*"))' "$SETTINGS_FILE" | head -5 || echo " (none found)" +if jq -r '.permissions.allow[]? | select(startswith("Bash(sudo") and endswith(":*"))' "$SETTINGS_FILE" | wc -l | grep -q -v "^0$"; then + echo " ... (review these carefully)" +fi +echo "" + +# Pipe to shell +echo "## Pipe to shell (should be in deny, not allow):" +jq -r '.permissions.allow[]? | select(contains("| bash") or contains("| sh"))' "$SETTINGS_FILE" || echo " (none found - good!)" +echo "" + +echo "=== QUESTIONABLE WILDCARDS ===" +echo "" + +# chmod/chown in allow (should probably be ask) +echo "## File permission changes in allow (consider ask):" +jq -r '.permissions.allow[]? | select(contains("chmod") or contains("chown"))' "$SETTINGS_FILE" || echo " (none found)" +echo "" + +echo "=== SAFE WILDCARDS ===" +echo "" + +# These are generally fine +echo "## Read-only operations (usually safe):" +jq -r '.permissions.allow[]? | select(contains("git log") or contains("git status") or contains("git diff"))' "$SETTINGS_FILE" | head -3 || echo " (none found)" +echo " ... (and others)" +echo "" + +echo "## Tool-specific arguments (usually safe):" +jq -r '.permissions.allow[]? | select(contains("npm install") or contains("cargo build") or contains("go build"))' "$SETTINGS_FILE" | head -3 || echo " (none found)" +echo " ... (and others)" +echo "" + +echo "=== SUMMARY ===" +total_allow=$(jq '.permissions.allow | length' "$SETTINGS_FILE") +wildcards=$(jq -r '.permissions.allow[]? | select(endswith(":*"))' "$SETTINGS_FILE" | wc -l) +echo "Total allow entries: $total_allow" +echo "With wildcards: $wildcards" +echo "Percentage: $(awk "BEGIN {printf \"%.1f%%\", ($wildcards/$total_allow)*100}")" diff --git a/home/.claude/skills/permissions-manager/scripts/generate-recommendations.sh b/home/.claude/skills/permissions-manager/scripts/generate-recommendations.sh new file mode 100755 index 0000000..61b0135 --- /dev/null +++ b/home/.claude/skills/permissions-manager/scripts/generate-recommendations.sh @@ -0,0 +1,217 @@ +#!/usr/bin/env bash +# Generate recommendations from aggregated permissions +set -euo pipefail + +AGGREGATE_FILE="$1" +OUTPUT_FILE="${2:-recommendations-$(date +%Y%m%d-%H%M%S).md}" + +if [[ ! -f "$AGGREGATE_FILE" ]]; then + echo "Error: Aggregate file not found: $AGGREGATE_FILE" + exit 1 +fi + +echo "Generating recommendations from: $AGGREGATE_FILE" +echo "Output: $OUTPUT_FILE" + +# Extract sections +extract_section() { + local section="$1" + local file="$2" + awk "/^## $section/,/^## / {print}" "$file" | grep "^[[:space:]]*[0-9]" || echo "" +} + +# Parse frequency and command +parse_entries() { + local input="$1" + echo "$input" | while read -r line; do + if [[ -z "$line" ]]; then continue; fi + freq=$(echo "$line" | awk '{print $1}' | tr -d 'x') + cmd=$(echo "$line" | cut -d' ' -f2-) + echo "$freq|$cmd" + done +} + +# Generate markdown +cat > "$OUTPUT_FILE" </dev/null || echo "unknown") +current_ask=$(jq '.permissions.ask | length' ~/.claude/settings.json 2>/dev/null || echo "unknown") +current_deny=$(jq '.permissions.deny | length' ~/.claude/settings.json 2>/dev/null || echo "unknown") + +cat >> "$OUTPUT_FILE" <> "$OUTPUT_FILE" +echo "" >> "$OUTPUT_FILE" +extract_section "allow" "$AGGREGATE_FILE" | \ + parse_entries | \ + awk -F'|' '$1 >= 4 {print $2}' | \ + sort -u | \ + while read -r cmd; do + if [[ -n "$cmd" ]]; then + echo "- \`$cmd\`" >> "$OUTPUT_FILE" + fi + done + +if ! grep -q "^- " "$OUTPUT_FILE" | tail -10; then + echo "(No patterns found)" >> "$OUTPUT_FILE" +fi +echo "" >> "$OUTPUT_FILE" + +# Medium frequency (2-3) +echo "## Medium-Frequency Patterns (2-3 projects)" >> "$OUTPUT_FILE" +echo "" >> "$OUTPUT_FILE" +extract_section "allow" "$AGGREGATE_FILE" | \ + parse_entries | \ + awk -F'|' '$1 >= 2 && $1 <= 3 {print $2}' | \ + sort -u | \ + while read -r cmd; do + if [[ -n "$cmd" ]]; then + echo "- \`$cmd\`" >> "$OUTPUT_FILE" + fi + done + +if ! grep -q "^- " "$OUTPUT_FILE" | tail -20; then + echo "(No patterns found)" >> "$OUTPUT_FILE" +fi +echo "" >> "$OUTPUT_FILE" + +# Deny analysis +echo "## Current Deny Rules" >> "$OUTPUT_FILE" +echo "" >> "$OUTPUT_FILE" +extract_section "deny" "$AGGREGATE_FILE" | \ + parse_entries | \ + sort -rn -t'|' -k1 | \ + while IFS='|' read -r freq cmd; do + if [[ -n "$cmd" ]]; then + echo "- \`$cmd\` (appears $freq times)" >> "$OUTPUT_FILE" + fi + done +echo "" >> "$OUTPUT_FILE" + +# Ask analysis +echo "## Current Ask Rules" >> "$OUTPUT_FILE" +echo "" >> "$OUTPUT_FILE" +extract_section "ask" "$AGGREGATE_FILE" | \ + parse_entries | \ + sort -rn -t'|' -k1 | \ + while IFS='|' read -r freq cmd; do + if [[ -n "$cmd" ]]; then + echo "- \`$cmd\` (appears $freq times)" >> "$OUTPUT_FILE" + fi + done +echo "" >> "$OUTPUT_FILE" + +# Recommendations section +cat >> "$OUTPUT_FILE" <= 3 && $2 ~ /^Bash\([a-z]+:/ {print $2}' | \ + grep -v "git\|npm\|cargo\|bundle\|pip" | \ + sort -u | \ + head -10 | \ + while read -r cmd; do + if [[ -n "$cmd" ]]; then + echo "- \`$cmd\`" >> "$OUTPUT_FILE" + fi + done + +cat >> "$OUTPUT_FILE" <= 2 {print $2}' | \ + grep -oE "Bash\([a-z]+" | \ + sed 's/Bash(//' | \ + sort | uniq -c | sort -rn | head -10 | \ + awk '{if ($1 >= 3) print "- `permissions." $2 ".json` (" $1 " commands)"}' \ + >> "$OUTPUT_FILE" + +cat >> "$OUTPUT_FILE" </dev/null | \ + while read -r perm; do + echo "- \`$perm\` → Use exact match" >> "$OUTPUT_FILE" + done || echo "- (No dangerous rm -rf wildcards found)" >> "$OUTPUT_FILE" + +cat >> "$OUTPUT_FILE" <> "$OUTPUT_FILE" + fi + done + +cat >> "$OUTPUT_FILE" <|g' "$OUTPUT_FILE" +perl -i -pe 's|/Users/[^/\s]+/workspace||g' "$OUTPUT_FILE" +perl -i -pe 's|/Users/[^/\s]+||g' "$OUTPUT_FILE" + +# Redact internal domains (keep common ones) +perl -i -pe 's/([a-z0-9-]+\.)(internal|corp|local)\b/$1/g' "$OUTPUT_FILE" + +# Redact API keys and tokens (just in case) +perl -i -pe 's/[A-Za-z0-9]{32,}//g' "$OUTPUT_FILE" + +echo "✓ Redaction complete" +echo "Total substitutions: $(grep -o "\|\|\|\|" "$OUTPUT_FILE" | wc -l)" diff --git a/home/.claude/skills/permissions-manager/skill.json b/home/.claude/skills/permissions-manager/skill.json new file mode 100644 index 0000000..c6983d1 --- /dev/null +++ b/home/.claude/skills/permissions-manager/skill.json @@ -0,0 +1,24 @@ +{ + "name": "permissions-manager", + "version": "1.0.0", + "description": "Analyze and manage Claude Code permissions across projects", + "author": "Josh Nichols", + "commands": [ + { + "name": "analyze", + "description": "Analyze permissions and generate recommendations", + "args": [] + }, + { + "name": "apply", + "description": "Apply permission changes interactively", + "args": [] + }, + { + "name": "review", + "description": "Review current permission configuration", + "args": [] + } + ], + "systemPromptFile": "prompt.md" +}