Skip to content

Commit d67c9bb

Browse files
authored
fix(include-parser): infinite recursion or out-of-memory with symlinks/large projects (#1598)
* fix(include-parser): use git ls-files instead of globby to memoize repository files * change back exception message to adhere with tests
1 parent fc9c06a commit d67c9bb

File tree

3 files changed

+14
-7
lines changed

3 files changed

+14
-7
lines changed

src/job.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1546,7 +1546,7 @@ export class Job {
15461546
if (include["local"]) {
15471547
const expandedInclude = Utils.expandText(include["local"], this._variables);
15481548
validateIncludeLocal(expandedInclude);
1549-
const files = resolveIncludeLocal(expandedInclude, cwd);
1549+
const files = await resolveIncludeLocal(expandedInclude, cwd);
15501550
if (files.length == 0) {
15511551
throw new AssertionError({message: `Local include file \`${include["local"]}\` specified in \`.${this.name}\` cannot be found!`});
15521552
}

src/parser-includes.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import assert, {AssertionError} from "assert";
77
import chalk from "chalk";
88
import {Parser} from "./parser.js";
99
import axios from "axios";
10-
import globby from "globby";
1110
import path from "path";
1211
import semver from "semver";
1312
import {RE2JS} from "re2js";
@@ -93,7 +92,7 @@ export class ParserIncludes {
9392
}
9493
if (value["local"]) {
9594
validateIncludeLocal(value["local"]);
96-
const files = resolveIncludeLocal(value["local"], cwd);
95+
const files = await resolveIncludeLocal(value["local"], cwd);
9796
if (files.length == 0) {
9897
throw new AssertionError({message: `Local include file cannot be found ${value["local"]}`});
9998
}
@@ -327,11 +326,11 @@ export class ParserIncludes {
327326

328327
static readonly memoLocalRepoFiles = (() => {
329328
const cache = new Map<string, string[]>();
330-
return (path: string) => {
329+
return async (path: string) => {
331330
let result = cache.get(path);
332331
if (typeof result !== "undefined") return result;
333332

334-
result = globby.sync(path, {dot: true, gitignore: true});
333+
result = (await Utils.getTrackedFiles(path)).map(p => `${path}/${p}`);
335334
cache.set(path, result);
336335
return result;
337336
};
@@ -361,8 +360,8 @@ export function resolveSemanticVersionRange (range: string, gitTags: string[]) {
361360
return found;
362361
}
363362

364-
export function resolveIncludeLocal (pattern: string, cwd: string) {
365-
const repoFiles = ParserIncludes.memoLocalRepoFiles(cwd);
363+
export async function resolveIncludeLocal (pattern: string, cwd: string) {
364+
const repoFiles = await ParserIncludes.memoLocalRepoFiles(cwd);
366365

367366
if (!pattern.startsWith("/")) pattern = `/${pattern}`; // Ensure pattern starts with `/`
368367
pattern = `${cwd}${pattern}`;

src/utils.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,4 +434,12 @@ export class Utils {
434434
// https://dev.to/babak/exhaustive-type-checking-with-typescript-4l3f
435435
throw new Error(`Unhandled case ${param}`);
436436
}
437+
438+
static async getTrackedFiles (cwd: string): Promise<string[]> {
439+
const lsFilesRes = await Utils.bash("git ls-files --deduplicate", cwd);
440+
if (lsFilesRes.exitCode != 0) {
441+
throw new Error(`Failed to list tracked files in ${cwd}: ${lsFilesRes.stderr}`);
442+
}
443+
return lsFilesRes.stdout.split("\n");
444+
}
437445
}

0 commit comments

Comments
 (0)