Skip to content

feat(cli): protect CLI config files from silent edits - #7573

Merged
alex-alecu merged 11 commits into
mainfrom
feat/config-permission-protection
Mar 27, 2026
Merged

feat(cli): protect CLI config files from silent edits#7573
alex-alecu merged 11 commits into
mainfrom
feat/config-permission-protection

Conversation

@alex-alecu

@alex-alecu alex-alecu commented Mar 25, 2026

Copy link
Copy Markdown
Contributor

Followup to #7444

Why

When a user selects "Always allow" for file edits, the AI agent can silently modify CLI config files (like permission rules, agent settings, or project config) without the user noticing. This is a security risk because config changes can alter how the tool behaves going forward.

What changed

The permission system now detects when an edit targets a config file and forces the user to approve it every time, even if they previously chose "Always allow" for edits. Config files include project-level directories like .kilo/, .kilocode/, .opencode/, root config files like kilo.json and AGENTS.md, and global config directories like ~/.config/kilo/. When a config edit is detected, the "Always allow" button is hidden in the TUI and the backend prevents any client from saving persistent allow-rules for these paths.

The kilo run --auto mode still works — edits go through but no permanent rules get saved.

Plan files exempt from config protection

Non-config subdirectories under .kilo/ (specifically .kilo/plans/) are excluded from config protection via EXCLUDED_SUBDIRS. The plan agent writes plan files to .kilo/plans/*.md using the edit permission, and these were previously caught by isRelative() because it matched any path starting with .kilo/. Plan files are not config files, so they now pass through without a permission prompt.

Demo

When asking CLI to edit global config:
Screenshot 2026-03-27 at 09 18 40

When asking CLI to edit local repo config:
Screenshot 2026-03-27 at 09 29 45

Global config directory protected for external_directory access

ConfigProtection.isRequest() previously only checked edit permissions. The external_directory permission (triggered when accessing paths outside the project, like ~/.config/kilo/) was not covered, meaning "Always allow" could grant permanent directory access to global config directories. isRequest() now also handles external_directory permissions by checking absolute patterns against known config dirs (Global.Path.config, KilocodePaths.globalDirs()).

How to test

  1. Start a session and ask the agent to edit a file like .kilo/command/test.md
  2. Verify the permission prompt shows only "Allow once" and "Reject" (no "Always allow")
  3. Ask the agent to edit a normal file like src/index.ts
  4. Verify the permission prompt shows all three options including "Allow always"
  5. If you previously set "Always allow" for edits, verify the agent still asks for config file edits
  6. Start a plan session — the plan agent should write to .kilo/plans/ without a permission prompt
  7. Trigger an external directory access to ~/.config/kilo/ — verify only "Allow once" and "Reject" are shown (no "Allow always")
  8. Trigger an external directory access to a non-config directory — verify all three options are shown

Add ConfigProtection namespace with utilities to detect when a
permission request targets CLI config files. This covers:
- Project-relative paths (.kilo/, .kilocode/, .opencode/ dirs, kilo.json, AGENTS.md)
- Absolute paths (~/.config/kilo/, ~/.kilo/, ~/.kilocode/)
- Multi-file requests from apply_patch (metadata.files[], comma-joined filepath)
Override 'allow' to 'ask' in the permission engine when editing config
files. Sets disableAlways metadata flag for UI clients.

Safety nets prevent persistent rule creation for config paths:
- reply() downgrades 'always' to 'once' (no rules saved)
- saveAlwaysRules() skips entirely for config requests
- drainCovered() never auto-resolves config file permissions
When the disableAlways metadata flag is set on a permission request,
only show 'Allow once' and 'Reject' options in the TUI prompt.
Comment thread packages/opencode/src/kilocode/permission/config-paths.ts Outdated
Comment thread packages/opencode/src/kilocode/permission/config-paths.ts Outdated
@kilo-code-bot

kilo-code-bot Bot commented Mar 25, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
packages/kilo-vscode/webview-ui/src/components/settings/McpEditView.tsx 112 Editing a legacy MCP config with { command: string, args: string[] } discards its saved args because the editor only preserves array-form command values.

Fix these issues in Kilo Cloud

Other Observations (not in diff)

Issues found in unchanged code that cannot receive inline comments:

File Line Issue
packages/kilo-vscode/webview-ui/src/components/settings/McpEditView.tsx 112 This warning came from incremental changes merged from main, so it is outside the current gh pr diff and could only be reported in the summary.

Previous warnings on packages/opencode/src/kilocode/permission/config-paths.ts about root-config normalization and the plans/ escape were fixed in e4600cd8f.

Files Reviewed (5 files)
  • packages/opencode/src/cli/cmd/tui/routes/session/permission.tsx
  • packages/opencode/src/kilocode/permission/config-paths.ts
  • packages/opencode/src/kilocode/permission/drain.ts
  • packages/opencode/src/permission/next.ts
  • packages/kilo-vscode/webview-ui/src/components/settings/McpEditView.tsx - 1 issue

Reviewed by gpt-5.4-20260305 · 1,632,850 tokens

…onstant

- Check absolute patterns in isRequest via isAbsolute() to match the
  existing metadata.filepath dual-dispatch logic, preventing a bypass
  where tools pass absolute project-level config paths in patterns
- Export ConfigProtection.DISABLE_ALWAYS_KEY constant to replace the
  'disableAlways' magic string across config-paths.ts, next.ts, and
  permission.tsx so renames/typos are caught at compile time
Comment thread packages/opencode/src/kilocode/permission/config-paths.ts
… access

Plan files (.kilo/plans/*.md) were incorrectly flagged as config files
by isRelative(), causing permission prompts for the plan agent. Add
EXCLUDED_SUBDIRS to skip non-config subdirectories under config dirs.

Extend isRequest() to also check external_directory permissions against
global config paths (~/.config/kilo/), preventing 'Always allow' from
granting permanent directory access to config directories.
Comment thread packages/opencode/src/kilocode/permission/config-paths.ts
@alex-alecu alex-alecu self-assigned this Mar 27, 2026
@alex-alecu alex-alecu changed the title feat(cli): protect config files from silent edits feat(cli): protect CLI config files from silent edits Mar 27, 2026
@alex-alecu
alex-alecu merged commit f71909c into main Mar 27, 2026
19 checks passed
@alex-alecu
alex-alecu deleted the feat/config-permission-protection branch March 27, 2026 09:45
jliounis pushed a commit to jliounis/kilocode that referenced this pull request May 18, 2026
…-protection

feat(cli): protect CLI config files from silent edits
t7tran pushed a commit to t7tran/kilocode that referenced this pull request Aug 14, 2026
…-protection

feat(cli): protect CLI config files from silent edits
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants