fix(pdf): reject unsafe page numbers before analysis (#105315)

* fix(pdf): reject unsafe page numbers before analysis

* test(pdf): bound unsafe page range regression
This commit is contained in:
qingminlong
2026-07-12 19:38:55 -07:00
committed by GitHub
parent a9761ab5af
commit 0e15a5d994
3 changed files with 25 additions and 8 deletions
+11
View File
@@ -79,6 +79,17 @@ describe("parsePageRange", () => {
expect(() => parsePageRange("1,2.5", 20)).toThrow('Invalid page number: "2.5"');
});
it("throws on unsafe integer page numbers and ranges", () => {
const unsafePage = String(Number.MAX_SAFE_INTEGER + 1);
const maxPages = 20;
expect(() => parsePageRange(unsafePage, maxPages)).toThrow(
`Invalid page number: "${unsafePage}"`,
);
expect(() => parsePageRange(`1-${unsafePage}`, maxPages)).toThrow(
`Invalid page range: "${unsafePage}"`,
);
});
it("throws on invalid range (start > end)", () => {
expect(() => parsePageRange("5-3", 20)).toThrow("Invalid page range");
});
+12 -7
View File
@@ -47,6 +47,14 @@ export function providerSupportsNativePdf(provider: string): boolean {
}
/** Parses a page range string into sorted, unique, 1-based page numbers within `maxPages`. */
function readPageNumber(value: string, errorLabel: string): number {
const parsed = Number(value);
if (!Number.isSafeInteger(parsed) || parsed < 1) {
throw new Error(`${errorLabel}: "${value}"`);
}
return parsed;
}
export function parsePageRange(range: string, maxPages: number): number[] {
const pages = new Set<number>();
const parts = range.split(",").map((p) => p.trim());
@@ -56,9 +64,9 @@ export function parsePageRange(range: string, maxPages: number): number[] {
}
const dashMatch = /^(\d+)\s*-\s*(\d+)$/.exec(part);
if (dashMatch) {
const start = Number(dashMatch[1]);
const end = Number(dashMatch[2]);
if (!Number.isFinite(start) || !Number.isFinite(end) || start < 1 || end < start) {
const start = readPageNumber(dashMatch[1] ?? "", "Invalid page range");
const end = readPageNumber(dashMatch[2] ?? "", "Invalid page range");
if (end < start) {
throw new Error(`Invalid page range: "${part}"`);
}
for (let i = start; i <= Math.min(end, maxPages); i++) {
@@ -68,10 +76,7 @@ export function parsePageRange(range: string, maxPages: number): number[] {
if (!/^\d+$/.test(part)) {
throw new Error(`Invalid page number: "${part}"`);
}
const num = Number(part);
if (!Number.isFinite(num) || num < 1) {
throw new Error(`Invalid page number: "${part}"`);
}
const num = readPageNumber(part, "Invalid page number");
if (num <= maxPages) {
pages.add(num);
}
+2 -1
View File
@@ -625,8 +625,9 @@ describe("createPdfTool", () => {
it.each([
["1.5", "1.5"],
["1,2.5", "2.5"],
[`1,${String(Number.MAX_SAFE_INTEGER + 1)}`, String(Number.MAX_SAFE_INTEGER + 1)],
])(
"rejects fractional page selection %s before loading or fallback extraction",
"rejects invalid page selection %s before loading or fallback extraction",
async (pages, invalidPage) => {
await withTempPdfAgentDir(async (agentDir) => {
const { loadSpy } = await stubPdfToolInfra(agentDir, {