Corrupt PNG throws uncatchable error

Abierto
#1,747 2 comentarios 3 reacciones 0 asignados Ver en GitHub

Los mantenedores suelen responder en 1 día

Nadie ha tomado este issue todavía.

  • #1788 de @afonsojanu — cerrado sin fusionar

Evaluación

Dificultad
4/5
Tiempo estimado
3-5 días
Aptitud para principiantes
55/100
Tipo de issue
Error
Claridad
Bastante claro
Estado de actividad
Tranquilo
Stack tecnológico
javascript, node.js
Área
backend

Línea de trabajo

Comienza en el punto de entrada doc.image y sigue la ruta de transparencia de PNG hasta png-js decodePixels, donde se vuelve a lanzar el error asíncrono de zlib. Reproduce el caso de PNG corrupto descrito en el issue y verifica que el fallo pueda ser capturado por el llamador o emitido a través de la ruta de errores del documento sin hacer que el proceso se bloquee.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

Bug Report

Description

When embedding a PNG which IDAT data is corrupted or truncated, pdfkit crashes the process with an uncaught exception. The error cannot be caught by the caller: wrapping inside try/catch does not work, and no error event is emitted on the document.

The root cause is in the png-js dependency. In decodePixels, it calls zlib.inflate and re-throws the error from inside the asynchronous callback:

decodePixels(fn) {
  return zlib.inflate(this.imgData, (err, data) => {
    if (err) {
      throw err;
    }
    ...

This happens specifically when pdfkit needs to access the raw pixels, for example for PNGs with transparency.

How to reproduce

Create a script as follows:

const PDFDocument = require('pdfkit');
const fs = require('fs');
const zlib = require('zlib');

// Construct a PNG which IDAT payload is not a valid zlib stream
function chunk(type, data) {
  const len = Buffer.alloc(4);
  len.writeUInt32BE(data.length);
  const body = Buffer.concat([Buffer.from(type, 'ascii'), data]);
  const crc = Buffer.alloc(4);
  crc.writeUInt32BE(zlib.crc32 ? zlib.crc32(body) : 0);
  return Buffer.concat([len, body, crc]);
}

const ihdr = Buffer.alloc(13);
ihdr.writeUInt32BE(1, 0); // width
ihdr.writeUInt32BE(1, 4); // height
ihdr[8] = 8; // bit depth
ihdr[9] = 6; // color type: RGBA (forces pdfkit to decode pixels)
const corruptPng = Buffer.concat([
  Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]),
  chunk('IHDR', ihdr),
  chunk('IDAT', Buffer.from([0xde, 0xad, 0xbe, 0xef])), // garbage deflate data
  chunk('IEND', Buffer.alloc(0)),
]);

const doc = new PDFDocument();
doc.pipe(fs.createWriteStream('/dev/null'));

try {
  doc.image(corruptPng, 0, 0);
  doc.end();
} catch (err) {
  console.log('Caught:', err.message); // never reached
}

Result: despite the try-catch, the Node process crashes.

Environment: pdfkit 0.19.1, png-js 1.1.0, Node.js 22

Suggested fix

The cleanest option is probably to replace png-js with a maintained decoder such as fast-png, which decodes synchronously, throws normal catchable errors, and seems to be more actively maintained.

Alternatively, fixing png-js itself would be fine too, of course, and I think that the fix would be small. However, this exact problem has already been reported in that repo many years ago (foliojs/png.js#14) and the repository shows little activity, so a fix on the pdfkit side seems more realistic.

Lenguaje dominante
JavaScript
Estrellas
10.7k
Forks
1.2k
Merge medio
21 h 41 min
PR fusionados (30 d)
27

Preparar el entorno

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de foliojs/pdfkit

Todos los issues de foliojs/pdfkit

Issues similares

Más issues de JavaScript

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.