mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-25 21:48:17 +00:00
Use WebpSanitizer.
This commit is contained in:
committed by
Greyson Parrelli
parent
debf964b5f
commit
15afaeabe3
77
app/src/main/java/org/thoughtcrime/securesms/glide/cache/WebpSanDecoder.kt
vendored
Normal file
77
app/src/main/java/org/thoughtcrime/securesms/glide/cache/WebpSanDecoder.kt
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.glide.cache
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import com.bumptech.glide.load.Options
|
||||
import com.bumptech.glide.load.ResourceDecoder
|
||||
import com.bumptech.glide.load.engine.Resource
|
||||
import org.signal.core.util.StreamUtil
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.signal.libsignal.media.WebpSanitizer
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
|
||||
/**
|
||||
* Uses WebpSanitizer to check for invalid webp.
|
||||
*/
|
||||
class WebpSanDecoder : ResourceDecoder<InputStream, Bitmap> {
|
||||
|
||||
companion object {
|
||||
private val TAG = Log.tag(WebpSanDecoder::class.java)
|
||||
|
||||
private val MAGIC_NUMBER_P1 = byteArrayOf(0x52, 0x49, 0x46, 0x46) // "RIFF"
|
||||
private val MAGIC_NUMBER_P2 = byteArrayOf(0x57, 0x45, 0x42, 0x50) // "WEBP"
|
||||
|
||||
private const val MAX_WEBP_COMPRESSED_SIZE = 10 * 1024 * 1024; // 10mb
|
||||
}
|
||||
|
||||
/**
|
||||
* The "magic number" for a WEBP file is in the first 12 bytes. The layout is:
|
||||
*
|
||||
* [0-3]: "RIFF"
|
||||
* [4-7]: File length
|
||||
* [8-11]: "WEBP"
|
||||
*
|
||||
* We're not verifying the file length here, so we just need to check the first and last.
|
||||
*
|
||||
* We then sanitize the webp and block the load if the check fails.
|
||||
*/
|
||||
override fun handles(source: InputStream, options: Options): Boolean {
|
||||
try {
|
||||
val magicNumberP1 = ByteArray(4)
|
||||
StreamUtil.readFully(source, magicNumberP1)
|
||||
|
||||
val fileLength = ByteArray(4)
|
||||
StreamUtil.readFully(source, fileLength)
|
||||
|
||||
val magicNumberP2 = ByteArray(4)
|
||||
StreamUtil.readFully(source, magicNumberP2)
|
||||
|
||||
if (magicNumberP1.contentEquals(MAGIC_NUMBER_P1) && magicNumberP2.contentEquals(MAGIC_NUMBER_P2)) {
|
||||
try {
|
||||
source.reset()
|
||||
source.mark(MAX_WEBP_COMPRESSED_SIZE)
|
||||
WebpSanitizer.sanitize(source, Long.MAX_VALUE)
|
||||
source.reset()
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Sanitize check failed or mark position invalidated by reset", e)
|
||||
return true
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
Log.w(TAG, "Failed to read magic number from stream!", e)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
override fun decode(source: InputStream, width: Int, height: Int, options: Options): Resource<Bitmap>? {
|
||||
Log.w(TAG, "Image did not pass sanitizer")
|
||||
throw IOException("Unable to load image")
|
||||
}
|
||||
}
|
||||
@@ -40,6 +40,7 @@ import org.thoughtcrime.securesms.glide.cache.EncryptedBitmapResourceEncoder;
|
||||
import org.thoughtcrime.securesms.glide.cache.EncryptedCacheDecoder;
|
||||
import org.thoughtcrime.securesms.glide.cache.EncryptedCacheEncoder;
|
||||
import org.thoughtcrime.securesms.glide.cache.EncryptedGifDrawableResourceEncoder;
|
||||
import org.thoughtcrime.securesms.glide.cache.WebpSanDecoder;
|
||||
import org.thoughtcrime.securesms.mms.AttachmentStreamUriLoader.AttachmentModel;
|
||||
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
|
||||
import org.thoughtcrime.securesms.stickers.StickerRemoteUri;
|
||||
@@ -64,6 +65,8 @@ public class SignalGlideComponents implements RegisterGlideComponents {
|
||||
|
||||
registry.prepend(File.class, File.class, UnitModelLoader.Factory.getInstance());
|
||||
|
||||
registry.prepend(InputStream.class, Bitmap.class, new WebpSanDecoder());
|
||||
|
||||
registry.prepend(InputStream.class, new EncryptedCacheEncoder(secret, glide.getArrayPool()));
|
||||
|
||||
registry.prepend(File.class, Bitmap.class, new EncryptedCacheDecoder<>(secret, new StreamBitmapDecoder(new Downsampler(registry.getImageHeaderParsers(), context.getResources().getDisplayMetrics(), glide.getBitmapPool(), glide.getArrayPool()), glide.getArrayPool())));
|
||||
|
||||
Reference in New Issue
Block a user