From 0c058f256a195b92f124be10109c95d1fbe93ad8 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Mon, 7 Mar 2022 17:16:54 +0100 Subject: [PATCH] Remove exif tags when processing images --- .../reusable-prepare-peertube-run/action.yml | 2 +- server/helpers/image-utils.ts | 11 ++++ server/tests/fixtures/exif.jpg | Bin 0 -> 10877 bytes server/tests/fixtures/exif.png | Bin 0 -> 21059 bytes server/tests/helpers/image.ts | 58 +++++++++++++++--- support/doc/development/tests.md | 8 ++- 6 files changed, 67 insertions(+), 12 deletions(-) create mode 100644 server/tests/fixtures/exif.jpg create mode 100644 server/tests/fixtures/exif.png diff --git a/.github/actions/reusable-prepare-peertube-run/action.yml b/.github/actions/reusable-prepare-peertube-run/action.yml index 1a6cd2cfd..aa5b897c9 100644 --- a/.github/actions/reusable-prepare-peertube-run/action.yml +++ b/.github/actions/reusable-prepare-peertube-run/action.yml @@ -8,7 +8,7 @@ runs: - name: Setup system dependencies shell: bash run: | - sudo apt-get install postgresql-client-common redis-tools parallel + sudo apt-get install postgresql-client-common redis-tools parallel libimage-exiftool-perl wget --quiet --no-check-certificate "https://download.cpy.re/ffmpeg/ffmpeg-release-4.3.1-64bit-static.tar.xz" tar xf ffmpeg-release-4.3.1-64bit-static.tar.xz mkdir -p $HOME/bin diff --git a/server/helpers/image-utils.ts b/server/helpers/image-utils.ts index b174ae436..28d8fff4c 100644 --- a/server/helpers/image-utils.ts +++ b/server/helpers/image-utils.ts @@ -80,6 +80,8 @@ async function autoResize (options: { const sourceIsPortrait = sourceImage.getWidth() < sourceImage.getHeight() const destIsPortraitOrSquare = newSize.width <= newSize.height + removeExif(sourceImage) + if (sourceIsPortrait && !destIsPortraitOrSquare) { const baseImage = sourceImage.cloneQuiet().cover(newSize.width, newSize.height) .color([ { apply: 'shade', params: [ 50 ] } ]) @@ -106,6 +108,7 @@ function skipProcessing (options: { const { sourceImage, newSize, imageBytes, inputExt, outputExt } = options const { width, height } = newSize + if (hasExif(sourceImage)) return false if (sourceImage.getWidth() > width || sourceImage.getHeight() > height) return false if (inputExt !== outputExt) return false @@ -116,3 +119,11 @@ function skipProcessing (options: { return imageBytes <= 15 * kB } + +function hasExif (image: Jimp) { + return !!(image.bitmap as any).exifBuffer +} + +function removeExif (image: Jimp) { + (image.bitmap as any).exifBuffer = null +} diff --git a/server/tests/fixtures/exif.jpg b/server/tests/fixtures/exif.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2997b38e990f3b28f6dc1dfa0a2314fe72d2f065 GIT binary patch literal 10877 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 V{WV)m(_f|7&#qt3IlTPze*j_H+F1Yq literal 0 HcmV?d00001 diff --git a/server/tests/fixtures/exif.png b/server/tests/fixtures/exif.png new file mode 100644 index 0000000000000000000000000000000000000000..a1a0113f8e0c8c146acb65ce3a5340e2bbdf6dfe GIT binary patch literal 21059 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 HyqNMoKE#$> literal 0 HcmV?d00001 diff --git a/server/tests/helpers/image.ts b/server/tests/helpers/image.ts index 64bd373cc..475ca8fb2 100644 --- a/server/tests/helpers/image.ts +++ b/server/tests/helpers/image.ts @@ -4,6 +4,7 @@ import 'mocha' import { expect } from 'chai' import { readFile, remove } from 'fs-extra' import { join } from 'path' +import { execPromise } from '@server/helpers/core-utils' import { buildAbsoluteFixturePath, root } from '@shared/core-utils' import { processImage } from '../../../server/helpers/image-utils' @@ -20,40 +21,77 @@ async function checkBuffers (path1: string, path2: string, equals: boolean) { } } +async function hasTitleExif (path: string) { + const result = JSON.parse(await execPromise(`exiftool -json ${path}`)) + + return result[0]?.Title === 'should be removed' +} + describe('Image helpers', function () { const imageDestDir = join(root(), 'test-images') - const imageDest = join(imageDestDir, 'test.jpg') + + const imageDestJPG = join(imageDestDir, 'test.jpg') + const imageDestPNG = join(imageDestDir, 'test.png') + const thumbnailSize = { width: 223, height: 122 } it('Should skip processing if the source image is okay', async function () { const input = buildAbsoluteFixturePath('thumbnail.jpg') - await processImage(input, imageDest, thumbnailSize, true) + await processImage(input, imageDestJPG, thumbnailSize, true) - await checkBuffers(input, imageDest, true) + await checkBuffers(input, imageDestJPG, true) }) it('Should not skip processing if the source image does not have the appropriate extension', async function () { const input = buildAbsoluteFixturePath('thumbnail.png') - await processImage(input, imageDest, thumbnailSize, true) + await processImage(input, imageDestJPG, thumbnailSize, true) - await checkBuffers(input, imageDest, false) + await checkBuffers(input, imageDestJPG, false) }) it('Should not skip processing if the source image does not have the appropriate size', async function () { const input = buildAbsoluteFixturePath('preview.jpg') - await processImage(input, imageDest, thumbnailSize, true) + await processImage(input, imageDestJPG, thumbnailSize, true) - await checkBuffers(input, imageDest, false) + await checkBuffers(input, imageDestJPG, false) }) it('Should not skip processing if the source image does not have the appropriate size', async function () { const input = buildAbsoluteFixturePath('thumbnail-big.jpg') - await processImage(input, imageDest, thumbnailSize, true) + await processImage(input, imageDestJPG, thumbnailSize, true) + + await checkBuffers(input, imageDestJPG, false) + }) + + it('Should strip exif for a jpg file that can not be copied', async function () { + const input = buildAbsoluteFixturePath('exif.jpg') + expect(await hasTitleExif(input)).to.be.true + + await processImage(input, imageDestJPG, { width: 100, height: 100 }, true) + await checkBuffers(input, imageDestJPG, false) + + expect(await hasTitleExif(imageDestJPG)).to.be.false + }) + + it('Should strip exif for a jpg file that could be copied', async function () { + const input = buildAbsoluteFixturePath('exif.jpg') + expect(await hasTitleExif(input)).to.be.true + + await processImage(input, imageDestJPG, thumbnailSize, true) + await checkBuffers(input, imageDestJPG, false) + + expect(await hasTitleExif(imageDestJPG)).to.be.false + }) + + it('Should strip exif for png', async function () { + const input = buildAbsoluteFixturePath('exif.png') + expect(await hasTitleExif(input)).to.be.true - await checkBuffers(input, imageDest, false) + await processImage(input, imageDestPNG, thumbnailSize, true) + expect(await hasTitleExif(imageDestPNG)).to.be.false }) after(async function () { - await remove(imageDest) + await remove(imageDestDir) }) }) diff --git a/support/doc/development/tests.md b/support/doc/development/tests.md index 02fc41147..47602156c 100644 --- a/support/doc/development/tests.md +++ b/support/doc/development/tests.md @@ -31,6 +31,12 @@ $ sudo docker run -p 9444:9000 chocobozzz/s3-ninja $ sudo docker run -p 10389:10389 chocobozzz/docker-test-openldap ``` +Ensure you also have these commands: + +``` +$ exiftool --help +``` + ### Test To run all test suites: @@ -39,7 +45,7 @@ To run all test suites: $ npm run test # See scripts/test.sh to run a particular suite ``` -Most of tests can be runned using: +Most of tests can be run using: ```bash TS_NODE_TRANSPILE_ONLY=true npm run mocha -- --timeout 30000 --exit -r ts-node/register -r tsconfig-paths/register --bail server/tests/api/videos/video-transcoder.ts -- 2.41.0