diff --git a/CMakeLists.txt b/CMakeLists.txt index c98392b0..63bff669 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ project(JVIPS C Java) cmake_minimum_required(VERSION 3.6.0) -set(JVIPS_VERSION 8.12.2) +set(JVIPS_VERSION 8.13.0) set(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${JVIPS_VERSION}") set(CPACK_SOURCE_GENERATOR "TGZ") diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index b151857c..9d934336 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -191,6 +191,33 @@ else() add_custom_target(giflib "") endif() +find_program(Meson_EXECUTABLE meson) +if(NOT Meson_EXECUTABLE) + message(FATAL_ERROR "Cooking: Meson is required!") +endif() + +find_program(Ninja_EXECUTABLE ninja) +if(NOT Ninja_EXECUTABLE) + message(FATAL_ERROR "Cooking: Ninja is required!") +endif() + +find_library(CGIF cgif PATHS "${EXT_INSTALL_DIR}/lib" NO_DEFAULT_PATH) +if (NOT CGIF) + ExternalProject_Add(cgif + URL "https://github.com/dloebl/cgif/archive/refs/tags/V${CGIF_VERSION}.tar.gz" + PREFIX "${CMAKE_CURRENT_BINARY_DIR}/cgif" + INSTALL_DIR ${EXT_INSTALL_DIR} + CONFIGURE_COMMAND + ${Meson_EXECUTABLE} --prefix= --libdir=lib + BUILD_COMMAND + ${Ninja_EXECUTABLE} -C + INSTALL_COMMAND + ${Ninja_EXECUTABLE} -C install + ) +else() + add_custom_target(cgif "") +endif() + find_library(LIBWEBP webp webpmux webpdemux PATHS "${EXT_INSTALL_DIR}/lib" NO_DEFAULT_PATH) if(NOT LIBWEBP) ExternalProject_Add(libwebp @@ -304,13 +331,14 @@ if(NOT VIPS) --with-tiff --with-tiff-includes=${EXT_INSTALL_DIR}/include --with-tiff-libraries=${EXT_INSTALL_DIR}/lib + --with-cgif --without-magick --without-orc --without-gsf --without-rsvg ${LIBSPNG_FLAGS} ${LIBHEIF_FLAGS} - DEPENDS libjpeg libpng libspng giflib libwebp libimagequant lcms2 libheif tiff + DEPENDS libjpeg libpng libspng giflib cgif libwebp libimagequant lcms2 libheif tiff BUILD_IN_SOURCE 1 ) else() diff --git a/lib/VERSIONS b/lib/VERSIONS index 571c3962..2f24e309 100644 --- a/lib/VERSIONS +++ b/lib/VERSIONS @@ -16,6 +16,9 @@ SPNG_VERSION=0.6.0 # renovate: datasource=git-tags depName=giflib packageName=https://git.code.sf.net/p/giflib/code extractVersionTemplate=^(?.*)$ GIF_VERSION=5.2.1 +# renovate: datasource=github-releases depName=cgif packageName=dloebl/cgif extractVersionTemplate=^V(?.*)$ +CGIF_VERSION=0.3.0 + # renovate: datasource=github-tags depName=libwebp packageName=webmproject/libwebp WEBP_VERSION=1.1.0 @@ -32,4 +35,4 @@ LCMS2_VERSION=2.11 TIFF_VERSION=4.1.0 # renovate: datasource=github-releases depName=libvips packageName=libvips/libvips -VIPS_VERSION=8.12.2 +VIPS_VERSION=8.13.0 diff --git a/script/enum-generator/EnumGenerator.py b/script/enum-generator/EnumGenerator.py index 26e5822c..bf15e007 100644 --- a/script/enum-generator/EnumGenerator.py +++ b/script/enum-generator/EnumGenerator.py @@ -8,7 +8,7 @@ from string import Template from itertools import takewhile -DEFAULT_VIPS_VERSION = '8.12.2' +DEFAULT_VIPS_VERSION = '8.13.0' JAVA_ENUM_TEMPLATE = "template/Enum.java" C_ENUM_TEST_TEMPLATE = "template/VipsEnumTest.c" @@ -90,6 +90,9 @@ def traverse_dictionary(dictionary, enum_output_dir, test_output_dir, license_co 'VIPS_OPERATION_SEQUENTIAL_UNBUFFERED': 2, 'VIPS_OPERATION_NOCACHE': 4, 'VIPS_OPERATION_DEPRECATED': 8, + 'VIPS_OPERATION_UNTRUSTED': 16, + 'VIPS_OPERATION_BLOCKED': 32, + 'VIPS_FOREIGN_NONE': 0, 'VIPS_FOREIGN_PARTIAL': 1, 'VIPS_FOREIGN_BIGENDIAN': 2, diff --git a/script/enum-generator/template/VipsEnumTest.c b/script/enum-generator/template/VipsEnumTest.c index 4b2e401b..f800aced 100644 --- a/script/enum-generator/template/VipsEnumTest.c +++ b/script/enum-generator/template/VipsEnumTest.c @@ -34,5 +34,8 @@ assertEqualsNativeEnumValue(JNIEnv *env, const int expected, const char *classNa JNIEXPORT void JNICALL Java_com_criteo_vips_VipsEnumTest_TestNativeEnums(JNIEnv *env, jclass c) { + if (VIPS_INIT("java") != 0) { + throwVipsException(env, "Unable to init vips"); + } $tests } diff --git a/src/main/c/VipsContext.c b/src/main/c/VipsContext.c index 31131be9..cfc246f2 100644 --- a/src/main/c/VipsContext.c +++ b/src/main/c/VipsContext.c @@ -65,4 +65,10 @@ JNIEXPORT void JNICALL Java_com_criteo_vips_VipsContext_shutdown(__attribute__((unused)) JNIEnv *env, __attribute__((unused)) jobject obj) { vips_shutdown(); -} \ No newline at end of file +} + +JNIEXPORT void JNICALL +Java_com_criteo_vips_VipsContext_setBlockUntrusted(__attribute__((unused))JNIEnv *env, __attribute__((unused)) jclass obj, jboolean enable) +{ + vips_block_untrusted_set(enable); +} diff --git a/src/main/c/VipsContext.h b/src/main/c/VipsContext.h index d789992e..6961fa63 100644 --- a/src/main/c/VipsContext.h +++ b/src/main/c/VipsContext.h @@ -71,6 +71,14 @@ JNIEXPORT jint JNICALL Java_com_criteo_vips_VipsContext_getMaxCacheMem JNIEXPORT void JNICALL Java_com_criteo_vips_VipsContext_shutdown (JNIEnv *, jclass); +/* + * Class: com_criteo_vips_VipsContext + * Method: setBlockUntrusted + * Signature: (Z)V + */ +JNIEXPORT void JNICALL Java_com_criteo_vips_VipsContext_setBlockUntrusted + (JNIEnv *, jclass, jboolean); + #ifdef __cplusplus } #endif diff --git a/src/main/c/VipsImage.c b/src/main/c/VipsImage.c index d9f1def9..6a1d2cf8 100644 --- a/src/main/c/VipsImage.c +++ b/src/main/c/VipsImage.c @@ -412,6 +412,8 @@ Java_com_criteo_vips_VipsImage_writeToArrayNative(JNIEnv *env, jobject obj, jstr status = vips_heifsave_buffer(im, &buffer, &result_length, "Q", quality, "compression", VIPS_FOREIGN_HEIF_COMPRESSION_AV1, NULL); else status = vips_heifsave_buffer(im, &buffer, &result_length, "compression", VIPS_FOREIGN_HEIF_COMPRESSION_AV1, NULL); + } else if (strcmp(ext, ".gif") == 0) { + status = vips_gifsave_buffer(im, &buffer, &result_length, "strip", strip, NULL); } else { if (quality < 0) status = vips_image_write_to_buffer(im, ext, &buffer, &result_length, "strip", strip, NULL); @@ -422,7 +424,7 @@ Java_com_criteo_vips_VipsImage_writeToArrayNative(JNIEnv *env, jobject obj, jstr { (*env)->ReleaseStringUTFChars(env, extension, ext); throwVipsException(env, "Unable to write image buffer"); - return ret; + return NULL; } ret = (*env)->NewByteArray(env, result_length); (*env)->SetByteArrayRegion(env, ret, 0, result_length * sizeof (jbyte), buffer); @@ -568,7 +570,7 @@ Java_com_criteo_vips_VipsImage_getPointPixelPacketNative(JNIEnv *env, jobject ob { throwVipsException(env, "Unable to get image point"); g_free(pixel); - return ret; + return NULL; } // Convert to uchar for (int i = 0; i < result_length; ++i) diff --git a/src/main/java/com/criteo/vips/Vips.java b/src/main/java/com/criteo/vips/Vips.java index 0b4786ad..2dd51c5f 100644 --- a/src/main/java/com/criteo/vips/Vips.java +++ b/src/main/java/com/criteo/vips/Vips.java @@ -30,6 +30,7 @@ public class Vips { "png16", "spng", "gif", + "cgif", "jpeg", "turbojpeg", "webp", diff --git a/src/main/java/com/criteo/vips/VipsContext.java b/src/main/java/com/criteo/vips/VipsContext.java index 71b822f2..fd1c498e 100644 --- a/src/main/java/com/criteo/vips/VipsContext.java +++ b/src/main/java/com/criteo/vips/VipsContext.java @@ -70,4 +70,11 @@ public class VipsContext extends Vips { * Shutdown vips context */ public static native void shutdown(); + + /** + * Set whether or not to block untrusted operations from running. + * @param blockUntrusted + */ + public static native void setBlockUntrusted(boolean blockUntrusted); + } diff --git a/src/main/java/com/criteo/vips/enums/VipsOperationFlags.java b/src/main/java/com/criteo/vips/enums/VipsOperationFlags.java index e91abe3b..decee754 100644 --- a/src/main/java/com/criteo/vips/enums/VipsOperationFlags.java +++ b/src/main/java/com/criteo/vips/enums/VipsOperationFlags.java @@ -25,7 +25,11 @@ public enum VipsOperationFlags { // must not be cached Nocache(4), // a compatibility thing - Deprecated(8); + Deprecated(8), + // not hardened for untrusted input + Untrusted(16), + // prevent this operation from running + Blocked(32); private int value; private static Map map = new HashMap(); diff --git a/src/test/c/VipsEnumTest.c b/src/test/c/VipsEnumTest.c index 80b7aa9b..ceda40f1 100644 --- a/src/test/c/VipsEnumTest.c +++ b/src/test/c/VipsEnumTest.c @@ -45,6 +45,9 @@ assertEqualsNativeEnumValue(JNIEnv *env, const int expected, const char *classNa JNIEXPORT void JNICALL Java_com_criteo_vips_VipsEnumTest_TestNativeEnums(JNIEnv *env, jclass c) { + if (VIPS_INIT("java") != 0) { + throwVipsException(env, "Unable to init vips"); + } // VipsAccess assertEqualsNativeEnumValue(env, VIPS_ACCESS_RANDOM, "com/criteo/vips/enums/VipsAccess", "Random"); assertEqualsNativeEnumValue(env, VIPS_ACCESS_SEQUENTIAL, "com/criteo/vips/enums/VipsAccess", "Sequential"); @@ -331,6 +334,8 @@ Java_com_criteo_vips_VipsEnumTest_TestNativeEnums(JNIEnv *env, jclass c) assertEqualsNativeEnumValue(env, VIPS_OPERATION_SEQUENTIAL_UNBUFFERED, "com/criteo/vips/enums/VipsOperationFlags", "SequentialUnbuffered"); assertEqualsNativeEnumValue(env, VIPS_OPERATION_NOCACHE, "com/criteo/vips/enums/VipsOperationFlags", "Nocache"); assertEqualsNativeEnumValue(env, VIPS_OPERATION_DEPRECATED, "com/criteo/vips/enums/VipsOperationFlags", "Deprecated"); + assertEqualsNativeEnumValue(env, VIPS_OPERATION_UNTRUSTED, "com/criteo/vips/enums/VipsOperationFlags", "Untrusted"); + assertEqualsNativeEnumValue(env, VIPS_OPERATION_BLOCKED, "com/criteo/vips/enums/VipsOperationFlags", "Blocked"); // VipsOperationMath assertEqualsNativeEnumValue(env, VIPS_OPERATION_MATH_SIN, "com/criteo/vips/enums/VipsOperationMath", "Sin"); assertEqualsNativeEnumValue(env, VIPS_OPERATION_MATH_COS, "com/criteo/vips/enums/VipsOperationMath", "Cos"); diff --git a/src/test/java/com/criteo/vips/VipsImageTest.java b/src/test/java/com/criteo/vips/VipsImageTest.java index f0f5e1d6..a9148f2a 100644 --- a/src/test/java/com/criteo/vips/VipsImageTest.java +++ b/src/test/java/com/criteo/vips/VipsImageTest.java @@ -151,7 +151,6 @@ public void TestShouldOpenCorrectlyFromVipsImage(@FromDataPoints("filenames") St public void TestWriteFromDirectByteBufferShouldNotThrows(@FromDataPoints("filenames") String filename, VipsImageFormat output, boolean strip) throws IOException, VipsException { - Assume.assumeTrue(output != VipsImageFormat.GIF); Assume.assumeTrue(output != VipsImageFormat.AVIF); ByteBuffer buffer = VipsTestUtils.getDirectByteBuffer(filename); try (VipsImage img = new VipsImage(buffer, buffer.capacity())) { @@ -164,7 +163,6 @@ public void TestWriteFromDirectByteBufferShouldNotThrows(@FromDataPoints("filena public void TestWriteFromByteArrayShouldNotThrows(@FromDataPoints("filenames") String filename, VipsImageFormat output, boolean strip) throws IOException, VipsException { - Assume.assumeTrue(output != VipsImageFormat.GIF); Assume.assumeTrue(output != VipsImageFormat.AVIF); byte[] buffer = VipsTestUtils.getByteArray(filename); try (VipsImage img = new VipsImage(buffer, buffer.length)) { @@ -528,8 +526,6 @@ public void TestJpgShouldNotHasTransparency() throws IOException, VipsException @Theory public void TestShouldWriteToArrayHasCorrectHeaderSignature(String filename, VipsImageFormat vipsImageFormat) throws IOException, VipsException { - // libvips can't save into gif format - Assume.assumeTrue(vipsImageFormat != VipsImageFormat.GIF); Assume.assumeTrue(vipsImageFormat != VipsImageFormat.AVIF); ArrayList expectedSignatures = SignaturesByExtension.get(vipsImageFormat.getFileExtension()); ByteBuffer buffer = VipsTestUtils.getDirectByteBuffer(filename); @@ -574,8 +570,6 @@ public void TestShouldFindTrimWithTransparent() throws IOException, VipsExceptio @Theory public void TestSimplePipelineShouldNotThrow(String filename, VipsImageFormat vipsImageFormat) throws IOException, VipsException { - // libvips can't save into gif format - Assume.assumeTrue(vipsImageFormat != VipsImageFormat.GIF); Assume.assumeTrue(vipsImageFormat != VipsImageFormat.AVIF); ByteBuffer buffer = VipsTestUtils.getDirectByteBuffer(filename); PixelPacket pixel = new PixelPacket(5.0, 255.0, 25.0); @@ -595,8 +589,6 @@ public void TestSimplePipelineShouldNotThrow(String filename, VipsImageFormat vi @Theory public void TestShouldThrowAnExceptionOnCorruptedPng(VipsImageFormat vipsImageFormat) throws IOException, VipsException { - // libvips can't save into gif format - Assume.assumeTrue(vipsImageFormat != VipsImageFormat.GIF); ByteBuffer buffer = VipsTestUtils.getDirectByteBuffer("olin.png"); try (VipsImage img = new VipsImage(buffer, buffer.capacity())) { byte[] out = img.writeToArray(vipsImageFormat, JPGQuality, true);