Skip to content

fix(cli): enforce read permissions for file mentions#12158

Open
marius-kilocode wants to merge 5 commits into
mainfrom
fix-read-permission-deny-bypass
Open

fix(cli): enforce read permissions for file mentions#12158
marius-kilocode wants to merge 5 commits into
mainfrom
fix-read-permission-deny-bypass

Conversation

@marius-kilocode

Copy link
Copy Markdown
Collaborator

File mentions are converted into local file: prompt attachments before model execution. Text attachments were expanded through a Read tool context with a no-op permission callback, while binary and media attachments read the filesystem directly. Because .kilocodeignore is enforced by generating permission.read deny rules, both paths could bypass the evaluator and persist denied file bytes into model-bound context.

Route every local prompt attachment through the active session permission resolver before content is read. allow retains the existing attachment behavior, ask waits for approval, and deny or .kilocodeignore produces a failure notice without persisting file content. Directory expansion now authorizes all child files first, and direct attachments enforce external-directory policy.

Canonicalize symlink targets and check both requested and resolved paths so an allowed alias cannot expose a denied target. Configured references retain their external-directory exemption only when the canonical target remains inside the configured reference root.

Fixes #12133

Comment thread packages/opencode/src/kilocode/reference/contains.ts Outdated
Comment thread packages/opencode/src/session/prompt.ts Outdated
Comment thread packages/opencode/src/session/prompt.ts
Comment thread packages/opencode/src/session/prompt.ts
Comment thread packages/opencode/src/tool/read.ts Outdated
Comment thread packages/opencode/src/tool/read.ts Outdated
Comment thread packages/opencode/src/session/prompt.ts Outdated
@kilo-code-bot

kilo-code-bot Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: 5 Issues Found | Recommendation: Address before merge

Overview

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

WARNING

File Line Issue
packages/opencode/src/tool/read.ts 61 Missing-file suggestions enumerate denied directories before read permission
packages/opencode/src/kilocode/tool/read-object.ts 58 Special files can block while opening before authorization
packages/opencode/src/kilocode/tool/read-object.ts 107 Directory listing is not bound to the validated directory object
packages/opencode/src/session/prompt.ts 1145 Concurrent permission waits retain unbounded open file handles
packages/opencode/src/session/prompt.ts 179 Synchronous attachment permission waits remain outside cancellation tracking
Files Reviewed (15 files)
  • .changeset/secure-file-mentions.md - 0 issues
  • packages/opencode/src/kilocode/reference/contains.ts - 0 issues
  • packages/opencode/src/kilocode/session/prompt.ts - 0 issues
  • packages/opencode/src/kilocode/text-stream.ts - 0 issues
  • packages/opencode/src/kilocode/tool/notebook.ts - 0 issues
  • packages/opencode/src/kilocode/tool/read-docx.ts - 0 issues
  • packages/opencode/src/kilocode/tool/read-extract.ts - 0 issues
  • packages/opencode/src/kilocode/tool/read-object.ts - 2 issues
  • packages/opencode/src/kilocode/tool/xlsx.ts - 0 issues
  • packages/opencode/src/server/routes/instance/httpapi/handlers/session.ts - 0 issues
  • packages/opencode/src/session/prompt.ts - 2 issues
  • packages/opencode/src/tool/read.ts - 1 issue
  • packages/opencode/test/kilocode/read-directory.test.ts - 0 issues
  • packages/opencode/test/kilocode/session-prompt-permission-refresh.test.ts - 0 issues
  • packages/opencode/test/kilocode/tool-encoding.test.ts - 0 issues

Fix these issues in Kilo Cloud

Previous Review Summary (commit 99b33db)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 99b33db)

Status: 7 Issues Found | Recommendation: Address before merge

Overview

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

WARNING

File Line Issue
packages/opencode/src/kilocode/reference/contains.ts 11 Canonicalization failure grants the configured-reference exemption
packages/opencode/src/session/prompt.ts 855 Stale reference containment bypasses external-directory enforcement
packages/opencode/src/session/prompt.ts 978 Session abort cannot cancel an attachment permission wait
packages/opencode/src/session/prompt.ts 1006 Implicit instruction files are read without permission evaluation
packages/opencode/src/tool/read.ts 225 Directory child can be swapped after authorization
packages/opencode/src/tool/read.ts 263 Canonical pathname can be replaced before the actual read
packages/opencode/src/session/prompt.ts 1208 Direct attachment is reopened after authorization and size checks
Files Reviewed (5 files)
  • .changeset/secure-file-mentions.md - 0 issues
  • packages/opencode/src/kilocode/reference/contains.ts - 1 issue
  • packages/opencode/src/session/prompt.ts - 4 issues
  • packages/opencode/src/tool/read.ts - 2 issues
  • packages/opencode/test/kilocode/session-prompt-permission-refresh.test.ts - 0 issues

Fix these issues in Kilo Cloud


Reviewed by gpt-5.6-sol · Input: 174.6K · Output: 27.6K · Cached: 1.6M

Review guidance: REVIEW.md from base branch main

@marius-kilocode marius-kilocode force-pushed the fix-read-permission-deny-bypass branch from 99b33db to 87b7d81 Compare July 13, 2026 12:43
),
Effect.catch(() => Effect.succeed([] as string[])),
)
const parent = yield* KiloReadObject.directory(dir).pipe(Effect.option)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[WARNING]: Missing-file suggestions bypass read permission

KiloReadObject.directory has already enumerated the parent before this branch, and miss never calls ctx.ask. This regresses the old ordering where the requested path was evaluated by permission.read before suggestions were generated: a denied or .kilocodeignore-covered directory can now disclose matching filenames through Did you mean. Canonicalize the parent without listing it, evaluate read permission, and only then enumerate suggestions.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.


export function use<A, E, R>(requested: string, fn: (file: File) => Effect.Effect<A, E, R>) {
const acquire = Effect.tryPromise({
try: () => open(requested, "r"),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[WARNING]: Opening happens before any permission check

Every caller acquires this handle before it can evaluate external_directory or read. For regular files that is mostly metadata access, but opening a FIFO for reading can block indefinitely waiting for a writer, so a denied file mention/tool call can hang a worker before authorization and cannot reach the abort-aware read loop. Reject non-regular targets using non-blocking/no-follow metadata first, or acquire them in a mode that cannot block before permission evaluation.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

if (opened.dev !== seen.dev || opened.ino !== seen.ino) {
throw new ChangedError(`Directory changed while opening: ${requested}`)
}
const entries = await readdir(resolved, { withFileTypes: true })

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[WARNING]: Bind the directory listing to the validated object

This still reads by pathname rather than through an opened directory handle. A cooperating process can rename the validated directory away, put an external-directory symlink at resolved for this readdir, and restore the original directory before the following stat/realpath; all post-checks then pass while entries contains names from the unauthorized directory. Open the directory once and enumerate through that stable handle, as the file branch does.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

filename: part.filename!,
source: part.source,
// kilocode_change start - authorize and consume direct attachments through one open object
const access = yield* KiloReadObject.use(filepath, (bound) =>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[WARNING]: Do not hold unbounded file handles while asking

use opens the file before context.ask and keeps it open for the entire human-response wait. Prompt parts are resolved with unbounded concurrency, so one prompt containing many files under an ask rule can retain one descriptor per part and exhaust the process-wide descriptor limit before the user can reply. Bound attachment concurrency and/or close the object while waiting, then reopen and verify the same object after approval.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

yield* elog.info("cancel", { sessionID })
yield* KiloSessionPromptQueue.cancel(sessionID) // kilocode_change - drop queued follow-up loops on abort
KiloSessionPrompt.abortPlanFollowup(sessionID) // kilocode_change - abort pending plan-followup handover work
yield* KiloSessionPrompt.abortAsyncPrompts(sessionID) // kilocode_change - interrupt attachment permission waits

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[WARNING]: Register synchronous prompts for intake cancellation too

Only promptAsync calls startAsyncPrompt; the synchronous /prompt handler invokes promptSvc.prompt directly. If that path waits in the new attachment permission phase, SessionRunState has not started yet and this abort call finds neither an async intake nor a runner, leaving the HTTP request, pending permission, and open file handle alive after Stop. Track the pre-loop intake for both endpoints rather than only the async route.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

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.

Read file permissions don't seem to work, potentially leaking secrets

1 participant