Skip to content

Commit 7b6b13c

Browse files
mrubensNaccOll
authored andcommitted
Follow symlinks in rooignore checks (RooCodeInc#7405)
1 parent 5bb4578 commit 7b6b13c

File tree

2 files changed

+44
-5
lines changed

2 files changed

+44
-5
lines changed

src/core/ignore/RooIgnoreController.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import path from "path"
22
import { fileExistsAtPath } from "../../utils/fs"
33
import fs from "fs/promises"
4+
import fsSync from "fs"
45
import ignore, { Ignore } from "ignore"
56
import * as vscode from "vscode"
67

@@ -81,6 +82,7 @@ export class RooIgnoreController {
8182

8283
/**
8384
* Check if a file should be accessible to the LLM
85+
* Automatically resolves symlinks
8486
* @param filePath - Path to check (relative to cwd)
8587
* @returns true if file is accessible, false if ignored
8688
*/
@@ -90,15 +92,25 @@ export class RooIgnoreController {
9092
return true
9193
}
9294
try {
93-
// Normalize path to be relative to cwd and use forward slashes
9495
const absolutePath = path.resolve(this.cwd, filePath)
95-
const relativePath = path.relative(this.cwd, absolutePath).toPosix()
9696

97-
// Ignore expects paths to be path.relative()'d
97+
// Follow symlinks to get the real path
98+
let realPath: string
99+
try {
100+
realPath = fsSync.realpathSync(absolutePath)
101+
} catch {
102+
// If realpath fails (file doesn't exist, broken symlink, etc.),
103+
// use the original path
104+
realPath = absolutePath
105+
}
106+
107+
// Convert real path to relative for .rooignore checking
108+
const relativePath = path.relative(this.cwd, realPath).toPosix()
109+
110+
// Check if the real path is ignored
98111
return !this.ignoreInstance.ignores(relativePath)
99112
} catch (error) {
100-
// console.error(`Error validating access for ${filePath}:`, error)
101-
// Ignore is designed to work with relative file paths, so will throw error for paths outside cwd. We are allowing access to all files outside cwd.
113+
// Allow access to files outside cwd or on errors (backward compatibility)
102114
return true
103115
}
104116
}

src/core/ignore/__tests__/RooIgnoreController.spec.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ import { RooIgnoreController, LOCK_TEXT_SYMBOL } from "../RooIgnoreController"
66
import * as vscode from "vscode"
77
import * as path from "path"
88
import * as fs from "fs/promises"
9+
import * as fsSync from "fs"
910
import { fileExistsAtPath } from "../../../utils/fs"
1011

1112
// Mock dependencies
1213
vi.mock("fs/promises")
14+
vi.mock("fs")
1315
vi.mock("../../../utils/fs")
1416

1517
// Mock vscode
@@ -66,6 +68,10 @@ describe("RooIgnoreController", () => {
6668
mockFileExists = fileExistsAtPath as Mock<typeof fileExistsAtPath>
6769
mockReadFile = fs.readFile as Mock<typeof fs.readFile>
6870

71+
// Setup fsSync mocks with default behavior (return path as-is, like regular files)
72+
const mockRealpathSync = vi.mocked(fsSync.realpathSync)
73+
mockRealpathSync.mockImplementation((filePath) => filePath.toString())
74+
6975
// Create controller
7076
controller = new RooIgnoreController(TEST_CWD)
7177
})
@@ -217,6 +223,27 @@ describe("RooIgnoreController", () => {
217223
expect(emptyController.validateAccess("secrets/api-keys.json")).toBe(true)
218224
expect(emptyController.validateAccess(".git/HEAD")).toBe(true)
219225
})
226+
227+
/**
228+
* Tests symlink resolution
229+
*/
230+
it("should block symlinks pointing to ignored files", () => {
231+
// Mock fsSync.realpathSync to simulate symlink resolution
232+
const mockRealpathSync = vi.mocked(fsSync.realpathSync)
233+
mockRealpathSync.mockImplementation((filePath) => {
234+
// Simulate "config.json" being a symlink to "node_modules/package.json"
235+
if (filePath.toString().endsWith("config.json")) {
236+
return path.join(TEST_CWD, "node_modules/package.json")
237+
}
238+
return filePath.toString()
239+
})
240+
241+
// Direct access to ignored file should be blocked
242+
expect(controller.validateAccess("node_modules/package.json")).toBe(false)
243+
244+
// Symlink to ignored file should also be blocked
245+
expect(controller.validateAccess("config.json")).toBe(false)
246+
})
220247
})
221248

222249
describe("validateCommand", () => {

0 commit comments

Comments
 (0)