mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-09-01 15:32:11 +08:00
fix(vscode, cli): handle Windows cross-drive paths in ignore checks (#7479)
On Windows, path.relative() returns an absolute path when source and target are on different drives. The ignore npm package throws a RangeError when fed such paths. This caused 'Failed to send prompt' when VS Code had open tabs from another drive (e.g. extension settings in AppData while workspace is on D:). Guard all path.relative() → ignore.ignores() call sites against absolute results by checking path.isAbsolute() and a Windows drive-letter regex.
This commit is contained in:
@@ -2471,7 +2471,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
|
||||
const uri = tab.input.uri
|
||||
if (uri.scheme === "file") {
|
||||
const rel = path.relative(dir, uri.fsPath)
|
||||
if (!rel.startsWith("..") && controller.validateAccess(uri.fsPath)) {
|
||||
if (!rel.startsWith("..") && !path.isAbsolute(rel) && controller.validateAccess(uri.fsPath)) {
|
||||
result.add(rel.replaceAll("\\", "/"))
|
||||
}
|
||||
}
|
||||
@@ -2505,7 +2505,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
|
||||
return undefined
|
||||
}
|
||||
const relative = path.relative(workspaceDir, fsPath)
|
||||
if (relative.startsWith("..")) {
|
||||
if (relative.startsWith("..") || path.isAbsolute(relative)) {
|
||||
return undefined
|
||||
}
|
||||
return relative
|
||||
|
||||
@@ -10,6 +10,11 @@ const GITIGNORE = ".gitignore"
|
||||
*/
|
||||
const SENSITIVE_PATTERNS = [".env", ".env.*"]
|
||||
|
||||
// Matches Windows drive-letter absolute paths (e.g. "C:/" or "c:\").
|
||||
// path.isAbsolute() on POSIX does not recognise these, so we check explicitly
|
||||
// to avoid passing them to the `ignore` package which throws a RangeError.
|
||||
const WINDOWS_DRIVE = /^[a-zA-Z]:[/\\]/
|
||||
|
||||
function toPosix(filePath: string): string {
|
||||
return filePath.replace(/\\/g, "/")
|
||||
}
|
||||
@@ -87,7 +92,7 @@ export class FileIgnoreController {
|
||||
}
|
||||
|
||||
const relative = path.relative(this.workspacePath, resolved)
|
||||
if (!relative || relative.startsWith("..")) {
|
||||
if (!relative || relative.startsWith("..") || path.isAbsolute(relative) || WINDOWS_DRIVE.test(relative)) {
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,16 @@ import { afterEach, describe, expect, it } from "bun:test"
|
||||
import os from "node:os"
|
||||
import path from "node:path"
|
||||
import fs from "node:fs/promises"
|
||||
import ignore from "ignore"
|
||||
import { FileIgnoreController } from "../../src/services/autocomplete/shims/FileIgnoreController"
|
||||
|
||||
// Activate Windows drive-letter detection in the `ignore` package.
|
||||
// On actual Windows this runs automatically (process.platform === 'win32');
|
||||
// here we enable it explicitly so the test reproduces the Windows-only
|
||||
// RangeError on any platform.
|
||||
const setup = (ignore as any)[Symbol.for("setupWindows")]
|
||||
if (typeof setup === "function") setup()
|
||||
|
||||
const tempDirs: string[] = []
|
||||
|
||||
afterEach(async () => {
|
||||
@@ -102,6 +110,55 @@ describe("FileIgnoreController", () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe("Windows cross-drive paths", () => {
|
||||
it("does not throw for a Windows-style absolute path from another drive", async () => {
|
||||
const workspace = await createTempWorkspace()
|
||||
await fs.writeFile(path.join(workspace, ".gitignore"), "node_modules/\n")
|
||||
|
||||
const controller = new FileIgnoreController(workspace)
|
||||
await controller.initialize()
|
||||
|
||||
// Simulates a VS Code tab open on a file from a different Windows drive.
|
||||
// On Windows, path.relative("D:\\project", "C:\\Users\\file") returns
|
||||
// "C:\\Users\\file" (absolute), which the `ignore` package rejects via
|
||||
// RangeError: path should be a `path.relative()`d string.
|
||||
//
|
||||
// On macOS, path.resolve joins "c:/..." relative to the workspace,
|
||||
// producing "c:/Users/..." as the relative portion — still detected as
|
||||
// a Windows drive letter by ignore's setupWindows() regex.
|
||||
const cross =
|
||||
"c:/Users/User/AppData/Roaming/Code/User/globalStorage/kilocode.kilo-code/settings/mcp_settings.json"
|
||||
|
||||
expect(() => controller.validateAccess(cross)).not.toThrow()
|
||||
expect(controller.validateAccess(cross)).toBe(false)
|
||||
})
|
||||
|
||||
it("does not throw for file:// URIs with Windows drive letters", async () => {
|
||||
const workspace = await createTempWorkspace()
|
||||
await fs.writeFile(path.join(workspace, ".gitignore"), "node_modules/\n")
|
||||
|
||||
const controller = new FileIgnoreController(workspace)
|
||||
await controller.initialize()
|
||||
|
||||
const uri =
|
||||
"file:///c:/Users/User/AppData/Roaming/Code/User/globalStorage/kilocode.kilo-code/settings/mcp_settings.json"
|
||||
|
||||
expect(() => controller.validateAccess(uri)).not.toThrow()
|
||||
expect(controller.validateAccess(uri)).toBe(false)
|
||||
})
|
||||
|
||||
it("still allows workspace files after cross-drive check", async () => {
|
||||
const workspace = await createTempWorkspace()
|
||||
await fs.writeFile(path.join(workspace, ".gitignore"), "node_modules/\n")
|
||||
|
||||
const controller = new FileIgnoreController(workspace)
|
||||
await controller.initialize()
|
||||
|
||||
expect(controller.validateAccess(path.join(workspace, "src", "main.ts"))).toBe(true)
|
||||
expect(controller.validateAccess(path.join(workspace, "node_modules", "foo.js"))).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe("when constructed with empty workspace path", () => {
|
||||
it("denies all access", async () => {
|
||||
const controller = new FileIgnoreController("")
|
||||
|
||||
@@ -596,12 +596,15 @@ export namespace File {
|
||||
const fullPath = path.join(resolved, entry.name)
|
||||
const relativePath = path.relative(Instance.directory, fullPath)
|
||||
const type = entry.isDirectory() ? "directory" : "file"
|
||||
// On Windows, path.relative() across drives returns an absolute path;
|
||||
// skip the gitignore check in that case to avoid a RangeError from `ignore`.
|
||||
const canIgnore = !path.isAbsolute(relativePath)
|
||||
nodes.push({
|
||||
name: entry.name,
|
||||
path: relativePath,
|
||||
absolute: fullPath,
|
||||
type,
|
||||
ignored: ignored(type === "directory" ? relativePath + "/" : relativePath),
|
||||
ignored: canIgnore && ignored(type === "directory" ? relativePath + "/" : relativePath),
|
||||
})
|
||||
}
|
||||
return nodes.sort((a, b) => {
|
||||
|
||||
Reference in New Issue
Block a user