diff --git a/CHANGES.md b/CHANGES.md
new file mode 100644
index 0000000..53ae376
--- /dev/null
+++ b/CHANGES.md
@@ -0,0 +1,31 @@
+## Main feature
+
+This module was created to roughly calculate randomness in redownloaded pictures.
+
+It can be used to detect possible memory leaks from image renderers.
+If there is some error in the image renderer logic, and it is not handling exceptions properly,
+it is possible that a malformed uploaded file will trigger it, and the response will contain parts of the
+image renderer memory.
+This behaviour will increase the randomness of pixels in the returned picture.
+
+By getting the full RGB list from the response picture, and compressing it with zlib, we can
+estimate the randomness of the pixels by dividing these two values with each other.
+
+This module will give an informational issue to all redownloaded pictures, where the compression ratio is beneath the
+median of all compression ratios, which can help us to see, if there is too much random noise in the picture,
+and from there, we can manually investigate the incident.
+
+## When/How it is used
+
+Image entropy calculation is only done on the Fingerping, fuzzer, and recursive uploader module, if the "Calculate image entropy" checkbox is selected.
+The module will create informational issues where the redownloaded picture compression ratio is lower than the avarage of it. It can be used to detect issues such as this: https://blog.silentsignal.eu/2019/04/18/drop-by-drop-bleeding-through-libvips/
+
+## Changes in the code:
+
+Added a checkbox to the general options UI with label "Calculate image entropy". It's referenced in the code as calculate_entropy
+
+Added new functions for the entropy calculation/reporting. These are defined from line 866 to 922.
+
+The calculation is called in the affected modules(Fingerping/Fuzzer/Recursive uploader), where all the ratios and request/responses are stored in a local variable called Entropy_list, which is used at the end of each module for the reporting.
+
+Added a new helper function which calculates the median of a given list. (line 4243)
diff --git a/UploadScanner.py b/UploadScanner.py
index 95b272d..c5f09f9 100755
--- a/UploadScanner.py
+++ b/UploadScanner.py
@@ -862,6 +862,66 @@ def add_log_entry(self, rr):
self._helpers.analyzeRequest(rr).getUrl()))
self.fireTableRowsInserted(row, row)
+ # Helper functions for image entropy calculation
+ def _calculate_image_entropy_wrapper(self, injector, urr):
+ download_responseInfo = self._helpers.analyzeResponse(urr.download_rr.getResponse())
+ headers = [FloydsHelpers.u2s(x) for x in download_responseInfo.getHeaders()]
+ if any("image/" in h for h in headers):
+ ratio = self._calculate_image_entropy(injector, urr)
+ return ratio
+
+ def _report_image_entropy(self, Entropy_list):
+ # Calculate median of the ratios
+ ratios = [ratio[0] for ratio in Entropy_list]
+ median = self._median(ratios)
+ for item in Entropy_list:
+ if item[0] and item[0] < median:
+ injector = item[1]
+ urr = item[2]
+ ratio = item[0]
+ service = urr.download_rr.getHttpService()
+ url = self._helpers.analyzeRequest(urr.download_rr).getUrl()
+ name = "Downloaded image entropy info"
+ severity = "Information"
+ confidence = "Firm"
+ detail = "Information about the redownloaded image entropy where compression ratios were lower than the avarage.
" \
+ "If the compression ratio is too low, that could mean that the image contains memory leak.
" \
+ "The avarage compression ratio for the module was: {}
" \
+ "Compression ratio for this image: {}
".format(median,ratio)
+
+ csi = CustomScanIssue([injector.get_brr()], name, detail, confidence, severity, service, url)
+ csi.httpMessagesPy = [urr.upload_rr, urr.download_rr]
+ if csi:
+ self._callbacks.addScanIssue(csi)
+
+
+
+ # Function to calculate image entropy via compressing redownloaded images
+ def _calculate_image_entropy(self, injector, urr):
+ download_iResponseInfo = self._helpers.analyzeResponse(urr.download_rr.getResponse())
+ download_body = FloydsHelpers.jb2ps(urr.download_rr.getResponse())[download_iResponseInfo.getBodyOffset():]
+
+ try:
+ # Rescaling the image for performance
+ picture_width, picture_height, fileformat = ImageHelpers.image_width_height(download_body)
+ if picture_width and picture_height and fileformat:
+ if picture_width >= 200 or picture_height >= 200:
+ download_thumbnail = ImageHelpers.rescale_image(200, 200, download_body)
+ if download_thumbnail:
+ download_body = download_thumbnail
+
+ download_rgbs = ImageHelpers.get_image_rgb_list(download_body)
+ download_conv = "".join([struct.pack("