exiftool: make stripped tags configurable
ci/woodpecker/pr/build-amd64 Pipeline is pending Details
ci/woodpecker/pr/build-arm64 Pipeline is pending Details
ci/woodpecker/pr/docs Pipeline is pending Details
ci/woodpecker/pr/lint Pipeline is pending Details
ci/woodpecker/pr/test Pipeline is pending Details

This commit is contained in:
Oneric 2024-04-18 18:05:29 +00:00
parent 5c665abba0
commit f01ace481a
5 changed files with 143 additions and 21 deletions

View File

@ -12,7 +12,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## Changed
- Uploadfilter `Pleroma.Upload.Filter.Exiftool` was replaced by `Pleroma.Upload.Filter.Exiftool.StripMetadata`;
the latter strips all non-essential metadata
the latter strips all non-essential metadata by default but can be configured.
To regains the old behaviour of only stripping gps data set `purge: ["gps:all"]`.
## Fixed
- Issue preventing fetching anything from IPv6-only instances

View File

@ -222,6 +222,26 @@ config :pleroma, :config_description, [
}
]
},
%{
group: :pleroma,
key: Pleroma.Upload.Filter.Exiftool.StripMetadata,
type: :group,
description: "Strip specified metadata from image uploads",
children: [
%{
key: :purge,
description: "Metadata fields or groups to strip",
type: {:list, :string},
suggestions: ["all", "CommonIFD0"]
},
%{
key: :preserve,
description: "Metadata fields or groups to preserve (takes precedence over stripping)",
type: {:list, :string},
suggestions: ["ColorSpaces", "Orientation"]
}
]
},
%{
group: :pleroma,
key: Pleroma.Emails.Mailer,

View File

@ -658,6 +658,8 @@ This filter replaces the declared filename (not the path) of an upload.
This filter strips metadata with Exiftool leaving color profiles and orientation intact.
* `purge`: List of Exiftool tag names or tag group names to purge
* `preserve`: List of Exiftool tag names or tag group names to preserve even if they occur in the purge list
No specific configuration.
#### Pleroma.Upload.Filter.OnlyMedia

View File

@ -9,6 +9,11 @@ defmodule Pleroma.Upload.Filter.Exiftool.StripMetadata do
"""
@behaviour Pleroma.Upload.Filter
alias Pleroma.Config
@purge_default ["all", "CommonIFD0"]
@preserve_default ["ColorSpaceTags", "Orientation"]
@spec filter(Pleroma.Upload.t()) :: {:ok, :noop} | {:ok, :filtered} | {:error, String.t()}
# Formats not compatible with exiftool at this time
@ -16,22 +21,23 @@ defmodule Pleroma.Upload.Filter.Exiftool.StripMetadata do
def filter(%Pleroma.Upload{content_type: "image/svg+xml"}), do: {:ok, :noop}
def filter(%Pleroma.Upload{tempfile: file, content_type: "image" <> _}) do
purge_args =
Config.get([__MODULE__, :purge], @purge_default)
|> Enum.map(fn mgroup -> "-" <> mgroup <> "=" end)
preserve_args =
Config.get([__MODULE__, :preserve], @preserve_default)
|> Enum.map(fn mgroup -> "-" <> mgroup end)
|> then(fn
# If -TagsFromFile is not followed by tag selectors, it will copy most available tags
[] -> []
args -> ["-TagsFromFile", "@" | args]
end)
args = ["-ignoreMinorErrors", "-overwrite_original" | purge_args] ++ preserve_args ++ [file]
try do
case System.cmd(
"exiftool",
[
"-ignoreMinorErrors",
"-overwrite_original",
"-all=",
"-CommonIFD0=",
"-TagsFromFile",
"@",
"-ColorSpaceTags",
"-Orientation",
file
],
parallelism: true
) do
case System.cmd("exiftool", args, parallelism: true) do
{_response, 0} -> {:ok, :filtered}
{error, 1} -> {:error, error}
end

View File

@ -6,29 +6,100 @@ defmodule Pleroma.Upload.Filter.Exiftool.StripMetadataTest do
use Pleroma.DataCase
alias Pleroma.Upload.Filter
test "apply exiftool filter" do
test "exiftool strip metadata strips GPS etc but preserves Orientation and ColorSpace by default" do
assert Pleroma.Utils.command_available?("exiftool")
tmpfile = "test/fixtures/DSCN0010_#{__MODULE__}_01.jpg"
File.cp!(
"test/fixtures/DSCN0010.jpg",
"test/fixtures/DSCN0010_tmp.jpg"
tmpfile
)
upload = %Pleroma.Upload{
name: "image_with_GPS_data.jpg",
content_type: "image/jpeg",
path: Path.absname("test/fixtures/DSCN0010.jpg"),
tempfile: Path.absname("test/fixtures/DSCN0010_tmp.jpg")
tempfile: Path.absname(tmpfile)
}
assert Filter.Exiftool.StripMetadata.filter(upload) == {:ok, :filtered}
{exif_original, 0} = System.cmd("exiftool", ["test/fixtures/DSCN0010.jpg"])
{exif_filtered, 0} = System.cmd("exiftool", ["test/fixtures/DSCN0010_tmp.jpg"])
exif_original = read_exif("test/fixtures/DSCN0010.jpg")
exif_filtered = read_exif(tmpfile)
refute exif_original == exif_filtered
assert String.match?(exif_original, ~r/GPS/)
refute String.match?(exif_filtered, ~r/GPS/)
assert String.match?(exif_original, ~r/Camera Model Name/)
refute String.match?(exif_filtered, ~r/Camera Model Name/)
assert String.match?(exif_original, ~r/Orientation/)
assert String.match?(exif_filtered, ~r/Orientation/)
assert String.match?(exif_original, ~r/Color Space/)
assert String.match?(exif_filtered, ~r/Color Space/)
end
# this is a nonsensical configuration, but it shouldn't explode
test "exiftool strip metadata is a noop with empty purge list" do
assert Pleroma.Utils.command_available?("exiftool")
clear_config([Pleroma.Upload.Filter.Exiftool.StripMetadata, :purge], [])
tmpfile = "test/fixtures/DSCN0010_#{__MODULE__}_02.jpg"
File.cp!(
"test/fixtures/DSCN0010.jpg",
tmpfile
)
upload = %Pleroma.Upload{
name: "image_with_GPS_data.jpg",
content_type: "image/jpeg",
path: Path.absname("test/fixtures/DSCN0010.jpg"),
tempfile: Path.absname(tmpfile)
}
assert Filter.Exiftool.StripMetadata.filter(upload) == {:ok, :filtered}
exif_original = read_exif("test/fixtures/DSCN0010.jpg")
exif_filtered = read_exif(tmpfile)
assert exif_original == exif_filtered
end
test "exiftool strip metadata works with empty preserve list" do
assert Pleroma.Utils.command_available?("exiftool")
clear_config([Pleroma.Upload.Filter.Exiftool.StripMetadata, :preserve], [])
tmpfile = "test/fixtures/DSCN0010_#{__MODULE__}_03.jpg"
File.cp!(
"test/fixtures/DSCN0010.jpg",
tmpfile
)
upload = %Pleroma.Upload{
name: "image_with_GPS_data.jpg",
content_type: "image/jpeg",
path: Path.absname("test/fixtures/DSCN0010.jpg"),
tempfile: Path.absname(tmpfile)
}
write_exif(["-ImageDescription=Trees and Houses", "-Orientation=1", tmpfile])
exif_extended = read_exif(tmpfile)
assert String.match?(exif_extended, ~r/Image Description[ \t]*:[ \t]*Trees and Houses/)
assert String.match?(exif_extended, ~r/Orientation/)
assert Filter.Exiftool.StripMetadata.filter(upload) == {:ok, :filtered}
exif_original = read_exif("test/fixtures/DSCN0010.jpg")
exif_filtered = read_exif(tmpfile)
refute exif_original == exif_filtered
refute exif_extended == exif_filtered
assert String.match?(exif_original, ~r/GPS/)
refute String.match?(exif_filtered, ~r/GPS/)
refute String.match?(exif_filtered, ~r/Image Description/)
refute String.match?(exif_filtered, ~r/Orientation/)
end
test "verify webp files are skipped" do
@ -39,4 +110,26 @@ defmodule Pleroma.Upload.Filter.Exiftool.StripMetadataTest do
assert Filter.Exiftool.StripMetadata.filter(upload) == {:ok, :noop}
end
defp read_exif(file) do
# time and file path tags cause mismatches even for byte-identical files
{exif_data, 0} =
System.cmd("exiftool", [
"-x",
"Time:All",
"-x",
"Directory",
"-x",
"FileName",
"-x",
"FileSize",
file
])
exif_data
end
defp write_exif(args) do
{_response, 0} = System.cmd("exiftool", ["-ignoreMinorErrors", "-overwrite_original" | args])
end
end