fix(pdf): guard pdfSplit against non-positive interval infinite loop

The interval split mode looped for (i = 0; i < totalPages; i += interval),
which spins forever when interval <= 0. Both the single-file dialog and the
batch dialog can reach it (the batch dialog's validateOperationData only
checks truthiness, so -1 passes). Guard at the source in the main process:
reject non-positive or non-integer intervals before the loop, protecting
both paths and any future caller.

Amit Haridas
This commit is contained in:
2026-08-23 19:31:33 +05:30
parent 2e16868f59
commit 363de75375
2 changed files with 54 additions and 0 deletions
+3
View File
@@ -112,6 +112,9 @@ async function pdfSplit(data) {
}
} else if (data.splitMode === 'interval') {
const interval = data.interval;
if (!Number.isInteger(interval) || interval <= 0) {
return { success: false, message: 'Split interval must be a positive integer.' };
}
for (let i = 0; i < totalPages; i += interval) {
const pages = [];
for (let j = i; j < i + interval && j < totalPages; j++) {
+51
View File
@@ -492,3 +492,54 @@ describe('PDFOperations - Task 27 honest encryption failure', () => {
expect(rotated.getPageCount()).toBe(1);
});
});
describe('PDFOperations - pdfSplit interval guard', () => {
let tmpDir, inputPath, outputFolder;
beforeEach(async () => {
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pdfops_split_'));
inputPath = path.join(tmpDir, 'in.pdf');
outputFolder = path.join(tmpDir, 'out');
fs.mkdirSync(outputFolder);
const doc = await PDFDocument.create();
doc.addPage([600, 800]);
doc.addPage([600, 800]);
fs.writeFileSync(inputPath, await doc.save());
});
afterEach(() => {
fs.rmSync(tmpDir, { recursive: true, force: true });
});
// A non-positive (or non-integer) interval previously made the split loop
// spin forever (i += 0 never advances the counter), so Jest's per-test
// timeout is the hang detector for these cases.
it.each([0, -1, 1.5])(
'rejects interval %s without writing any output files',
async (interval) => {
const result = await PDFOperations.pdfSplit({
inputPath,
outputFolder,
splitMode: 'interval',
interval,
});
expect(result.success).toBe(false);
expect(result.message).toBe('Split interval must be a positive integer.');
expect(fs.readdirSync(outputFolder)).toEqual([]);
}
);
it('still splits every N pages for a valid positive interval', async () => {
const result = await PDFOperations.pdfSplit({
inputPath,
outputFolder,
splitMode: 'interval',
interval: 1,
});
expect(result.success).toBe(true);
expect(fs.readdirSync(outputFolder).sort()).toEqual(['in_part_1.pdf', 'in_part_2.pdf']);
});
});