From 33b6b242b13a0cd2c10b102af3015d794bfca77e Mon Sep 17 00:00:00 2001 From: ghost <49853598+JSONbored@users.noreply.github.com> Date: Fri, 3 Jul 2026 21:23:04 -0700 Subject: [PATCH] fix(engine): cap miner goal list inspection --- .../src/miner-goal-spec-parse.ts | 6 +++- .../test/miner-goal-spec-parse.test.ts | 36 +++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/packages/gittensory-engine/src/miner-goal-spec-parse.ts b/packages/gittensory-engine/src/miner-goal-spec-parse.ts index 0149cf432c..20ac143ebe 100644 --- a/packages/gittensory-engine/src/miner-goal-spec-parse.ts +++ b/packages/gittensory-engine/src/miner-goal-spec-parse.ts @@ -32,7 +32,11 @@ function parseStringList(value: unknown, field: string, warnings: string[]): str } const seen = new Set(); const out: string[] = []; - for (const entry of value) { + for (const [index, entry] of value.entries()) { + if (index >= MAX_LIST_ENTRIES) { + warnings.push(`MinerGoalSpec field "${field}" is capped at ${MAX_LIST_ENTRIES} entries; dropping the rest.`); + break; + } if (typeof entry !== "string") { warnings.push(`MinerGoalSpec field "${field}" entries must be strings; skipping non-string.`); continue; diff --git a/packages/gittensory-engine/test/miner-goal-spec-parse.test.ts b/packages/gittensory-engine/test/miner-goal-spec-parse.test.ts index fa50c0c48d..a65e9524d3 100644 --- a/packages/gittensory-engine/test/miner-goal-spec-parse.test.ts +++ b/packages/gittensory-engine/test/miner-goal-spec-parse.test.ts @@ -63,3 +63,39 @@ test("parseMinerGoalSpec warns and falls back on malformed fields", () => { assert.equal(result.spec.issueDiscoveryPolicy, "neutral"); assert.ok(result.warnings.length >= 4); }); + +test("parseMinerGoalSpec caps list inspection for invalid entries", () => { + const result = parseMinerGoalSpec({ + wantedPaths: Array.from({ length: 1_000 }, () => null), + }); + + assert.deepEqual(result.spec.wantedPaths, []); + assert.equal(result.warnings.length, 201); + assert.match(result.warnings.at(-1) ?? "", /capped at 200 entries/); +}); + +test("parseMinerGoalSpec caps list inspection for duplicate, empty, and overlong entries", () => { + const duplicates = parseMinerGoalSpec({ + wantedPaths: Array.from({ length: 1_000 }, () => "src/**"), + }); + assert.deepEqual(duplicates.spec.wantedPaths, ["src/**"]); + assert.deepEqual(duplicates.warnings, [ + 'MinerGoalSpec field "wantedPaths" is capped at 200 entries; dropping the rest.', + ]); + + const empty = parseMinerGoalSpec({ + wantedPaths: Array.from({ length: 1_000 }, () => " "), + }); + assert.deepEqual(empty.spec.wantedPaths, []); + assert.deepEqual(empty.warnings, [ + 'MinerGoalSpec field "wantedPaths" is capped at 200 entries; dropping the rest.', + ]); + + const overlong = parseMinerGoalSpec({ + wantedPaths: Array.from({ length: 1_000 }, () => "x".repeat(301)), + }); + assert.deepEqual(overlong.spec.wantedPaths, []); + assert.deepEqual(overlong.warnings, [ + 'MinerGoalSpec field "wantedPaths" is capped at 200 entries; dropping the rest.', + ]); +});