Skip to content

Commit 487ff0c

Browse files
committed
test: add symlink-dependent containsPath tests that activate after anomalyco#16651
Tests for external symlinks, symlinks escaping project, and ELOOP propagation probe at runtime whether Filesystem.resolve() follows symlinks. They skip gracefully on dev (before anomalyco#16651) and activate once realpathSync lands.
1 parent 540f3d3 commit 487ff0c

1 file changed

Lines changed: 31 additions & 4 deletions

File tree

packages/opencode/test/file/path-traversal.test.ts

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -215,16 +215,25 @@ describe("Instance.containsPath", () => {
215215

216216
test("returns true when project is accessed via external symlink", async () => {
217217
if (process.platform === "win32") return
218+
// This test requires Filesystem.resolve() to resolve symlinks (realpathSync, from #16651).
219+
// Skip if resolve() does not follow symlinks yet.
220+
const probe = path.join(require("os").tmpdir(), `oc-symlink-probe-${Date.now()}`)
221+
const probeTarget = path.join(require("os").tmpdir(), `oc-symlink-probe-target-${Date.now()}`)
222+
await fs.mkdir(probeTarget)
223+
await fs.symlink(probeTarget, probe)
224+
const resolvesSymlinks = Filesystem.resolve(probe) === Filesystem.resolve(probeTarget)
225+
await fs.unlink(probe).catch(() => {})
226+
await fs.rm(probeTarget, { recursive: true }).catch(() => {})
227+
if (!resolvesSymlinks) return
228+
218229
await using tmp = await tmpdir({ git: true })
219230
await Bun.write(path.join(tmp.path, "file.txt"), "content")
220-
// Create a symlink to the project directory from outside
221231
const externalLink = tmp.path + "-ext-symlink"
222232
await fs.symlink(tmp.path, externalLink)
223233
try {
224234
await Instance.provide({
225235
directory: tmp.path,
226236
fn: () => {
227-
// A path via the external symlink should resolve to the canonical project dir
228237
expect(Instance.containsPath(path.join(externalLink, "file.txt"))).toBe(true)
229238
},
230239
})
@@ -235,17 +244,25 @@ describe("Instance.containsPath", () => {
235244

236245
test("returns false for symlink that resolves outside project", async () => {
237246
if (process.platform === "win32") return
247+
// Requires Filesystem.resolve() with realpathSync (#16651)
248+
const probe = path.join(require("os").tmpdir(), `oc-symlink-probe-${Date.now()}`)
249+
const probeTarget = path.join(require("os").tmpdir(), `oc-symlink-probe-target-${Date.now()}`)
250+
await fs.mkdir(probeTarget)
251+
await fs.symlink(probeTarget, probe)
252+
const resolvesSymlinks = Filesystem.resolve(probe) === Filesystem.resolve(probeTarget)
253+
await fs.unlink(probe).catch(() => {})
254+
await fs.rm(probeTarget, { recursive: true }).catch(() => {})
255+
if (!resolvesSymlinks) return
256+
238257
await using tmp = await tmpdir({ git: true })
239258
await using outside = await tmpdir()
240259
await Bun.write(path.join(outside.path, "secret.txt"), "secret")
241-
// Symlink inside project pointing to directory outside project
242260
const link = path.join(tmp.path, "escape-link")
243261
await fs.symlink(outside.path, link)
244262

245263
await Instance.provide({
246264
directory: tmp.path,
247265
fn: () => {
248-
// The symlink is inside the project, but its target is outside
249266
expect(Instance.containsPath(path.join(link, "secret.txt"))).toBe(false)
250267
},
251268
})
@@ -269,6 +286,16 @@ describe("Instance.containsPath", () => {
269286

270287
test("propagates ELOOP from symlink cycle", async () => {
271288
if (process.platform === "win32") return
289+
// Requires Filesystem.resolve() with realpathSync and narrowed catch (#16651)
290+
const probe = path.join(require("os").tmpdir(), `oc-symlink-probe-${Date.now()}`)
291+
const probeTarget = path.join(require("os").tmpdir(), `oc-symlink-probe-target-${Date.now()}`)
292+
await fs.mkdir(probeTarget)
293+
await fs.symlink(probeTarget, probe)
294+
const resolvesSymlinks = Filesystem.resolve(probe) === Filesystem.resolve(probeTarget)
295+
await fs.unlink(probe).catch(() => {})
296+
await fs.rm(probeTarget, { recursive: true }).catch(() => {})
297+
if (!resolvesSymlinks) return
298+
272299
await using tmp = await tmpdir({ git: true })
273300
const a = path.join(tmp.path, "a")
274301
const b = path.join(tmp.path, "b")

0 commit comments

Comments
 (0)