fotos: si no se pueden limpiar, no se suben

Si quitar los metadatos fallaba, blobHandler subia la foto original, con el
GPS. Ahora se para con un 400 y un aviso (imageMetadataUnsafe, en los 11
idiomas): HEIC/HEIF/AVIF y TIFF, que pueden llevar EXIF y aqui no se saben
limpiar, y JPEG/PNG/WebP que no se dejan recorrer. Un JPEG cortado ya no
sube como una imagen vacia. Formatos sin EXIF (BMP, SVG...) pasan igual.

Es el cambio del 10 de octubre, llevado a la copia de la limpieza que vive
ahora en blobHandler.js.
This commit is contained in:
oasis-bot 2026-10-19 02:02:16 +02:00
parent ed77a13c85
commit b64dcf9b7a
2 changed files with 59 additions and 8 deletions

View file

@ -85,11 +85,12 @@ const stripJpeg = (buf) => {
const out = [buf.subarray(0, 2)];
let orientation = 0;
let i = 2;
let scan = false;
while (i + 4 <= buf.length) {
if (buf[i] !== 0xFF) return null; // no es un JPEG sano: no tocarlo
const marker = buf[i + 1];
if (marker === 0xD8 || (marker >= 0xD0 && marker <= 0xD9)) { i += 2; continue; }
if (marker === 0xDA) { out.push(buf.subarray(i)); break; } // datos comprimidos: hasta el final
if (marker === 0xDA) { out.push(buf.subarray(i)); scan = true; break; } // datos comprimidos: hasta el final
const len = buf.readUInt16BE(i + 2);
if (len < 2 || i + 2 + len > buf.length) return null;
const seg = buf.subarray(i + 4, i + 2 + len);
@ -97,6 +98,8 @@ const stripJpeg = (buf) => {
if (!JPEG_DROP.has(marker)) out.push(buf.subarray(i, i + 2 + len));
i += 2 + len;
}
// sin datos de imagen (SOS) es un JPEG cortado: devolver la cabecera sola subiria una foto vacia
if (!scan) return null;
if (orientation > 1) {
const exif = exifOnlyOrientation(orientation);
const head = Buffer.alloc(4);
@ -150,14 +153,40 @@ const stripWebp = (buf) => {
return Buffer.concat([head, body]);
};
/** Limpia segun el formato; si algo no cuadra, devuelve la imagen tal cual. */
const stripMetadataPure = (buffer) => {
try {
const out = stripJpeg(buffer) || stripPng(buffer) || stripWebp(buffer);
return out && out.length ? out : buffer;
} catch {
return buffer;
// Formatos que pueden llevar EXIF (GPS incluido) y que aqui no se saben limpiar:
// HEIC/HEIF/AVIF (caja ftyp) y TIFF. Antes subian tal cual, con la ubicacion.
const isUncleanable = (buf) => {
if (buf.length >= 12 && buf.toString("latin1", 4, 8) === "ftyp") {
return /^(heic|heix|hevc|hevx|heim|heis|mif1|msf1|avif|avis)$/.test(buf.toString("latin1", 8, 12));
}
return buf.length >= 4 && (buf.toString("latin1", 0, 4) === "II*\0" || buf.toString("latin1", 0, 4) === "MM\0*");
};
const looksCleanable = (buf) =>
(buf.length >= 2 && buf[0] === 0xFF && buf[1] === 0xD8) ||
(buf.length >= 8 && buf.subarray(0, 8).equals(Buffer.from([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]))) ||
(buf.length >= 12 && buf.toString("latin1", 0, 4) === "RIFF" && buf.toString("latin1", 8, 12) === "WEBP");
const unsafe = () => {
let msg = "This image could not be cleaned of its metadata, so it has not been uploaded.";
try { msg = require("../views/main_views").i18n.imageMetadataUnsafe || msg; } catch (_) {}
const err = new Error(msg);
err.status = 400;
return err;
};
/**
* Limpia segun el formato. Si es JPEG/PNG/WebP y no se puede limpiar, o es un
* formato con EXIF que no sabemos limpiar, LANZA: es mejor no subir la foto que
* subirla con la ubicacion. Formatos sin metadatos de ese tipo (BMP, SVG, ICO...)
* pasan tal cual.
*/
const stripMetadataPure = (buffer) => {
if (isUncleanable(buffer)) throw unsafe();
if (!looksCleanable(buffer)) return buffer;
let out = null;
try { out = stripJpeg(buffer) || stripPng(buffer) || stripWebp(buffer); } catch (_) { out = null; }
if (!out || !out.length) throw unsafe();
return out;
};
const stripImageMetadata = async (buffer) => {