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:
parent
ed77a13c85
commit
b64dcf9b7a
2 changed files with 59 additions and 8 deletions
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
module.exports = {
|
||||
en: {
|
||||
// fotos que no se pueden limpiar (blobHandler.js)
|
||||
imageMetadataUnsafe: "This image could not be cleaned of its metadata (location, camera…), so it has not been uploaded. Save it as JPEG or PNG and try again.",
|
||||
// Karvan
|
||||
karvanActive: "Active rooms",
|
||||
karvanBlurb: "Rooms that live only in memory and destroy themselves.",
|
||||
|
|
@ -80,6 +82,8 @@ module.exports = {
|
|||
spreadMore: "more"
|
||||
},
|
||||
es: {
|
||||
// fotos que no se pueden limpiar (blobHandler.js)
|
||||
imageMetadataUnsafe: "No se han podido quitar los metadatos de esta imagen (ubicación, cámara…), así que no se ha subido. Guárdala como JPEG o PNG y vuelve a intentarlo.",
|
||||
// Karvan
|
||||
karvanActive: "Salas activas",
|
||||
karvanBlurb: "Salas que viven solo en memoria y se destruyen solas.",
|
||||
|
|
@ -149,6 +153,8 @@ module.exports = {
|
|||
totalspreads: "Réplicas totales"
|
||||
},
|
||||
fr: {
|
||||
// fotos que no se pueden limpiar (blobHandler.js)
|
||||
imageMetadataUnsafe: "Impossible de retirer les métadonnées de cette image (position, appareil…), elle n'a donc pas été envoyée. Enregistrez-la en JPEG ou PNG et réessayez.",
|
||||
// Karvan
|
||||
karvanActive: "Salons actifs",
|
||||
karvanBlurb: "Des salons qui ne vivent qu'en mémoire et se détruisent d'eux-mêmes.",
|
||||
|
|
@ -212,6 +218,8 @@ module.exports = {
|
|||
spreadMore: "plus"
|
||||
},
|
||||
eu: {
|
||||
// fotos que no se pueden limpiar (blobHandler.js)
|
||||
imageMetadataUnsafe: "Ezin izan dira irudi honen metadatuak kendu (kokapena, kamera…), beraz ez da igo. Gorde JPEG edo PNG gisa eta saiatu berriro.",
|
||||
// Karvan
|
||||
karvanActive: "Gela aktiboak",
|
||||
karvanBlurb: "Memorian bakarrik bizi diren eta beren kabuz suntsitzen diren gelak.",
|
||||
|
|
@ -275,6 +283,8 @@ module.exports = {
|
|||
spreadMore: "gehiago"
|
||||
},
|
||||
de: {
|
||||
// fotos que no se pueden limpiar (blobHandler.js)
|
||||
imageMetadataUnsafe: "Die Metadaten dieses Bildes (Standort, Kamera…) konnten nicht entfernt werden, daher wurde es nicht hochgeladen. Speichere es als JPEG oder PNG und versuche es erneut.",
|
||||
// Karvan
|
||||
karvanActive: "Aktive Räume",
|
||||
karvanBlurb: "Räume, die nur im Speicher leben und sich von selbst zerstören.",
|
||||
|
|
@ -338,6 +348,8 @@ module.exports = {
|
|||
spreadMore: "mehr"
|
||||
},
|
||||
it: {
|
||||
// fotos que no se pueden limpiar (blobHandler.js)
|
||||
imageMetadataUnsafe: "Non è stato possibile rimuovere i metadati di questa immagine (posizione, fotocamera…), quindi non è stata caricata. Salvala come JPEG o PNG e riprova.",
|
||||
// Karvan
|
||||
karvanActive: "Stanze attive",
|
||||
karvanBlurb: "Stanze che vivono solo in memoria e si distruggono da sole.",
|
||||
|
|
@ -401,6 +413,8 @@ module.exports = {
|
|||
spreadMore: "altro"
|
||||
},
|
||||
pt: {
|
||||
// fotos que no se pueden limpiar (blobHandler.js)
|
||||
imageMetadataUnsafe: "Não foi possível remover os metadados desta imagem (localização, câmara…), por isso não foi enviada. Guarda-a como JPEG ou PNG e tenta de novo.",
|
||||
// Karvan
|
||||
karvanActive: "Salas ativas",
|
||||
karvanBlurb: "Salas que vivem apenas em memória e destroem-se sozinhas.",
|
||||
|
|
@ -464,6 +478,8 @@ module.exports = {
|
|||
spreadMore: "mais"
|
||||
},
|
||||
zh: {
|
||||
// fotos que no se pueden limpiar (blobHandler.js)
|
||||
imageMetadataUnsafe: "无法清除这张图片的元数据(位置、相机等),因此没有上传。请另存为 JPEG 或 PNG 后重试。",
|
||||
// Karvan
|
||||
karvanActive: "活跃房间",
|
||||
karvanBlurb: "只存在于内存中、会自行销毁的房间。",
|
||||
|
|
@ -527,6 +543,8 @@ module.exports = {
|
|||
spreadMore: "更多"
|
||||
},
|
||||
ar: {
|
||||
// fotos que no se pueden limpiar (blobHandler.js)
|
||||
imageMetadataUnsafe: "تعذّر إزالة البيانات الوصفية من هذه الصورة (الموقع، الكاميرا…)، لذلك لم تُرفع. احفظها بصيغة JPEG أو PNG وحاول مجددًا.",
|
||||
// Karvan
|
||||
karvanActive: "الغرف النشطة",
|
||||
karvanBlurb: "غرف تعيش في الذاكرة فقط وتدمّر نفسها بنفسها.",
|
||||
|
|
@ -590,6 +608,8 @@ module.exports = {
|
|||
spreadMore: "المزيد"
|
||||
},
|
||||
hi: {
|
||||
// fotos que no se pueden limpiar (blobHandler.js)
|
||||
imageMetadataUnsafe: "इस छवि का मेटाडेटा (स्थान, कैमरा…) हटाया नहीं जा सका, इसलिए इसे अपलोड नहीं किया गया। इसे JPEG या PNG के रूप में सहेजें और फिर कोशिश करें।",
|
||||
// Karvan
|
||||
karvanActive: "सक्रिय कक्ष",
|
||||
karvanBlurb: "ऐसे कक्ष जो केवल मेमोरी में रहते हैं और स्वयं नष्ट हो जाते हैं।",
|
||||
|
|
@ -653,6 +673,8 @@ module.exports = {
|
|||
spreadMore: "और"
|
||||
},
|
||||
ru: {
|
||||
// fotos que no se pueden limpiar (blobHandler.js)
|
||||
imageMetadataUnsafe: "Не удалось удалить метаданные этого изображения (местоположение, камера…), поэтому оно не загружено. Сохраните его как JPEG или PNG и попробуйте снова.",
|
||||
// Karvan
|
||||
karvanActive: "Активные комнаты",
|
||||
karvanBlurb: "Комнаты, которые живут только в памяти и уничтожаются сами.",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue