branch: elpa/jabber
commit 03aec85565c1596fbaa5bfdd5a192ab3f7f74c3e
Author: Thanos Apollo <[email protected]>
Commit: Thanos Apollo <[email protected]>
chat: Enforce size cap and type checks on aesgcm images
---
lisp/jabber-chat.el | 55 ++++++++++++++++++++++-------------------------
tests/jabber-test-chat.el | 25 +++++++++++++++++++++
2 files changed, 51 insertions(+), 29 deletions(-)
diff --git a/lisp/jabber-chat.el b/lisp/jabber-chat.el
index b9e8cc4424..5c52857496 100644
--- a/lisp/jabber-chat.el
+++ b/lisp/jabber-chat.el
@@ -1559,44 +1559,41 @@ not a valid aesgcm:// URL. The fragment must be 88 hex
characters
:iv iv
:key key))))
-(defun jabber-chat--fetch-aesgcm-image (url callback &rest cbargs)
+(defun jabber-chat--aesgcm-image-from-body (encrypted key iv allowed-types)
+ "Decrypt ENCRYPTED with KEY and IV and build an inline image.
+Return nil when ENCRYPTED is missing or exceeds
+`jabber-image-max-bytes', decryption fails, or the plaintext
+fails the ALLOWED-TYPES check per `jabber-image-from-data'."
+ (and encrypted
+ (jabber-image--size-ok-p encrypted)
+ (and-let* ((plaintext (condition-case err
+ (jabber-omemo-aesgcm-decrypt key iv encrypted)
+ (error
+ (message "aesgcm: decryption failed: %s"
+ (error-message-string err))
+ nil))))
+ (jabber-image-from-data plaintext allowed-types))))
+
+(defun jabber-chat--fetch-aesgcm-image (url allowed-types callback &rest
cbargs)
"Fetch and decrypt an aesgcm:// image URL.
-Downloads via HTTPS, decrypts with AES-256-GCM, calls CALLBACK
-with the created image (or nil) followed by CBARGS."
+Downloads via HTTPS, decrypts with AES-256-GCM, and calls
+CALLBACK with the created image (or nil) followed by CBARGS.
+ALLOWED-TYPES and `jabber-image-max-bytes' are enforced per
+`jabber-image-from-data'."
(let ((parsed (jabber-chat--parse-aesgcm-url url)))
(if (null parsed)
(apply callback nil cbargs)
(url-queue-retrieve
(plist-get parsed :https-url)
- (lambda (status key iv cb args)
+ (lambda (status key iv types cb args)
(let ((url-buffer (current-buffer))
- (image
- (unless (plist-get status :error)
- (set-buffer-multibyte nil)
- (goto-char (point-min))
- (when (re-search-forward "\r?\n\r?\n" nil t)
- (let* ((encrypted (buffer-substring-no-properties
- (point) (point-max)))
- (plaintext (condition-case nil
- (jabber-omemo-aesgcm-decrypt
- key iv encrypted)
- (error nil))))
- (when plaintext
- (let ((img (create-image plaintext nil t)))
- (if (null img)
- (progn
- (message "aesgcm: failed to create image (%d
bytes decrypted)"
- (length plaintext))
- nil)
- (setf (image-property img :max-width)
- jabber-image-max-width)
- (setf (image-property img :max-height)
- jabber-image-max-height)
- img))))))))
+ (image (unless (plist-get status :error)
+ (jabber-chat--aesgcm-image-from-body
+ (jabber-image--response-body) key iv types))))
(kill-buffer url-buffer)
(apply cb image args)))
(list (plist-get parsed :key) (plist-get parsed :iv)
- callback cbargs)
+ allowed-types callback cbargs)
'silent
'inhibit-cookies))))
@@ -1969,7 +1966,7 @@ Preserve URL text and restore cached images without
refetching."
(buf (current-buffer)))
(if (string-prefix-p "aesgcm://" url)
(jabber-chat--fetch-aesgcm-image
- url
+ url nil
#'jabber-chat--replace-url-with-image
url beg end buf)
(jabber-image-fetch
diff --git a/tests/jabber-test-chat.el b/tests/jabber-test-chat.el
index b125294de9..78556f4d54 100644
--- a/tests/jabber-test-chat.el
+++ b/tests/jabber-test-chat.el
@@ -928,6 +928,31 @@ JC is a fake connection from
`jabber-test-chat--make-fake-jc'."
(cl-letf (((symbol-function 'jabber-muc-sender-p) #'ignore))
(should-not (jabber-chat--find-buffer "[email protected]/x"))))
+;;; Group 13: aesgcm image policy
+
+(ert-deftest jabber-test-chat-aesgcm-image-size-cap-blocks-decrypt ()
+ "Oversized ciphertext is rejected before decryption runs."
+ (let ((decrypted nil))
+ (cl-letf (((symbol-function 'jabber-omemo-aesgcm-decrypt)
+ (lambda (&rest _) (setq decrypted t) "plain")))
+ (let ((jabber-image-max-bytes 4))
+ (should-not (jabber-chat--aesgcm-image-from-body
+ "too big ciphertext" "key" "iv" nil))
+ (should-not decrypted)))))
+
+(ert-deftest jabber-test-chat-aesgcm-image-threads-allowed-types ()
+ (cl-letf (((symbol-function 'jabber-omemo-aesgcm-decrypt)
+ (lambda (&rest _) "plaintext"))
+ ((symbol-function 'jabber-image-from-data)
+ (lambda (data types) (list data types))))
+ (let ((jabber-image-max-bytes nil))
+ (should (equal (jabber-chat--aesgcm-image-from-body
+ "ct" "key" "iv" '(png))
+ '("plaintext" (png)))))))
+
+(ert-deftest jabber-test-chat-aesgcm-image-nil-body-returns-nil ()
+ (should-not (jabber-chat--aesgcm-image-from-body nil "key" "iv" nil)))
+
(provide 'jabber-test-chat)
;;; jabber-test-chat.el ends here