Page MenuHomePhorge

No OneTemporary

Authored By
Unknown
Size
53 KB
Referenced Files
None
Subscribers
None
diff --git a/Dockerfile b/Dockerfile
index 4018d9bc9..1067ef1c8 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -42,7 +42,7 @@ ARG HOME=/opt/pleroma
ARG DATA=/var/lib/pleroma
RUN apk update &&\
- apk add exiftool ffmpeg vips libmagic ncurses postgresql-client &&\
+ apk add ffmpeg vips libmagic ncurses postgresql-client &&\
adduser --system --shell /bin/false --home ${HOME} pleroma &&\
mkdir -p ${DATA}/uploads &&\
mkdir -p ${DATA}/static &&\
diff --git a/Dockerfile-e2e b/Dockerfile-e2e
index df8bb39aa..59a285a67 100644
--- a/Dockerfile-e2e
+++ b/Dockerfile-e2e
@@ -41,7 +41,7 @@ ARG HOME=/opt/pleroma
ARG DATA=/var/lib/pleroma
RUN apk update &&\
- apk add exiftool ffmpeg vips libmagic ncurses postgresql-client &&\
+ apk add ffmpeg vips libmagic ncurses postgresql-client &&\
adduser --system --shell /bin/false --home ${HOME} pleroma &&\
mkdir -p ${DATA}/uploads &&\
mkdir -p ${DATA}/static &&\
diff --git a/changelog.d/exif-heic-gps.security b/changelog.d/exif-heic-gps.security
new file mode 100644
index 000000000..0b6593f5f
--- /dev/null
+++ b/changelog.d/exif-heic-gps.security
@@ -0,0 +1 @@
+Strip GPS data from WebP and HEIC/AVIF uploads; the Exiftool.StripLocation filter used to skip them; GPS data in XMP (including DJI drone positions) is stripped too
diff --git a/changelog.d/exif-stripper.change b/changelog.d/exif-stripper.change
new file mode 100644
index 000000000..ba8d47a19
--- /dev/null
+++ b/changelog.d/exif-stripper.change
@@ -0,0 +1 @@
+Exiftool upload filters are replaced by the built-in Exif.StripLocation and Exif.ReadDescription filters; exiftool is no longer required, old filter names still work but are deprecated
diff --git a/docs/administration/CLI_tasks/instance.md b/docs/administration/CLI_tasks/instance.md
index 88509cf5b..622299629 100644
--- a/docs/administration/CLI_tasks/instance.md
+++ b/docs/administration/CLI_tasks/instance.md
@@ -37,8 +37,8 @@ If any of the options are left unspecified, you will be prompted interactively.
- `--static-dir <path>` - the directory custom public files should be read from (custom emojis, frontend bundle overrides, robots.txt, etc.)
- `--listen-ip <ip>` - the ip the app should listen to, defaults to 127.0.0.1
- `--listen-port <port>` - the port the app should listen to, defaults to 4000
-- `--strip-uploads-location <Y|N>` - use ExifTool to strip uploads of sensitive location data
-- `--read-uploads-description <Y|N>` - use ExifTool to read image descriptions from uploads
+- `--strip-uploads-location <Y|N>` - strip sensitive location data from uploaded images
+- `--read-uploads-description <Y|N>` - read image descriptions from upload metadata
- `--anonymize-uploads <Y|N>` - randomize uploaded filenames
- `--dedupe-uploads <Y|N>` - store files based on their hash to reduce data storage requirements if duplicates are uploaded with different filenames
- `--skip-release-env` - skip generation the release environment file
diff --git a/docs/configuration/cheatsheet.md b/docs/configuration/cheatsheet.md
index d49aa51f7..869179798 100644
--- a/docs/configuration/cheatsheet.md
+++ b/docs/configuration/cheatsheet.md
@@ -705,15 +705,19 @@ This filter replaces the filename (not the path) of an upload. For complete obfu
No specific configuration.
-#### Pleroma.Upload.Filter.Exiftool.StripLocation
+#### Pleroma.Upload.Filter.Exif.StripLocation
-This filter only strips the GPS and location metadata with Exiftool leaving color profiles and attributes intact.
+This filter strips GPS and location metadata from JPEG, WebP, HEIC, AVIF and TIFF images, leaving color profiles and attributes intact. For PNG images it removes all text and EXIF metadata chunks. Images are not re-encoded, and other formats are left unchanged.
+
+It replaces the deprecated `Pleroma.Upload.Filter.Exiftool.StripLocation`, which no longer needs exiftool.
No specific configuration.
-#### Pleroma.Upload.Filter.Exiftool.ReadDescription
+#### Pleroma.Upload.Filter.Exif.ReadDescription
+
+This filter reads the ImageDescription and iptc:Caption-Abstract fields so clients can prefill the media description field.
-This filter reads the ImageDescription and iptc:Caption-Abstract fields with Exiftool so clients can prefill the media description field.
+It replaces the deprecated `Pleroma.Upload.Filter.Exiftool.ReadDescription`, which no longer needs exiftool.
No specific configuration.
diff --git a/docs/installation/alpine_linux_en.md b/docs/installation/alpine_linux_en.md
index 7154bca48..777a94dfc 100644
--- a/docs/installation/alpine_linux_en.md
+++ b/docs/installation/alpine_linux_en.md
@@ -68,7 +68,7 @@ sudo rc-update add postgresql
### Install media / graphics packages (optional, see [`docs/installation/optional/media_graphics_packages.md`](../installation/optional/media_graphics_packages.md))
```shell
-sudo apk add ffmpeg imagemagick exiftool
+sudo apk add ffmpeg imagemagick
```
### Install PleromaBE
diff --git a/docs/installation/debian_based_en.md b/docs/installation/debian_based_en.md
index f9086d2f9..2c85e52aa 100644
--- a/docs/installation/debian_based_en.md
+++ b/docs/installation/debian_based_en.md
@@ -36,7 +36,7 @@ sudo apt install elixir erlang-dev erlang-nox
### Optional packages: [`docs/installation/optional/media_graphics_packages.md`](../installation/optional/media_graphics_packages.md)
```shell
-sudo apt install imagemagick ffmpeg libimage-exiftool-perl
+sudo apt install imagemagick ffmpeg
```
### Install PleromaBE
diff --git a/docs/installation/debian_based_jp.md b/docs/installation/debian_based_jp.md
index c853840c2..e374900e5 100644
--- a/docs/installation/debian_based_jp.md
+++ b/docs/installation/debian_based_jp.md
@@ -28,7 +28,6 @@ Note: This article is potentially outdated because at this time we may not have
- `certbot` (または何らかのLet's Encrypt向けACMEクライアント)
- `ImageMagick`
- `ffmpeg`
-- `exiftool`
### システムを準備する
@@ -60,7 +59,7 @@ sudo apt install elixir erlang-dev erlang-nox
### オプションパッケージ: [`docs/installation/optional/media_graphics_packages.md`](../installation/optional/media_graphics_packages.md)
```shell
-sudo apt install imagemagick ffmpeg libimage-exiftool-perl
+sudo apt install imagemagick ffmpeg
```
### Pleroma BE (バックエンド) をインストールします
diff --git a/docs/installation/freebsd_en.md b/docs/installation/freebsd_en.md
index 920bc7d35..2b857b9a9 100644
--- a/docs/installation/freebsd_en.md
+++ b/docs/installation/freebsd_en.md
@@ -31,7 +31,7 @@ Setup the required services to automatically start at boot, using `sysrc(8)`.
### Install media / graphics packages (optional, see [`docs/installation/optional/media_graphics_packages.md`](../installation/optional/media_graphics_packages.md))
```shell
-# pkg install imagemagick ffmpeg p5-Image-ExifTool vips
+# pkg install imagemagick ffmpeg vips
```
## Configuring Pleroma
diff --git a/docs/installation/generic_dependencies.include b/docs/installation/generic_dependencies.include
index 86dbc6838..da7e50848 100644
--- a/docs/installation/generic_dependencies.include
+++ b/docs/installation/generic_dependencies.include
@@ -12,7 +12,6 @@
* ImageMagick
* FFmpeg
-* exiftool
!!!info
**Source installs only**
diff --git a/docs/installation/gentoo_en.md b/docs/installation/gentoo_en.md
index dc47d27f8..066497b85 100644
--- a/docs/installation/gentoo_en.md
+++ b/docs/installation/gentoo_en.md
@@ -41,7 +41,6 @@ Gentoo quite pointedly does not come with a cron daemon installed, and as such i
* `app-crypt/certbot-nginx` (nginx certbot plugin that allows use of the all-powerful `--nginx` flag on certbot)
* `media-gfx/imagemagick`
* `media-video/ffmpeg`
-* `media-libs/exiftool`
### Prepare the system
@@ -97,7 +96,7 @@ Not only does this make it much easier to deploy changes you make, as you can co
### Install media / graphics packages (optional, see [`docs/installation/optional/media_graphics_packages.md`](docs/installation/optional/media_graphics_packages.md))
```shell
-# emerge --ask media-video/ffmpeg media-gfx/imagemagick media-libs/exiftool
+# emerge --ask media-video/ffmpeg media-gfx/imagemagick
```
### Install PleromaBE
diff --git a/docs/installation/gentoo_otp_en.md b/docs/installation/gentoo_otp_en.md
index 20d8835da..352d9b807 100644
--- a/docs/installation/gentoo_otp_en.md
+++ b/docs/installation/gentoo_otp_en.md
@@ -25,7 +25,6 @@ Gentoo quite pointedly does not come with a cron daemon installed, and as such i
* `app-crypt/certbot-nginx` (nginx certbot plugin that allows use of the all-powerful `--nginx` flag on certbot)
* `media-gfx/imagemagick`
* `media-video/ffmpeg`
-* `media-libs/exiftool`
### Prepare the system
@@ -68,7 +67,7 @@ The output from emerging postgresql should give you a command for initializing t
See [Optional software packages needed for specific functionality](optional/media_graphics_packages.md) for details.
```shell
-# emerge --ask media-video/ffmpeg media-gfx/imagemagick media-libs/exiftool
+# emerge --ask media-video/ffmpeg media-gfx/imagemagick
```
### Setup PleromaBE
diff --git a/docs/installation/openbsd_en.md b/docs/installation/openbsd_en.md
index 82d137e96..1cc638ae0 100644
--- a/docs/installation/openbsd_en.md
+++ b/docs/installation/openbsd_en.md
@@ -26,12 +26,11 @@ Independently of the reverse proxy, [acme-client(1)](https://man.openbsd.org/acm
* ImageMagick
* ffmpeg
- * exiftool
To install the above:
```
-# pkg_add ImageMagick ffmpeg p5-Image-ExifTool
+# pkg_add ImageMagick ffmpeg
```
For more information read [`docs/installation/optional/media_graphics_packages.md`](../installation/optional/media_graphics_packages.md):
diff --git a/docs/installation/openbsd_fi.md b/docs/installation/openbsd_fi.md
index 858e64020..9ed553280 100644
--- a/docs/installation/openbsd_fi.md
+++ b/docs/installation/openbsd_fi.md
@@ -25,11 +25,10 @@ Asenna tarvittava ohjelmisto:
[`docs/installation/optional/media_graphics_packages.md`](../installation/optional/media_graphics_packages.md):
* ImageMagick
* ffmpeg
- * exiftool
Asenna tarvittava ohjelmisto:
-`# pkg_add ImageMagick ffmpeg p5-Image-ExifTool`
+`# pkg_add ImageMagick ffmpeg`
Luo postgresql-tietokanta:
diff --git a/docs/installation/optional/media_graphics_packages.md b/docs/installation/optional/media_graphics_packages.md
index 89125d1c6..e7e6283f8 100644
--- a/docs/installation/optional/media_graphics_packages.md
+++ b/docs/installation/optional/media_graphics_packages.md
@@ -2,9 +2,8 @@
For specific Pleroma functionality (which is disabled by default) some or all of the below packages are required:
-* `ImageMagic`
+* `ImageMagick`
* `ffmpeg`
-* `exiftool`
Please refer to documentation in `docs/installation` on how to install them on specific OS.
@@ -26,12 +25,3 @@ It is required for the following Pleroma features:
It is required for the following Pleroma features:
* Media preview proxy for videos (related config: `media_preview_proxy/enabled` in `config/config.exs`)
-
-## `exiftool`
-
-`exiftool` is media files metadata reader/writer.
-
-It is required for the following Pleroma features:
-
-* `Pleroma.Upload.Filters.Exiftool.StripLocation` upload filter (related config: `Pleroma.Upload/filters` in `config/config.exs`)
-* `Pleroma.Upload.Filters.Exiftool.ReadDescription` upload filter (related config: `Pleroma.Upload/filters` in `config/config.exs`)
diff --git a/docs/installation/otp_en.md b/docs/installation/otp_en.md
index 0f296a9f6..7132ef5a5 100644
--- a/docs/installation/otp_en.md
+++ b/docs/installation/otp_en.md
@@ -61,17 +61,16 @@ Other than things bundled in the OTP release Pleroma depends on:
Per [`docs/installation/optional/media_graphics_packages.md`](optional/media_graphics_packages.md):
* ImageMagick
* ffmpeg
- * exiftool
=== "Alpine"
```
apk update
- apk add imagemagick ffmpeg exiftool
+ apk add imagemagick ffmpeg
```
=== "Debian/Ubuntu"
```
- apt install imagemagick ffmpeg libimage-exiftool-perl
+ apt install imagemagick ffmpeg
```
## Setup
diff --git a/installation/release-to-docker/Dockerfile b/installation/release-to-docker/Dockerfile
index 0ffeb5d5b..ac9118aba 100644
--- a/installation/release-to-docker/Dockerfile
+++ b/installation/release-to-docker/Dockerfile
@@ -12,7 +12,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
openssl libssl3 \
libmagic1t64 file \
postgresql-client \
- ffmpeg imagemagick libimage-exiftool-perl \
+ ffmpeg imagemagick \
libvips42t64 \
unzip \
curl \
diff --git a/lib/mix/tasks/pleroma/instance.ex b/lib/mix/tasks/pleroma/instance.ex
index 143af5cdd..1a196264a 100644
--- a/lib/mix/tasks/pleroma/instance.ex
+++ b/lib/mix/tasks/pleroma/instance.ex
@@ -162,38 +162,20 @@ defmodule Mix.Tasks.Pleroma.Instance do
)
|> Path.expand()
- {strip_uploads_location_message, strip_uploads_location_default} =
- if Pleroma.Utils.command_available?("exiftool") do
- {"Do you want to strip location (GPS) data from uploaded images? This requires exiftool, it was detected as installed. (y/n)",
- "y"}
- else
- {"Do you want to strip location (GPS) data from uploaded images? This requires exiftool, it was detected as not installed, please install it if you answer yes. (y/n)",
- "n"}
- end
-
strip_uploads_location =
get_option(
options,
:strip_uploads_location,
- strip_uploads_location_message,
- strip_uploads_location_default
+ "Do you want to strip location (GPS) data from uploaded images? (y/n)",
+ "y"
) === "y"
- {read_uploads_description_message, read_uploads_description_default} =
- if Pleroma.Utils.command_available?("exiftool") do
- {"Do you want to read data from uploaded files so clients can use it to prefill fields like image description? This requires exiftool, it was detected as installed. (y/n)",
- "y"}
- else
- {"Do you want to read data from uploaded files so clients can use it to prefill fields like image description? This requires exiftool, it was detected as not installed, please install it if you answer yes. (y/n)",
- "n"}
- end
-
read_uploads_description =
get_option(
options,
:read_uploads_description,
- read_uploads_description_message,
- read_uploads_description_default
+ "Do you want to read metadata from uploaded images so clients can prefill the media description field? (y/n)",
+ "y"
) === "y"
anonymize_uploads =
@@ -324,14 +306,14 @@ defmodule Mix.Tasks.Pleroma.Instance do
defp upload_filters(filters) when is_map(filters) do
enabled_filters =
if filters.strip_location do
- [Pleroma.Upload.Filter.Exiftool.StripLocation]
+ [Pleroma.Upload.Filter.Exif.StripLocation]
else
[]
end
enabled_filters =
if filters.read_description do
- enabled_filters ++ [Pleroma.Upload.Filter.Exiftool.ReadDescription]
+ enabled_filters ++ [Pleroma.Upload.Filter.Exif.ReadDescription]
else
enabled_filters
end
diff --git a/lib/pleroma/application_requirements.ex b/lib/pleroma/application_requirements.ex
index 87ecb7e2d..d9f4eb1d0 100644
--- a/lib/pleroma/application_requirements.ex
+++ b/lib/pleroma/application_requirements.ex
@@ -168,8 +168,6 @@ defmodule Pleroma.ApplicationRequirements do
defp check_system_commands!(:ok) do
filter_commands_statuses = [
- check_filter(Pleroma.Upload.Filter.Exiftool.StripLocation, "exiftool"),
- check_filter(Pleroma.Upload.Filter.Exiftool.ReadDescription, "exiftool"),
check_filter(Pleroma.Upload.Filter.Mogrify, "mogrify"),
check_filter(Pleroma.Upload.Filter.Mogrifun, "mogrify"),
check_filter(Pleroma.Upload.Filter.AnalyzeMetadata, "ffprobe")
diff --git a/lib/pleroma/config/deprecation_warnings.ex b/lib/pleroma/config/deprecation_warnings.ex
index 934a4a02e..b383b646c 100644
--- a/lib/pleroma/config/deprecation_warnings.ex
+++ b/lib/pleroma/config/deprecation_warnings.ex
@@ -22,40 +22,44 @@ defmodule Pleroma.Config.DeprecationWarnings do
@logger_formatter_config_knobs [:colors, :format, :metadata, :truncate, :utc_log]
+ @exiftool_filter_replacements %{
+ Pleroma.Upload.Filter.Exiftool => Pleroma.Upload.Filter.Exif.StripLocation,
+ Pleroma.Upload.Filter.Exiftool.StripLocation => Pleroma.Upload.Filter.Exif.StripLocation,
+ Pleroma.Upload.Filter.Exiftool.ReadDescription => Pleroma.Upload.Filter.Exif.ReadDescription
+ }
+
def check_exiftool_filter do
filters = Config.get([Pleroma.Upload]) |> Keyword.get(:filters, [])
- if Pleroma.Upload.Filter.Exiftool in filters do
- Logger.warning("""
- !!!DEPRECATION WARNING!!!
- Your config is using Exiftool as a filter instead of Exiftool.StripLocation. This should work for now, but you are advised to change to the new configuration to prevent possible issues later:
-
- ```
- config :pleroma, Pleroma.Upload,
- filters: [Pleroma.Upload.Filter.Exiftool]
- ```
+ deprecated_filters =
+ filters
+ |> Enum.filter(&Map.has_key?(@exiftool_filter_replacements, &1))
+ |> Enum.uniq()
- Is now
+ case deprecated_filters do
+ [] ->
+ :ok
+ deprecated_filters ->
+ replacements =
+ Enum.map_join(deprecated_filters, fn filter ->
+ "\n* `#{inspect(filter)}` is now `#{inspect(@exiftool_filter_replacements[filter])}`"
+ end)
- ```
- config :pleroma, Pleroma.Upload,
- filters: [Pleroma.Upload.Filter.Exiftool.StripLocation]
- ```
- """)
+ Logger.warning("""
+ !!!DEPRECATION WARNING!!!
+ Your config is using Exiftool upload filters. Pleroma no longer needs exiftool, the filters were replaced by built-in ones. The old names should work for now, but you are advised to change to the new configuration to prevent possible issues later:
+ #{replacements}
+ """)
- new_config =
- filters
- |> Enum.map(fn
- Pleroma.Upload.Filter.Exiftool -> Pleroma.Upload.Filter.Exiftool.StripLocation
- filter -> filter
- end)
+ new_config =
+ filters
+ |> Enum.map(&Map.get(@exiftool_filter_replacements, &1, &1))
+ |> Enum.uniq()
- Config.put([Pleroma.Upload, :filters], new_config)
+ Config.put([Pleroma.Upload, :filters], new_config)
- :error
- else
- :ok
+ :error
end
end
diff --git a/lib/pleroma/upload/filter/exif/read_description.ex b/lib/pleroma/upload/filter/exif/read_description.ex
new file mode 100644
index 000000000..96b219042
--- /dev/null
+++ b/lib/pleroma/upload/filter/exif/read_description.ex
@@ -0,0 +1,32 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2026 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Upload.Filter.Exif.ReadDescription do
+ @moduledoc """
+ Reads a description from image metadata when the upload has none yet.
+
+ Checks EXIF ImageDescription, then IPTC Caption-Abstract (and XMP for GIF). Blank
+ values and values longer than `[:instance, :description_limit]` are ignored.
+ """
+
+ @behaviour Pleroma.Upload.Filter
+
+ @impl true
+ def filter(%Pleroma.Upload{description: description}) when is_binary(description),
+ do: {:ok, :noop}
+
+ def filter(%Pleroma.Upload{tempfile: file, content_type: "image" <> _} = upload) do
+ max_length = Pleroma.Config.get([:instance, :description_limit])
+
+ case ExifStripper.Description.read_file(file, max_length: max_length) do
+ description when is_binary(description) ->
+ {:ok, :filtered, %{upload | description: description}}
+
+ nil ->
+ {:ok, :noop}
+ end
+ end
+
+ def filter(_), do: {:ok, :noop}
+end
diff --git a/lib/pleroma/upload/filter/exif/strip_location.ex b/lib/pleroma/upload/filter/exif/strip_location.ex
new file mode 100644
index 000000000..622f8e335
--- /dev/null
+++ b/lib/pleroma/upload/filter/exif/strip_location.ex
@@ -0,0 +1,25 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2026 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Upload.Filter.Exif.StripLocation do
+ @moduledoc """
+ Strips GPS metadata from uploaded images and overwrites the file in place.
+
+ Supports JPEG, PNG, WebP, TIFF and ISO-BMFF images (HEIC, AVIF). This is lossless:
+ the image data is not re-encoded. Other image types are left untouched.
+ """
+
+ @behaviour Pleroma.Upload.Filter
+
+ @impl true
+ def filter(%Pleroma.Upload{tempfile: file, content_type: "image" <> _}) do
+ case ExifStripper.GpsStripper.strip_gps_data(file, file) do
+ :ok -> {:ok, :filtered}
+ :noop -> {:ok, :noop}
+ {:error, _} = error -> error
+ end
+ end
+
+ def filter(_), do: {:ok, :noop}
+end
diff --git a/lib/pleroma/upload/filter/exiftool.ex b/lib/pleroma/upload/filter/exiftool.ex
new file mode 100644
index 000000000..869ac8b2b
--- /dev/null
+++ b/lib/pleroma/upload/filter/exiftool.ex
@@ -0,0 +1,14 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2026 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Upload.Filter.Exiftool do
+ @moduledoc """
+ Deprecated: use `Pleroma.Upload.Filter.Exif.StripLocation`. This module delegates to it.
+ """
+
+ @behaviour Pleroma.Upload.Filter
+
+ @impl true
+ defdelegate filter(upload), to: Pleroma.Upload.Filter.Exif.StripLocation
+end
diff --git a/lib/pleroma/upload/filter/exiftool/read_description.ex b/lib/pleroma/upload/filter/exiftool/read_description.ex
index 8283b8643..c1c60dfdc 100644
--- a/lib/pleroma/upload/filter/exiftool/read_description.ex
+++ b/lib/pleroma/upload/filter/exiftool/read_description.ex
@@ -4,51 +4,11 @@
defmodule Pleroma.Upload.Filter.Exiftool.ReadDescription do
@moduledoc """
- Gets a valid description from the related EXIF tags and provides them in the response if no description is provided yet.
- It will first check ImageDescription, when that doesn't probide a valid description, it will check iptc:Caption-Abstract.
- A valid description means the fields are filled in and not too long (see `:instance, :description_limit`).
+ Deprecated: use `Pleroma.Upload.Filter.Exif.ReadDescription`. This module delegates to it.
"""
- @behaviour Pleroma.Upload.Filter
-
- def filter(%Pleroma.Upload{description: description})
- when is_binary(description),
- do: {:ok, :noop}
-
- def filter(%Pleroma.Upload{tempfile: file} = upload),
- do: {:ok, :filtered, upload |> Map.put(:description, read_description_from_exif_data(file))}
-
- def filter(_, _), do: {:ok, :noop}
- defp read_description_from_exif_data(file) do
- nil
- |> read_when_empty(file, "-ImageDescription")
- |> read_when_empty(file, "-iptc:Caption-Abstract")
- end
-
- defp read_when_empty(current_description, _, _) when is_binary(current_description),
- do: current_description
-
- defp read_when_empty(_, file, tag) do
- if File.exists?(file) do
- try do
- {tag_content, 0} =
- System.cmd("exiftool", ["-m", "-b", "-s3", tag, file],
- stderr_to_stdout: false,
- parallelism: true
- )
-
- tag_content = String.trim(tag_content)
+ @behaviour Pleroma.Upload.Filter
- if tag_content != "" and
- String.length(tag_content) <=
- Pleroma.Config.get([:instance, :description_limit]),
- do: tag_content,
- else: nil
- rescue
- _ in ErlangError -> nil
- end
- else
- nil
- end
- end
+ @impl true
+ defdelegate filter(upload), to: Pleroma.Upload.Filter.Exif.ReadDescription
end
diff --git a/lib/pleroma/upload/filter/exiftool/strip_location.ex b/lib/pleroma/upload/filter/exiftool/strip_location.ex
index 23346d234..3e64adca8 100644
--- a/lib/pleroma/upload/filter/exiftool/strip_location.ex
+++ b/lib/pleroma/upload/filter/exiftool/strip_location.ex
@@ -4,30 +4,11 @@
defmodule Pleroma.Upload.Filter.Exiftool.StripLocation do
@moduledoc """
- Strips GPS related EXIF tags and overwrites the file in place.
- Also strips or replaces filesystem metadata e.g., timestamps.
+ Deprecated: use `Pleroma.Upload.Filter.Exif.StripLocation`. This module delegates to it.
"""
- @behaviour Pleroma.Upload.Filter
-
- # Formats not compatible with exiftool at this time
- def filter(%Pleroma.Upload{content_type: "image/heic"}), do: {:ok, :noop}
- def filter(%Pleroma.Upload{content_type: "image/webp"}), do: {:ok, :noop}
- def filter(%Pleroma.Upload{content_type: "image/svg" <> _}), do: {:ok, :noop}
- def filter(%Pleroma.Upload{tempfile: file, content_type: "image" <> _}) do
- try do
- case System.cmd("exiftool", ["-m", "-overwrite_original", "-gps:all=", "-png:all=", file],
- stderr_to_stdout: true,
- parallelism: true
- ) do
- {_response, 0} -> {:ok, :filtered}
- {error, _} -> {:error, error}
- end
- rescue
- e in ErlangError ->
- {:error, "#{__MODULE__}: #{inspect(e)}"}
- end
- end
+ @behaviour Pleroma.Upload.Filter
- def filter(_), do: {:ok, :noop}
+ @impl true
+ defdelegate filter(upload), to: Pleroma.Upload.Filter.Exif.StripLocation
end
diff --git a/mix.exs b/mix.exs
index e2af5fda6..6c2cfe9c9 100644
--- a/mix.exs
+++ b/mix.exs
@@ -205,6 +205,7 @@ defmodule Pleroma.Mixfile do
{:majic, "~> 1.2"},
{:open_api_spex, "~> 3.22"},
{:ecto_psql_extras, "~> 0.8"},
+ {:exif_stripper, "~> 0.1.3"},
{:vix, "~> 0.36"},
{:elixir_make, "~> 0.7.8", override: true},
{:blurhash, "~> 0.1.0", hex: :rinpatch_blurhash},
diff --git a/mix.lock b/mix.lock
index 7ab59ee46..9e60402e5 100644
--- a/mix.lock
+++ b/mix.lock
@@ -47,6 +47,7 @@
"ex_doc": {:hex, :ex_doc, "0.38.2", "504d25eef296b4dec3b8e33e810bc8b5344d565998cd83914ffe1b8503737c02", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "732f2d972e42c116a70802f9898c51b54916e542cc50968ac6980512ec90f42b"},
"ex_machina": {:hex, :ex_machina, "2.8.0", "a0e847b5712065055ec3255840e2c78ef9366634d62390839d4880483be38abe", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}], "hexpm", "79fe1a9c64c0c1c1fab6c4fa5d871682cb90de5885320c187d117004627a7729"},
"ex_syslogger": {:hex, :ex_syslogger, "1.5.2", "72b6aa2d47a236e999171f2e1ec18698740f40af0bd02c8c650bf5f1fd1bac79", [:mix], [{:poison, ">= 1.5.0", [hex: :poison, repo: "hexpm", optional: true]}, {:syslog, "~> 1.1.0", [hex: :syslog, repo: "hexpm", optional: false]}], "hexpm", "ab9fab4136dbc62651ec6f16fa4842f10cf02ab4433fa3d0976c01be99398399"},
+ "exif_stripper": {:hex, :exif_stripper, "0.1.3", "b3d89a623bb49c09fbe45fe5d4f1becee0ffe011bcade174c3bb91cdae59745e", [:mix], [], "hexpm", "e1ab26d766205ae54be57c19ec70d408b020f3c31855b49d53a55460de81c6e1"},
"exile": {:hex, :exile, "0.10.0", "b69e2d27a9af670b0f0a0898addca0eda78f6f5ba95ccfbc9bc6ccdd04925436", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "c62ee8fee565b5ac4a898d0dcd58d2b04fb5eec1655af1ddcc9eb582c6732c33"},
"expo": {:hex, :expo, "0.5.1", "249e826a897cac48f591deba863b26c16682b43711dd15ee86b92f25eafd96d9", [:mix], [], "hexpm", "68a4233b0658a3d12ee00d27d37d856b1ba48607e7ce20fd376958d0ba6ce92b"},
"fast_html": {:hex, :fast_html, "2.5.0", "23578c1c1fa03db6a3786d78218b2d944df34b0fc3729e72de912f390913c80f", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}], "hexpm", "69eb46ed98a5d9cca1ccd4a5ac94ce5dd626fc29513fbaa0a16cd8b2da67ae3e"},
diff --git a/priv/repo/migrations/20261027000000_upload_filter_exiftool_to_exif.exs b/priv/repo/migrations/20261027000000_upload_filter_exiftool_to_exif.exs
new file mode 100644
index 000000000..4e737e859
--- /dev/null
+++ b/priv/repo/migrations/20261027000000_upload_filter_exiftool_to_exif.exs
@@ -0,0 +1,41 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2026 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Repo.Migrations.UploadFilterExiftoolToExif do
+ use Ecto.Migration
+
+ alias Pleroma.ConfigDB
+
+ @up %{
+ Pleroma.Upload.Filter.Exiftool => Pleroma.Upload.Filter.Exif.StripLocation,
+ Pleroma.Upload.Filter.Exiftool.StripLocation => Pleroma.Upload.Filter.Exif.StripLocation,
+ Pleroma.Upload.Filter.Exiftool.ReadDescription => Pleroma.Upload.Filter.Exif.ReadDescription
+ }
+
+ @down %{
+ Pleroma.Upload.Filter.Exif.StripLocation => Pleroma.Upload.Filter.Exiftool.StripLocation,
+ Pleroma.Upload.Filter.Exif.ReadDescription => Pleroma.Upload.Filter.Exiftool.ReadDescription
+ }
+
+ def up, do: rename_filters(@up)
+
+ def down, do: rename_filters(@down)
+
+ # Only touch configs that list filters: adding an empty list would hide the
+ # filters from the config file.
+ defp rename_filters(renames) do
+ with %{value: value} <- ConfigDB.get_by_params(%{group: :pleroma, key: Pleroma.Upload}),
+ filters when is_list(filters) <- value[:filters],
+ new_filters when new_filters != filters <-
+ filters |> Enum.map(&Map.get(renames, &1, &1)) |> Enum.uniq() do
+ ConfigDB.update_or_create(%{
+ group: :pleroma,
+ key: Pleroma.Upload,
+ value: Keyword.put(value, :filters, new_filters)
+ })
+ else
+ _ -> nil
+ end
+ end
+end
diff --git a/test/fixtures/dji_gps.jpg b/test/fixtures/dji_gps.jpg
new file mode 100644
index 000000000..7249228cf
Binary files /dev/null and b/test/fixtures/dji_gps.jpg differ
diff --git a/test/fixtures/strip_location_gps.heic b/test/fixtures/strip_location_gps.heic
new file mode 100644
index 000000000..f27961d83
Binary files /dev/null and b/test/fixtures/strip_location_gps.heic differ
diff --git a/test/fixtures/strip_location_gps.tiff b/test/fixtures/strip_location_gps.tiff
new file mode 100644
index 000000000..afbc4ac58
Binary files /dev/null and b/test/fixtures/strip_location_gps.tiff differ
diff --git a/test/fixtures/strip_location_gps.webp b/test/fixtures/strip_location_gps.webp
new file mode 100644
index 000000000..9c7724529
Binary files /dev/null and b/test/fixtures/strip_location_gps.webp differ
diff --git a/test/fixtures/strip_location_gps_grid.heic b/test/fixtures/strip_location_gps_grid.heic
new file mode 100644
index 000000000..3a9d5bf97
Binary files /dev/null and b/test/fixtures/strip_location_gps_grid.heic differ
diff --git a/test/fixtures/xmp_gps.jpg b/test/fixtures/xmp_gps.jpg
new file mode 100644
index 000000000..dd76e84f7
Binary files /dev/null and b/test/fixtures/xmp_gps.jpg differ
diff --git a/test/mix/tasks/pleroma/instance_test.exs b/test/mix/tasks/pleroma/instance_test.exs
index 5ecb6e445..0a8c3d107 100644
--- a/test/mix/tasks/pleroma/instance_test.exs
+++ b/test/mix/tasks/pleroma/instance_test.exs
@@ -95,7 +95,7 @@ defmodule Mix.Tasks.Pleroma.InstanceTest do
assert generated_config =~ "http: [ip: {127, 0, 0, 1}, port: 4000]"
assert generated_config =~
- "filters: [Pleroma.Upload.Filter.Exiftool.StripLocation, Pleroma.Upload.Filter.Exiftool.ReadDescription]"
+ "filters: [Pleroma.Upload.Filter.Exif.StripLocation, Pleroma.Upload.Filter.Exif.ReadDescription]"
assert File.read!(tmp_path() <> "setup.psql") == generated_setup_psql()
assert File.exists?(Path.expand("./test/instance/static/robots.txt"))
diff --git a/test/pleroma/config/deprecation_warnings_test.exs b/test/pleroma/config/deprecation_warnings_test.exs
index 62f5bdeea..65b6a3d98 100644
--- a/test/pleroma/config/deprecation_warnings_test.exs
+++ b/test/pleroma/config/deprecation_warnings_test.exs
@@ -15,38 +15,37 @@ defmodule Pleroma.Config.DeprecationWarningsTest do
test "gives warning when still used" do
clear_config(
[Pleroma.Upload, :filters],
- [Pleroma.Upload.Filter.Exiftool]
+ [Pleroma.Upload.Filter.Exiftool, Pleroma.Upload.Filter.Exiftool.ReadDescription]
)
- assert capture_log(fn -> DeprecationWarnings.check_exiftool_filter() end) =~
- """
- !!!DEPRECATION WARNING!!!
- Your config is using Exiftool as a filter instead of Exiftool.StripLocation. This should work for now, but you are advised to change to the new configuration to prevent possible issues later:
+ log = capture_log(fn -> DeprecationWarnings.check_exiftool_filter() end)
- ```
- config :pleroma, Pleroma.Upload,
- filters: [Pleroma.Upload.Filter.Exiftool]
- ```
+ assert log =~ "!!!DEPRECATION WARNING!!!"
- Is now
+ assert log =~
+ "* `Pleroma.Upload.Filter.Exiftool` is now `Pleroma.Upload.Filter.Exif.StripLocation`"
+ assert log =~
+ "* `Pleroma.Upload.Filter.Exiftool.ReadDescription` is now `Pleroma.Upload.Filter.Exif.ReadDescription`"
- ```
- config :pleroma, Pleroma.Upload,
- filters: [Pleroma.Upload.Filter.Exiftool.StripLocation]
- ```
- """
+ refute log =~ "Exiftool.StripLocation`"
end
- test "changes setting to exiftool strip location" do
+ test "changes setting to the Exif filters" do
clear_config(
[Pleroma.Upload, :filters],
- [Pleroma.Upload.Filter.Exiftool, Pleroma.Upload.Filter.Exiftool.ReadDescription]
+ [
+ Pleroma.Upload.Filter.Dedupe,
+ Pleroma.Upload.Filter.Exiftool,
+ Pleroma.Upload.Filter.Exiftool.StripLocation,
+ Pleroma.Upload.Filter.Exiftool.ReadDescription
+ ]
)
expected_config = [
- Pleroma.Upload.Filter.Exiftool.StripLocation,
- Pleroma.Upload.Filter.Exiftool.ReadDescription
+ Pleroma.Upload.Filter.Dedupe,
+ Pleroma.Upload.Filter.Exif.StripLocation,
+ Pleroma.Upload.Filter.Exif.ReadDescription
]
capture_log(fn -> DeprecationWarnings.warn() end)
@@ -58,8 +57,8 @@ defmodule Pleroma.Config.DeprecationWarningsTest do
clear_config(
[Pleroma.Upload, :filters],
[
- Pleroma.Upload.Filter.Exiftool.StripLocation,
- Pleroma.Upload.Filter.Exiftool.ReadDescription
+ Pleroma.Upload.Filter.Exif.StripLocation,
+ Pleroma.Upload.Filter.Exif.ReadDescription
]
)
diff --git a/test/pleroma/repo/migrations/upload_filter_exiftool_to_exif_test.exs b/test/pleroma/repo/migrations/upload_filter_exiftool_to_exif_test.exs
new file mode 100644
index 000000000..0fdf26961
--- /dev/null
+++ b/test/pleroma/repo/migrations/upload_filter_exiftool_to_exif_test.exs
@@ -0,0 +1,73 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2026 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Repo.Migrations.UploadFilterExiftoolToExifTest do
+ use Pleroma.DataCase
+ import Pleroma.Factory
+ import Pleroma.Tests.Helpers
+ alias Pleroma.ConfigDB
+
+ setup_all do: require_migration("20261027000000_upload_filter_exiftool_to_exif")
+
+ defp filters do
+ ConfigDB.get_by_params(%{group: :pleroma, key: Pleroma.Upload}).value[:filters]
+ end
+
+ test "up/0 renames the Exiftool filters", %{migration: migration} do
+ insert(:config,
+ group: :pleroma,
+ key: Pleroma.Upload,
+ value: [
+ filters: [
+ Pleroma.Upload.Filter.Dedupe,
+ Pleroma.Upload.Filter.Exiftool,
+ Pleroma.Upload.Filter.Exiftool.StripLocation,
+ Pleroma.Upload.Filter.Exiftool.ReadDescription
+ ]
+ ]
+ )
+
+ migration.up()
+
+ assert filters() == [
+ Pleroma.Upload.Filter.Dedupe,
+ Pleroma.Upload.Filter.Exif.StripLocation,
+ Pleroma.Upload.Filter.Exif.ReadDescription
+ ]
+ end
+
+ test "down/0 restores the Exiftool filters", %{migration: migration} do
+ insert(:config,
+ group: :pleroma,
+ key: Pleroma.Upload,
+ value: [
+ filters: [
+ Pleroma.Upload.Filter.Exif.StripLocation,
+ Pleroma.Upload.Filter.Exif.ReadDescription
+ ]
+ ]
+ )
+
+ migration.down()
+
+ assert filters() == [
+ Pleroma.Upload.Filter.Exiftool.StripLocation,
+ Pleroma.Upload.Filter.Exiftool.ReadDescription
+ ]
+ end
+
+ test "up/0 does nothing without a database config", %{migration: migration} do
+ assert migration.up() == nil
+ assert ConfigDB.get_by_params(%{group: :pleroma, key: Pleroma.Upload}) == nil
+ end
+
+ test "up/0 does not add filters to a database config without them", %{migration: migration} do
+ insert(:config, group: :pleroma, key: Pleroma.Upload, value: [uploader: Pleroma.Uploaders.S3])
+
+ migration.up()
+
+ value = ConfigDB.get_by_params(%{group: :pleroma, key: Pleroma.Upload}).value
+ assert value == [uploader: Pleroma.Uploaders.S3]
+ end
+end
diff --git a/test/pleroma/upload/filter/exif/read_description_test.exs b/test/pleroma/upload/filter/exif/read_description_test.exs
new file mode 100644
index 000000000..07e3ce710
--- /dev/null
+++ b/test/pleroma/upload/filter/exif/read_description_test.exs
@@ -0,0 +1,85 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2026 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Upload.Filter.Exif.ReadDescriptionTest do
+ use Pleroma.DataCase, async: true
+
+ alias Pleroma.Upload.Filter.Exif.ReadDescription
+
+ defp upload(fixture, attrs \\ []) do
+ path = Path.absname(Path.join("test/fixtures", fixture))
+
+ struct!(
+ %Pleroma.Upload{
+ name: fixture,
+ content_type: "image/" <> String.trim_leading(Path.extname(fixture), "."),
+ path: path,
+ tempfile: path,
+ description: nil
+ },
+ attrs
+ )
+ end
+
+ test "keeps description when not empty" do
+ upload =
+ upload("image_with_imagedescription_and_caption-abstract.jpg",
+ description: "Some description"
+ )
+
+ assert ReadDescription.filter(upload) == {:ok, :noop}
+ end
+
+ test "otherwise returns ImageDescription when present" do
+ upload = upload("image_with_imagedescription_and_caption-abstract.jpg")
+
+ assert ReadDescription.filter(upload) ==
+ {:ok, :filtered, %{upload | description: "a descriptive white pixel"}}
+ end
+
+ test "ignores stray data after the image" do
+ upload = upload("image_with_imagedescription_and_caption-abstract_and_stray_data_after.png")
+
+ assert {:ok, :filtered, %{description: "a descriptive white pixel"}} =
+ ReadDescription.filter(upload)
+
+ assert ReadDescription.filter(upload("image_with_stray_data_after.png")) == {:ok, :noop}
+ end
+
+ test "otherwise returns iptc:Caption-Abstract when present" do
+ upload = upload("image_with_caption-abstract.jpg")
+
+ assert ReadDescription.filter(upload) ==
+ {:ok, :filtered, %{upload | description: "an abstract white pixel"}}
+ end
+
+ test "reads ImageDescription from WebP and HEIC" do
+ assert {:ok, :filtered, %{description: "a webp with a location"}} =
+ ReadDescription.filter(upload("strip_location_gps.webp"))
+
+ assert {:ok, :filtered, %{description: "a heic with a location"}} =
+ ReadDescription.filter(upload("strip_location_gps.heic"))
+ end
+
+ test "does nothing when there is no description" do
+ assert ReadDescription.filter(upload("image_with_no_description.jpg")) == {:ok, :noop}
+ end
+
+ test "ignores descriptions that exceed the maximum length" do
+ clear_config([:instance, :description_limit], 5)
+
+ upload = upload("image_with_imagedescription_and_caption-abstract.jpg")
+ assert ReadDescription.filter(upload) == {:ok, :noop}
+ end
+
+ test "ignores content with only whitespace" do
+ upload = upload("image_with_imagedescription_and_caption-abstract_whitespaces.jpg")
+ assert ReadDescription.filter(upload) == {:ok, :noop}
+ end
+
+ test "does nothing when the file can't be read" do
+ upload = %{upload("image.jpg") | tempfile: Path.absname("test/fixtures/non-existant_tmp.jpg")}
+ assert ReadDescription.filter(upload) == {:ok, :noop}
+ end
+end
diff --git a/test/pleroma/upload/filter/exif/strip_location_test.exs b/test/pleroma/upload/filter/exif/strip_location_test.exs
new file mode 100644
index 000000000..f1bb3b87c
--- /dev/null
+++ b/test/pleroma/upload/filter/exif/strip_location_test.exs
@@ -0,0 +1,72 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2026 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Upload.Filter.Exif.StripLocationTest do
+ use Pleroma.DataCase, async: true
+
+ import Pleroma.Tests.ExifHelpers
+
+ alias Pleroma.Upload.Filter.Exif.StripLocation
+
+ for {fixture, content_type} <- [
+ {"DSCN0010.jpg", "image/jpeg"},
+ {"DSCN0010.png", "image/png"},
+ {"strip_location_gps.webp", "image/webp"},
+ {"strip_location_gps.heic", "image/heic"},
+ {"strip_location_gps_grid.heic", "image/heic"},
+ {"strip_location_gps.tiff", "image/tiff"}
+ ] do
+ test "strips GPS data from #{fixture} (#{content_type})" do
+ upload = upload_copy(unquote(fixture), unquote(content_type))
+ assert gps_tags(upload.tempfile) =~ "GPS Latitude"
+
+ assert StripLocation.filter(upload) == {:ok, :filtered}
+ assert gps_tags(upload.tempfile) == ""
+ end
+ end
+
+ test "strips GPS data from XMP, including DJI drone positions" do
+ for fixture <- ["xmp_gps.jpg", "dji_gps.jpg"] do
+ upload = upload_copy(fixture, "image/jpeg")
+ assert exiftool_xmp(upload.tempfile) =~ "GPSLatitude"
+
+ assert StripLocation.filter(upload) == {:ok, :filtered}
+ refute exiftool_xmp(upload.tempfile) =~ ~r/GPS|AbsoluteAltitude/
+ end
+ end
+
+ defp exiftool_xmp(path) do
+ {output, 0} = System.cmd("exiftool", ["-m", "-s", "-xmp:all", path])
+ output
+ end
+
+ test "keeps other EXIF data in HEIC" do
+ upload = upload_copy("strip_location_gps.heic", "image/heic")
+
+ assert StripLocation.filter(upload) == {:ok, :filtered}
+ assert tag(upload.tempfile, "ImageDescription") == "a heic with a location"
+ assert tag(upload.tempfile, "ImageSize") == "64x48"
+ end
+
+ test "does nothing when there is no GPS data" do
+ upload = upload_copy("image.jpg", "image/jpeg")
+ original = File.read!(upload.tempfile)
+
+ assert StripLocation.filter(upload) == {:ok, :noop}
+ assert File.read!(upload.tempfile) == original
+ end
+
+ test "accepts PNG files with data after IEND" do
+ upload = upload_copy("image_with_stray_data_after.png", "image/png")
+
+ assert {:ok, _} = StripLocation.filter(upload)
+ end
+
+ test "ignores non-image uploads" do
+ upload = upload_copy("DSCN0010.jpg", "application/octet-stream")
+
+ assert StripLocation.filter(upload) == {:ok, :noop}
+ assert gps_tags(upload.tempfile) =~ "GPS Latitude"
+ end
+end
diff --git a/test/pleroma/upload/filter/exiftool/read_description_test.exs b/test/pleroma/upload/filter/exiftool/read_description_test.exs
deleted file mode 100644
index 9a1bd61d7..000000000
--- a/test/pleroma/upload/filter/exiftool/read_description_test.exs
+++ /dev/null
@@ -1,144 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Upload.Filter.Exiftool.ReadDescriptionTest do
- use Pleroma.DataCase
- alias Pleroma.Upload.Filter
-
- @uploads %Pleroma.Upload{
- name: "image_with_imagedescription_and_caption-abstract.jpg",
- content_type: "image/jpeg",
- path: Path.absname("test/fixtures/image_with_imagedescription_and_caption-abstract.jpg"),
- tempfile: Path.absname("test/fixtures/image_with_imagedescription_and_caption-abstract.jpg"),
- description: nil
- }
-
- test "keeps description when not empty" do
- uploads = %Pleroma.Upload{
- name: "image_with_imagedescription_and_caption-abstract.jpg",
- content_type: "image/jpeg",
- path: Path.absname("test/fixtures/image_with_imagedescription_and_caption-abstract.jpg"),
- tempfile:
- Path.absname("test/fixtures/image_with_imagedescription_and_caption-abstract.jpg"),
- description: "Some description"
- }
-
- assert Filter.Exiftool.ReadDescription.filter(uploads) ==
- {:ok, :noop}
- end
-
- test "otherwise returns ImageDescription when present" do
- uploads_after = %Pleroma.Upload{
- name: "image_with_imagedescription_and_caption-abstract.jpg",
- content_type: "image/jpeg",
- path: Path.absname("test/fixtures/image_with_imagedescription_and_caption-abstract.jpg"),
- tempfile:
- Path.absname("test/fixtures/image_with_imagedescription_and_caption-abstract.jpg"),
- description: "a descriptive white pixel"
- }
-
- assert Filter.Exiftool.ReadDescription.filter(@uploads) ==
- {:ok, :filtered, uploads_after}
- end
-
- test "Ignores warnings" do
- uploads = %Pleroma.Upload{
- name: "image_with_imagedescription_and_caption-abstract_and_stray_data_after.png",
- content_type: "image/png",
- path:
- Path.absname(
- "test/fixtures/image_with_imagedescription_and_caption-abstract_and_stray_data_after.png"
- ),
- tempfile:
- Path.absname(
- "test/fixtures/image_with_imagedescription_and_caption-abstract_and_stray_data_after.png"
- )
- }
-
- assert {:ok, :filtered, %{description: "a descriptive white pixel"}} =
- Filter.Exiftool.ReadDescription.filter(uploads)
-
- uploads = %Pleroma.Upload{
- name: "image_with_stray_data_after.png",
- content_type: "image/png",
- path: Path.absname("test/fixtures/image_with_stray_data_after.png"),
- tempfile: Path.absname("test/fixtures/image_with_stray_data_after.png")
- }
-
- assert {:ok, :filtered, %{description: nil}} = Filter.Exiftool.ReadDescription.filter(uploads)
- end
-
- test "otherwise returns iptc:Caption-Abstract when present" do
- upload = %Pleroma.Upload{
- name: "image_with_caption-abstract.jpg",
- content_type: "image/jpeg",
- path: Path.absname("test/fixtures/image_with_caption-abstract.jpg"),
- tempfile: Path.absname("test/fixtures/image_with_caption-abstract.jpg"),
- description: nil
- }
-
- upload_after = %Pleroma.Upload{
- name: "image_with_caption-abstract.jpg",
- content_type: "image/jpeg",
- path: Path.absname("test/fixtures/image_with_caption-abstract.jpg"),
- tempfile: Path.absname("test/fixtures/image_with_caption-abstract.jpg"),
- description: "an abstract white pixel"
- }
-
- assert Filter.Exiftool.ReadDescription.filter(upload) ==
- {:ok, :filtered, upload_after}
- end
-
- test "otherwise returns nil" do
- uploads = %Pleroma.Upload{
- name: "image_with_no_description.jpg",
- content_type: "image/jpeg",
- path: Path.absname("test/fixtures/image_with_no_description.jpg"),
- tempfile: Path.absname("test/fixtures/image_with_no_description.jpg"),
- description: nil
- }
-
- assert Filter.Exiftool.ReadDescription.filter(uploads) ==
- {:ok, :filtered, uploads}
- end
-
- test "Return nil when image description from EXIF data exceeds the maximum length" do
- clear_config([:instance, :description_limit], 5)
-
- assert Filter.Exiftool.ReadDescription.filter(@uploads) ==
- {:ok, :filtered, @uploads}
- end
-
- test "Ignores content with only whitespace" do
- uploads = %Pleroma.Upload{
- name: "non-existant.jpg",
- content_type: "image/jpeg",
- path:
- Path.absname(
- "test/fixtures/image_with_imagedescription_and_caption-abstract_whitespaces.jpg"
- ),
- tempfile:
- Path.absname(
- "test/fixtures/image_with_imagedescription_and_caption-abstract_whitespaces.jpg"
- ),
- description: nil
- }
-
- assert Filter.Exiftool.ReadDescription.filter(uploads) ==
- {:ok, :filtered, uploads}
- end
-
- test "Return nil when image description from EXIF data can't be read" do
- uploads = %Pleroma.Upload{
- name: "non-existant.jpg",
- content_type: "image/jpeg",
- path: Path.absname("test/fixtures/non-existant.jpg"),
- tempfile: Path.absname("test/fixtures/non-existant_tmp.jpg"),
- description: nil
- }
-
- assert Filter.Exiftool.ReadDescription.filter(uploads) ==
- {:ok, :filtered, uploads}
- end
-end
diff --git a/test/pleroma/upload/filter/exiftool/strip_location_test.exs b/test/pleroma/upload/filter/exiftool/strip_location_test.exs
deleted file mode 100644
index 485060215..000000000
--- a/test/pleroma/upload/filter/exiftool/strip_location_test.exs
+++ /dev/null
@@ -1,51 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Upload.Filter.Exiftool.StripLocationTest do
- use Pleroma.DataCase, async: true
- alias Pleroma.Upload.Filter
-
- test "apply exiftool filter" do
- assert Pleroma.Utils.command_available?("exiftool")
-
- ~w{jpg png}
- |> Enum.map(fn type ->
- File.cp!(
- "test/fixtures/DSCN0010.#{type}",
- "test/fixtures/DSCN0010_tmp.#{type}"
- )
-
- upload = %Pleroma.Upload{
- name: "image_with_GPS_data.#{type}",
- content_type: "image/jpeg",
- path: Path.absname("test/fixtures/DSCN0010.#{type}"),
- tempfile: Path.absname("test/fixtures/DSCN0010_tmp.#{type}")
- }
-
- assert Filter.Exiftool.StripLocation.filter(upload) == {:ok, :filtered}
-
- {exif_original, 0} = System.cmd("exiftool", ["-m", "test/fixtures/DSCN0010.#{type}"])
- {exif_filtered, 0} = System.cmd("exiftool", ["-m", "test/fixtures/DSCN0010_tmp.#{type}"])
-
- assert String.match?(exif_original, ~r/GPS/)
- refute String.match?(exif_filtered, ~r/GPS/)
- end)
- end
-
- test "verify webp, heic, svg files are skipped" do
- uploads =
- ~w{webp heic svg svg+xml}
- |> Enum.map(fn type ->
- %Pleroma.Upload{
- name: "sample.#{type}",
- content_type: "image/#{type}"
- }
- end)
-
- uploads
- |> Enum.each(fn upload ->
- assert Filter.Exiftool.StripLocation.filter(upload) == {:ok, :noop}
- end)
- end
-end
diff --git a/test/pleroma/upload/filter/exiftool_test.exs b/test/pleroma/upload/filter/exiftool_test.exs
new file mode 100644
index 000000000..c3ee3c59c
--- /dev/null
+++ b/test/pleroma/upload/filter/exiftool_test.exs
@@ -0,0 +1,31 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2026 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Upload.Filter.ExiftoolTest do
+ # Instances may still configure the deprecated Exiftool filter names.
+ # They must keep working, including for formats exiftool used to skip.
+ use Pleroma.DataCase, async: true
+
+ import Pleroma.Tests.ExifHelpers
+
+ for filter <- [Pleroma.Upload.Filter.Exiftool, Pleroma.Upload.Filter.Exiftool.StripLocation] do
+ test "#{inspect(filter)} strips GPS data from HEIC uploads" do
+ upload = upload_copy("strip_location_gps.heic", "image/heic")
+ assert gps_tags(upload.tempfile) =~ "GPS Latitude"
+
+ assert Pleroma.Upload.Filter.filter([unquote(filter)], upload) == {:ok, upload}
+ assert gps_tags(upload.tempfile) == ""
+ end
+ end
+
+ test "Exiftool.ReadDescription reads the description" do
+ upload = upload_copy("strip_location_gps.heic", "image/heic", description: nil)
+
+ assert {:ok, %{description: "a heic with a location"}} =
+ Pleroma.Upload.Filter.filter(
+ [Pleroma.Upload.Filter.Exiftool.ReadDescription],
+ upload
+ )
+ end
+end
diff --git a/test/support/exif_helpers.ex b/test/support/exif_helpers.ex
new file mode 100644
index 000000000..3a8a3b84e
--- /dev/null
+++ b/test/support/exif_helpers.ex
@@ -0,0 +1,44 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2026 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Tests.ExifHelpers do
+ @moduledoc """
+ Helpers for upload filter tests that use exiftool as an independent oracle.
+ """
+
+ @doc "Copies a fixture to a unique temporary file and returns an upload for it."
+ def upload_copy(fixture, content_type, attrs \\ []) do
+ source = Path.absname(Path.join("test/fixtures", fixture))
+
+ tempfile =
+ Path.join(
+ System.tmp_dir!(),
+ "pleroma_exif_#{System.unique_integer([:positive])}#{Path.extname(fixture)}"
+ )
+
+ File.cp!(source, tempfile)
+ ExUnit.Callbacks.on_exit(fn -> File.rm(tempfile) end)
+
+ struct!(
+ %Pleroma.Upload{
+ name: fixture,
+ content_type: content_type,
+ path: source,
+ tempfile: tempfile
+ },
+ attrs
+ )
+ end
+
+ @doc "Returns the GPS tags exiftool finds in a file, as text."
+ def gps_tags(path), do: exiftool!(["-gps:all", path])
+
+ @doc "Returns the value of one tag as exiftool reads it."
+ def tag(path, tag), do: exiftool!(["-s3", "-#{tag}", path])
+
+ defp exiftool!(args) do
+ {output, 0} = System.cmd("exiftool", ["-m" | args])
+ String.trim(output)
+ end
+end

File Metadata

Mime Type
text/x-diff
Expires
Sun, Nov 1, 11:49 PM (13 h, 26 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1803427
Default Alt Text
(53 KB)

Event Timeline