Skip to content
| Marketplace
Sign in
Visual Studio Code>Linters>PermDrift: AI Agent Permission MonitorNew to Visual Studio Code? Get it now.
PermDrift: AI Agent Permission Monitor

PermDrift: AI Agent Permission Monitor

jaytank_dev

| (0) | Free
Flags risky AI agent permissions and every change that widens them vs git HEAD, live, across Claude Code, Codex CLI, Copilot agent mode auto-approve, MCP and Gemini CLI configs.
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

PermDrift: AI Agent Permission Monitor

PermDrift watches the files that decide what your AI coding agents may do without asking - run shell commands, edit files, fetch URLs, call MCP tools - and tells you when those permissions get wider. It compares every permission file with its committed version (git HEAD), flags each grant that widens agent permissions on the exact line, and warns you live when an agent or another process widens them on disk.

Works with Claude Code, OpenAI Codex CLI, VS Code / GitHub Copilot agent mode, MCP clients and Gemini CLI.

Why

Agent permissions drift. Every "Yes, and don't ask again" adds an allow rule. A tired Bash(npm test *) becomes Bash(npm *), then Bash. A mode moves from default to acceptEdits to bypassPermissions. Sometimes the agent edits its own permission file. None of this shows up anywhere you normally look, and a one-line change in a JSON file is easy to wave through in code review.

PermDrift makes that drift visible:

  • In the editor: diagnostics on the exact line, with "before -> after" and a safer alternative.
  • In the status bar: Perms: 2 widened · 1 unrestricted, highlighted when something widened or safety is off.
  • Live: when a permission file is widened on disk by something other than your own VS Code save, a warning names what was added (+ Bash(git push *)) with a one-click diff against HEAD.
  • In review: a Markdown permission report to paste into the PR.

Supported agents and files

Agent Files What is read
Claude Code .claude/settings.json, .claude/settings.local.json permissions.allow / ask / deny, permissions.defaultMode, permissions.additionalDirectories, permissions.disableBypassPermissionsMode, enableAllProjectMcpServers, enabledMcpjsonServers
Claude Code .claude/agents/*.md frontmatter permissionMode, tools
Claude Code .claude/skills/**/SKILL.md frontmatter allowed-tools
OpenAI Codex CLI .codex/config.toml, codex.toml approval_policy, sandbox_mode, [sandbox_workspace_write] network_access (also inside [profiles.*])
VS Code / Copilot agent mode .vscode/settings.json chat.tools.global.autoApprove, chat.permissions.default, chat.tools.terminal.autoApprove, chat.tools.terminal.enableAutoApprove, chat.tools.edits.autoApprove, chat.tools.urls.autoApprove, claudeCode.initialPermissionMode
MCP clients .mcp.json, .vscode/mcp.json, .cursor/mcp.json, .roo/mcp.json per server alwaysAllow / autoApprove lists, trust
Gemini CLI .gemini/settings.json tools.allowed, general.defaultApprovalMode, security.disableYoloMode, mcpServers.*.trust

Files are found anywhere in the workspace (monorepos included). JSON files may contain comments and trailing commas.

Rules

ID Rule Default Bad Better
PD001 Widened vs HEAD - any grant present now but not at HEAD that increases capability Warning HEAD "Bash(npm test *)", now "Bash(npm *)" Keep the committed rule, or add only the exact command you need
PD002 Unrestricted shell - the agent may run any command Warning "allow": ["Bash"], "Bash(*)", run_shell_command, terminal auto-approve "/.*/": true "Bash(npm test *)", "Bash(git status)"
PD003 Dangerous command grant - an allow rule that auto-approves a destructive or outbound command Warning "Bash(git push *)", "Bash(git *)" (covers push), "Bash(sudo *)", bare WebFetch plus shell access Put them under ask, or deny them
PD004 Safety off - prompts are disabled wholesale Error "defaultMode": "bypassPermissions", approval_policy = "never" + sandbox_mode = "danger-full-access", "chat.tools.global.autoApprove": true, MCP "alwaysAllow": ["*"], MCP "trust": true acceptEdits / default with narrow allow rules; workspace-write + on-request
PD005 Local file shadowing - .claude/settings.local.json (usually gitignored, so never reviewed) grants more than the committed .claude/settings.json Information local-only "Bash(docker *)" Move shared grants into settings.json so they are reviewed

PD003 tests each shell grant against a curated list of sample commands: rm -rf, git push, git reset --hard, curl/wget piped to a shell, sudo, chmod 777, docker run --privileged, kubectl delete, terraform apply/destroy, npm publish, ssh/scp. A broad rule like Bash(git *) is flagged because it covers git push. Add your own samples with permDrift.extraDangerousCommands.

What counts as "widened" (PD001)

  • a new allow rule that is not already covered by a committed allow rule (Bash(npm *) replacing Bash(npm test *) is shown as Bash(npm test *) -> Bash(npm *); adding the narrower Bash(npm test *) next to Bash(npm *) is not flagged)
  • a removed deny or ask rule (unless a deny rule still covers it)
  • a mode or policy moved up the permissiveness order:
Setting Order, least -> most permissive Unset means
Claude Code defaultMode, agent permissionMode, claudeCode.initialPermissionMode dontAsk < plan < default (manual) < acceptEdits < auto < bypassPermissions default
Codex approval_policy untrusted < on-request = granular < on-failure < never on-request
Codex sandbox_mode read-only < workspace-write < danger-full-access read-only
VS Code chat.permissions.default default < autoApprove < autopilot default
Gemini general.defaultApprovalMode plan < default < auto_edit < yolo default

(dontAsk sounds permissive but only runs pre-approved tools and denies everything else, so it is the most locked-down Claude Code mode.)

  • a boolean switched on (enableAllProjectMcpServers, chat.tools.global.autoApprove, network_access, MCP trust), or a guard switched off (disableBypassPermissionsMode, disableYoloMode, chat.tools.terminal.enableAutoApprove: false)
  • a new entry in an auto-approve or allow list (additionalDirectories, enabledMcpjsonServers, MCP alwaysAllow/autoApprove, chat.tools.*.autoApprove, Gemini tools.allowed)
  • a subagent whose tools restriction was removed (it now inherits every tool)

Narrowing changes are never flagged.

How the HEAD comparison works

For each permission file PermDrift reads the committed version with git show HEAD:./<file> (from the file's own folder), extracts the grants from both versions and compares them. A file that is not in HEAD yet is reported once as a summary ("New file not in HEAD: all 4 grant(s) are new") instead of line by line. .claude/settings.local.json is normally gitignored, so it is checked against the committed settings.json instead (PD005).

The comparison refreshes when you edit, save, run PermDrift: Scan Now, or return to the window (to pick up commits made in a terminal).

Live alerts. A watcher follows every supported file. When a file changes on disk and VS Code did not save that file within the last 1.5 seconds (permDrift.saveWindowMs), the change is treated as external - an agent, a script, a git checkout. If the new content widens permissions compared with the previous content, you get a non-modal warning listing what was added, with Show diff (HEAD vs working tree) and Dismiss. Your own editor saves never trigger it.

Commands

Command What it does
PermDrift: Scan Now Re-read every permission file and refresh HEAD content
PermDrift: Show Widened Since HEAD Quick pick of every PD001 finding; pick one to jump to it
PermDrift: Copy Permission Report (Markdown) Findings grouped by agent and file, ready for a PR comment
PermDrift: Show HEAD Diff for Current File Side-by-side diff of the committed and working version
PermDrift: Show Agent Permissions Focus the Agent Permissions view in the Explorer (grouped by agent -> file -> finding; click to jump to the line)

Quick Fixes (lightbulb, applied only when you pick them): remove a single widened entry from a JSON array, or revert a mode / flag to its HEAD value. Nothing is ever changed automatically.

Settings

Setting Default Description
permDrift.enabled true Check agent permission files
permDrift.compareWithHead true PD001 comparison with git HEAD (trusted workspaces only)
permDrift.liveAlerts true Warn when a permission file is widened from outside the editor
permDrift.saveWindowMs 1500 Window that attributes a file change to your own VS Code save
permDrift.severity {} Per rule: error, warning, information, hint or off, e.g. { "PD005": "off" }
permDrift.extraDangerousCommands [] Extra sample commands for PD003, e.g. ["helm uninstall prod"]
permDrift.maxFiles 200 Maximum permission files scanned

Privacy and security

  • Everything runs locally. No network calls, no telemetry, no account.
  • Only permission keys are read. MCP env and headers blocks - where tokens usually live - are never parsed into findings, so their values (and names) never appear in messages, the tree or the report. Token-like strings inside rule text are masked as ***.
  • Workspace Trust: in an untrusted workspace the static rules (PD002-PD005) and live alerts still work because they only read files; the HEAD comparison (PD001) needs git, which honors repo-local configuration, so it turns on only after you trust the workspace.
  • Every git call uses an argument list (no shell), ignores global and system git config, sets GIT_OPTIONAL_LOCKS=0, and passes -c core.fsmonitor= --no-pager --no-ext-diff --no-textconv, so a hostile repo cannot run an fsmonitor hook, pager, diff driver or textconv filter.

Limitations

  • PermDrift reads project files only. User-level (~/.claude/settings.json, ~/.codex/config.toml, VS Code user settings) and managed / enterprise policy files are not compared.
  • Rule matching follows the documented wildcard syntax (*, trailing * and :*, mcp__server prefixes) but is not the agents' exact matcher; unusual patterns may be misjudged as covering or not covering each other.
  • PD003 is a curated sample list, not a shell parser; wrappers and compound commands are not expanded.
  • The Codex reader understands the keys above, basic strings, booleans and one-level inline tables - not the full TOML spec.
  • External-change detection is a timing heuristic: a VS Code save and an agent write within the same 1.5 seconds can be confused.
  • Key names were checked against the official Claude Code, Codex, VS Code and Gemini CLI docs in 2026-09; tools rename settings, so unknown keys are simply ignored.

How this differs from existing tools

  • agnix is a broad linter / language server for agent config files (CLAUDE.md, AGENTS.md, skills, hooks, MCP). It validates configs statically - including warnings for bypassPermissions and unrestricted Bash in skills, and whether Codex approval_policy / sandbox_mode values are valid. It does not compare permissions against git history, rate Codex approval/sandbox combinations or VS Code / MCP auto-approve settings for risk, or alert you live when a permission file is widened. The two work fine side by side.
  • The client-side Claude Code settings linter web tool at hidekazu-konishi.com checks a settings file you paste into a web page. It covers Claude Code only and has no view of your git history or live changes.

PermDrift's focus is narrower: the direction of change - what your agents can do now that they could not do at the last commit - across several agents, inside the editor, as it happens.

License

MIT - the full license text is included with the extension.

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
  • Your Privacy Choices
  • Consumer Health Privacy
© 2026 Microsoft