diff --git a/lib/utils.js b/lib/utils.js index 5627bec..624e359 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -13,7 +13,18 @@ const fs = require("fs"); */ function findFiles(parent, directory, name) { const names = [].concat(name); - const chunks = path.resolve(parent, directory).split(path.sep); + const resolvedParent = path.resolve(parent); + const resolvedDir = path.resolve(resolvedParent, directory); + + // If the resolved starting directory escapes the parent (e.g. a + // path.relative() result like "../../outside"), do not search outside the + // workspace — return null immediately. + if (resolvedDir !== resolvedParent && + !resolvedDir.startsWith(resolvedParent + path.sep)) { + return null; + } + + const chunks = resolvedDir.split(path.sep); while (chunks.length) { let currentDir = chunks.join(path.sep); @@ -23,7 +34,7 @@ function findFiles(parent, directory, name) { return filePath; } } - if (parent === currentDir) { + if (resolvedParent === currentDir) { break; } chunks.pop(); diff --git a/test/unit.test.js b/test/unit.test.js index 0fb759e..0a7cdec 100644 --- a/test/unit.test.js +++ b/test/unit.test.js @@ -103,4 +103,18 @@ describe("findFiles", () => { const result = findFiles(path.join(tmpRoot, "single"), ".", "phpcs.xml"); assert.equal(result, expected); }); + + test("returns null when directory resolves outside parent (no workspace escape)", () => { + // Simulates a file opened from outside the workspace, where + // path.relative(workspaceRoot, filePath) produces "../../outside". + // findFiles must NOT search outside the parent boundary. + mkFile("outside-config.xml"); // exists above the workspace root + mkDir("workspace"); + const result = findFiles( + path.join(tmpRoot, "workspace"), + path.join("..", ".."), // escapes tmpRoot/workspace + "outside-config.xml" + ); + assert.equal(result, null); + }); });