Commit abc39f61 for libheif

commit abc39f6121805dd7eeee03cf90d2ea911775aedf
Author: Dirk Farin <dirk.farin@gmail.com>
Date:   Sun Oct 25 17:04:40 2026 +0100

    Avoid re-adding color profiles while reading HEIF files [GHSA-hhg3-fxg7-3gxw]

diff --git a/libheif/context.cc b/libheif/context.cc
index f3cad89f..623634ed 100644
--- a/libheif/context.cc
+++ b/libheif/context.cc
@@ -786,8 +786,16 @@ Error HeifContext::interpret_heif_file_images()
     for (const auto& prop : properties) {
       auto colr = std::dynamic_pointer_cast<Box_colr>(prop);
       if (colr) {
+        // The ImageItem color-profile setters also add 'colr' properties for
+        // encoding. On read, only populate the description: this property is
+        // already associated with the item in the file.
         auto profile = colr->get_color_profile();
-        image->set_color_profile(profile);
+        if (auto icc = std::dynamic_pointer_cast<const color_profile_raw>(profile)) {
+          image->ImageDescription::set_color_profile_icc(icc);
+        }
+        else if (auto nclx = std::dynamic_pointer_cast<const color_profile_nclx>(profile)) {
+          image->ImageDescription::set_color_profile_nclx(nclx->get_nclx_color_profile());
+        }
         continue;
       }

@@ -1109,11 +1117,11 @@ Error HeifContext::interpret_heif_file_images()

       auto tile_img = iter->second;
       if (image->get_color_profile_icc() == nullptr && tile_img->get_color_profile_icc()) {
-        image->set_color_profile(tile_img->get_color_profile_icc());
+        image->ImageDescription::set_color_profile_icc(tile_img->get_color_profile_icc());
       }

       if (!image->has_nclx_color_profile() && tile_img->has_nclx_color_profile()) {
-        image->set_color_profile_nclx(tile_img->get_color_profile_nclx());
+        image->ImageDescription::set_color_profile_nclx(tile_img->get_color_profile_nclx());
       }
     }
   }
diff --git a/libheif/image/image_description.h b/libheif/image/image_description.h
index 9c2c8180..d3e5a6d2 100644
--- a/libheif/image/image_description.h
+++ b/libheif/image/image_description.h
@@ -184,6 +184,9 @@ public:

   const std::shared_ptr<const color_profile_raw>& get_color_profile_icc() const { return m_color_profile_icc; }

+  // This wrapper calls the virtual setters. On an ImageItem, those setters
+  // also add 'colr' properties for encoding. When reading an existing 'colr'
+  // property, use the qualified ImageDescription setters instead.
   void set_color_profile(const std::shared_ptr<const color_profile>& profile)
   {
     auto icc = std::dynamic_pointer_cast<const color_profile_raw>(profile);
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 98bd28dc..10f0e2dc 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -79,6 +79,7 @@ add_libheif_test(sdtp_sample_limit)
     add_libheif_test(hevc_brands)
     add_libheif_test(file_layout)
     add_libheif_test(image_description_metadata)
+    add_libheif_test(read_color_profile_properties)
     add_libheif_test(exception_guard)

     # decompression_bomb builds the bomb payload with the library-internal
diff --git a/tests/read_color_profile_properties.cc b/tests/read_color_profile_properties.cc
new file mode 100644
index 00000000..6ed16a70
--- /dev/null
+++ b/tests/read_color_profile_properties.cc
@@ -0,0 +1,48 @@
+/*
+  libheif unit tests
+
+  MIT License
+
+  Copyright (c) 2026 Dirk Farin <dirk.farin@gmail.com>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in all
+  copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+  SOFTWARE.
+*/
+
+#include "catch_amalgamated.hpp"
+#include "context.h"
+#include "image-items/image_item.h"
+#include "test-config.h"
+
+
+TEST_CASE("reading a file does not add color profile properties")
+{
+  // This file associates one nclx 'colr' property with its primary item.
+  HeifContext ctx;
+  Error err = ctx.read_from_file((tests_data_directory + "/clap_cropped.avif").c_str());
+  INFO("read error: " << err.message);
+  REQUIRE(!err);
+
+  auto image = ctx.get_primary_image(false);
+  REQUIRE(image);
+  REQUIRE(image->get_all_properties<Box_colr>().size() == 1);
+
+  // Reading still assigns the profile to the image description.
+  REQUIRE(image->has_nclx_color_profile());
+  REQUIRE(image->get_color_profile_icc() == nullptr);
+}