Install
$ agentstack add skill-malob-nix-config-audit-permissions ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Audit Claude Permissions
Scan project-local Claude Code settings files, aggregate permission patterns, and recommend promotions to global configuration.
Workflow Overview
This audit runs in three phases, each as a separate task. Use TaskCreate at the start to create all three tasks, then work through them sequentially with user input via AskUserQuestion.
- Phase 1: Promote to Global — Requires user judgment. Present candidates, get decisions, apply to global config.
- Phase 2: Automated Redundancy Cleanup — Script-driven. After Phase 1 promotions, run cleanup script to remove local permissions now covered by global. Present for sanity-checking, then apply.
- Phase 3: Judgment Calls — Everything requiring user input: security hygiene, policy conflicts, one-off cruft, empty file deletion, moderate-risk items.
Initial Setup
- Check for symlinked global settings:
readlink -f ~/.claude/settings.json
If the global settings file is a symlink, note the real path. All writes to ~/.claude/settings.json must edit the symlink target, not create a new file that replaces the symlink.
- Load user preferences from
~/.claude/cc-maintenance.local.md(if it exists). See User Preferences section below. Apply any configured defaults (risk tolerance, auto-cleanup preference).
- Read global CLAUDE.md from
~/.claude/CLAUDE.mdto identify tool preference policies for Phase 3 policy conflict detection.
- Run the discovery and extraction pipeline:
scripts/discover-settings.sh | xargs scripts/extract-permissions.py
- Read the actual global settings from
~/.claude/settings.json— compare against the real allow list, not just the static examples in this skill.
- Create tasks for the three phases:
TaskCreate: "Review and promote permissions to global config"
TaskCreate: "Automated redundancy cleanup"
TaskCreate: "Judgment calls: security, policy, and cruft"
- Analyze the data and categorize permissions (see Categorization Rules below).
Phase 1: Promote to Global
Goal: Identify permissions worth adding to global config and get user approval.
Present Findings
Show a summary table of promotion candidates:
## Promotion Candidates
### Strong Recommendations (safe patterns, multiple projects)
| Permission | Projects | Suggested Global Pattern |
| ---------- | -------- | ------------------------ |
| ... | ... | ... |
### Moderate Recommendations (review carefully)
| Permission | Projects | Notes |
| ---------- | -------- | ----- |
| ... | ... | ... |
### Cross-Project File Patterns
[If any Read/Write/Edit permissions reference paths outside their project directory
and appear in multiple projects, flag them here. Example: multiple projects have
`Write(~/.config/some-tool/config.json)` - might indicate a shared config worth
adding globally.]
Get User Decision
Use AskUserQuestion to let the user decide:
Question: "Which permissions should I add to global settings?"
Options:
- "Add all strong recommendations"
- "Add strong + moderate recommendations"
- "Let me pick specific ones" (then list individually)
- "Skip - don't add any"
Apply Changes
If user approves additions:
- Add selected permissions to
~/.claude/settings.json(or symlink target) - Respect existing logical groupings (git, nix, gh, etc.)
- Sort alphabetically within groups
- Use space-syntax:
Bash(cmd *)notBash(cmd:*) - Mark Phase 1 task as completed
Phase 2: Automated Redundancy Cleanup
Goal: Remove local permissions now covered by global config. This is mechanical — the script identifies exact matches; user just sanity-checks the list.
Preview Cleanup
Run the cleanup script in dry-run mode:
scripts/discover-settings.sh | scripts/cleanup-redundant.py
Present Findings
Show what would be removed:
## Redundant Permissions
| File | Permissions to Remove | Remaining |
| --------- | --------------------- | --------- |
| project-a | 5 (ls _, grep _, ...) | 12 |
| project-b | 3 (gh api \*, ...) | 8 |
| ... | ... | ... |
**Total:** X permissions across Y files
The script also normalizes any remaining colon-syntax (Bash(cmd:*)) to space-syntax (Bash(cmd *)) when applying.
Get User Decision
If auto_cleanup_redundant: true in user preferences, skip the question and apply directly (still show the summary). Otherwise:
Question: "Should I remove these redundant permissions from local files?"
Options:
- "Yes, clean them up"
- "Show me the full list first"
- "Skip cleanup"
Apply Changes
If user approves:
scripts/discover-settings.sh | scripts/cleanup-redundant.py --apply
Mark Phase 2 task as completed.
Phase 3: Judgment Calls
Goal: Everything that requires real user judgment — security risks, policy conflicts, stale cruft, and cleanup opportunities. Present all categories together.
Category A: Security Hygiene
Flag permissions that match these patterns:
High Risk (recommend removal):
Bash(curl *),Bash(wget *)— network exfiltration riskBash(rm *)— can delete any fileBash(source *)— executes arbitrary scriptsBash(eval *)— arbitrary code execution
Moderate Risk (review):
Bash(git reset *),Bash(git checkout *)— can discard workBash(pkill *),Bash(kill *)— process terminationBash(python *),Bash(python3 *),Bash(node *)— arbitrary code (flag if user hasn't consciously chosen this)
Adjust what counts as "moderate" vs "high" based on the user's risk_tolerance preference if set.
Category B: Policy Conflicts
Read the user's global ~/.claude/CLAUDE.md for stated tool preferences. Flag local permissions that conflict with those policies.
How to detect: Look for patterns like "prefer X over Y", "use X instead of Y", "avoid Y". Then scan all local permissions for uses of the deprecated tool. For example:
> If CLAUDE.md says "prefer tool X over built-in Y", flag all Y permissions across local settings as policy conflicts.
Present these as informational — the user may have valid reasons for specific overrides.
Category C: One-Off Cruft
- Hardcoded file paths (e.g.,
Bash(prettier --write /full/path/to/file.md)) - Incomplete shell constructs (
Bash(done),Bash(for file in *.rs)) - Very specific commands with no wildcards that look like debugging artifacts
- Duplicate entries
- Legacy colon-syntax permissions (
Bash(cmd:*)) that weren't caught by the cleanup script (e.g., ones without a global equivalent)
Category D: Cross-Project File Access
Read,Write, orEditpermissions for paths outside the project- Flag if the same external path appears in multiple projects (potential global candidate)
- Flag broad patterns like
Write(~/.config/*)as security concerns
Category F: MCP Tool Permissions
- Flag MCP tools with write/send/delete capabilities (message sending, data deletion, post creation) — these have side effects beyond the local environment
- Flag server-wide wildcards (
mcp__server__*) — convenient but auto-permits any future tools added to that server without review - Suggest consolidation: if all or most tools from a server are individually listed, suggest replacing with the server wildcard (with a note about the trade-off)
Category E: Empty File Deletion
After cleanup, identify settings files where:
- All permissions have been removed (empty allow list or no allow list)
- No other settings exist (no hooks, enabledPlugins, etc.)
Offer to delete these empty files entirely — they serve no purpose.
Present Findings
## Judgment Calls
### Security Hygiene
#### High Risk — Recommend Removal
| Permission | Project | Risk |
| -------------- | --------- | ----------------- |
| `Bash(curl *)` | project-x | Data exfiltration |
#### Moderate Risk — Review
| Permission | Project | Risk |
| ---------- | ------- | ---- |
| ... | ... | ... |
### Policy Conflicts
| Permission | Projects | Policy |
| --------------- | -------- | --------------------------- |
| `WebFetch(url)` | 3 | CLAUDE.md: prefer Firecrawl |
### One-Off Cruft
| Permission | Project |
| -------------------------------------------------- | --------- |
| `Bash(prettier --write /path/to/specific/file.md)` | project-y |
### External File Access
| Permission | Projects | Notes |
| ------------------------------ | ---------- | ------------------------------- |
| `Write(~/.config/tool/config)` | 3 projects | Shared config — consider global |
| `Edit(/etc/hosts)` | 1 project | System file — review necessity |
### MCP Tool Permissions
| Permission | Projects | Notes |
| -------------------------- | -------- | ------------------------------------------- |
| `mcp__slack__post_message` | 2 | Side effect: sends messages externally |
| `mcp__puppeteer__*` | 1 | Server wildcard — auto-permits future tools |
### Empty Settings Files
| File | Reason |
| ----------------------------------------- | ------------------------------------------ |
| `~/project-z/.claude/settings.local.json` | All permissions removed, no other settings |
Get User Decision
Use AskUserQuestion:
Question: "How should I handle the judgment call items?"
Options:
- "Remove all flagged items"
- "Remove high risk + cruft only"
- "Let me review each category"
- "Skip — keep everything"
If user wants to review categories, ask about each separately.
Apply Changes
Edit each affected settings.local.json to remove approved items. Delete empty settings files if approved.
Mark Phase 3 task as completed.
Session Wrap-Up
After all phases complete, offer to create or update ~/.claude/cc-maintenance.local.md with session learnings. Include:
- Any risk tolerance preferences expressed during the session
- Tool preference notes mentioned during review
- Freeform notes from user decisions (overrides, exceptions, per-project rules — anything the user said that should carry forward to future audits)
- Session history entry (date, summary of changes)
User Preferences
Location: ~/.claude/cc-maintenance.local.md
Read at audit start. If missing, proceed with defaults. At session end, offer to create/update.
Format: YAML frontmatter for structured preferences + markdown body for freeform notes.
---
# All preferences are optional
risk_tolerance: moderate # conservative | moderate | aggressive
auto_cleanup_redundant: true
---
## Tool Preferences
- Prefer Exa/Firecrawl over built-in WebSearch/WebFetch
## Risk Notes
- sqlite3 and op item get considered moderate risk
- uv run python treated as arbitrary code execution
## Notes
- Always keep python3 permissions in the data-science project
- Don't consolidate Firecrawl tools into a server wildcard
## Session History
### 2026-02-09
- Promoted 13 permissions to global
- Removed ~220 cruft permissions from Assistant project
Behavior:
risk_toleranceadjusts what gets flagged as moderate vs high in Phase 3auto_cleanup_redundantskips the confirmation prompt in Phase 2 (still shows summary)- Tool Preferences section supplements CLAUDE.md policy detection
- Notes section is a freeform catch-all for any user preferences that don't fit the structured sections — overrides, exceptions, per-project rules, anything the agent should remember across audits
- Session History provides context for future audits
Scripts
All scripts are in scripts/ relative to this skill directory.
scripts/discover-settings.sh— Finds all.claude/settings.local.jsonfiles across~usingfdwith sensible exclusions (Library, node_modules, .git, etc.). Max depth of 5 for performance.scripts/extract-permissions.py— Aggregates permissions from multiple settings files. Outputs JSON with each permission, occurrence count, and list of projects using it. Sorted by count descending.scripts/cleanup-redundant.py— Removes permissions from local files that are covered by global config. Handles cross-syntax matching (colon-syntax vs space-syntax). Normalizes remaining permissions to space-syntax on write. Dry-run by default; use--applyto modify files.
Usage examples:
# Extract and aggregate all permissions
scripts/discover-settings.sh | xargs scripts/extract-permissions.py
# Preview redundant permission cleanup (dry-run, default)
scripts/discover-settings.sh | scripts/cleanup-redundant.py
# Actually remove redundant permissions
scripts/discover-settings.sh | scripts/cleanup-redundant.py --apply
Categorization Rules
Formatting Rules
Use space-syntax for all permissions: Bash(cmd *) not Bash(cmd:*).
Reasonable Global Candidates
Patterns worth promoting. These operate on local project code or perform read-only operations. Compare against the actual global config, not just this static list — these are examples of the kinds of patterns to look for.
Git Commands (read-only): git branch *, git diff *, git log *, git show *, git status *
File Inspection (read-only): cat *, head *, tail *, ls *, find *, grep *, du *
Build and Check Commands: cargo build *, cargo test *, cargo check *, go build *, go test *, npm run build *, npm run test *, deno check *, deno lint *, xcodebuild *
System Utilities: open *, pbcopy, pbpaste, lsof *, ps *
Nix Commands: nix build *, nix-build *, nix develop *, nix eval *, nix flake *, nix path-info *, nix-prefetch-url *, nh darwin build *
Homebrew (read-only): brew info *, brew search *
GitHub CLI: gh api *, gh issue list *, gh issue view *, gh pr list *, gh pr view *, gh pr diff *, gh pr checks *, gh search *, gh run list *, gh run view *
Wildcards: * --help *, * --version
Pattern Generalization
When promoting, generalize cautiously — only for safe patterns:
| Local Pattern | Global Pattern | Notes | | ----------------------------- | ----------------------- | ---------------------------- | | Bash(npm run build) | Bash(npm run build *) | Safe — runs project scripts | | Bash(cargo test --release) | Bash(cargo test *) | Safe — tests local code | | Bash(nix build .#package) | Bash(nix build *) | Safe — sandboxed builds | | Bash(python3 script.py) | Keep specific or skip | Risky — arbitrary code | | WebFetch(domain:github.com) | Keep as-is | Domain patterns don't change |
Formatting Rules for Global Settings
When adding to ~/.claude/settings.json:
- Respect existing logical groupings (git, file inspection, nix, brew, gh, system utilities, build tools, wildcards, Skills, MCP tools)
- Within each group, sort alphabetically
- Place new permissions in the appropriate group based on command prefix
- Use space-syntax:
Bash(cmd *)notBash(cmd:*)
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: malob
- Source: malob/nix-config
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.