CVE-2024-25737: Insufficient Information (NVD-CWE-noinfo)
value: A Server-Side Request Forgery (SSRF) vulnerability in the /Cover/Show route (showAction in CoverController.php) in Open Library Foundation VuFind 2.4 through 9.1 before 9.1.1 allows remote attackers to access internal HTTP servers and perform Cross-Site Scripting (XSS) attacks by proxying arbitrary URLs via the proxy GET parameter.
CWE description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
Published: 2024-05-22T19:15Z
Repository: https://github.com/vufind-org/vufind
Language: Other
Files changed: config/vufind/config.ini, module/VuFind/src/VuFind/Controller/CoverController.php
Commit date: 2024-02-06 15:08:29 -0500
Commit message: Make cover proxy legal content types configurable. (#3388)


Diff: diff --git a/config/vufind/config.ini b/config/vufind/config.ini
index 6fcbcc1daa..74142fbb20 100644
--- a/config/vufind/config.ini
+++ b/config/vufind/config.ini
@@ -1028,10 +1028,17 @@ coverproxyCache[] = "/assets\.thirdiron\.com/"
 ; expressions below will not be proxied. Be sure to define regular expressions
 ; using an end of string ($) marker to prevent abuse.
 coverproxyAllowedHosts[] = "/assets\.thirdiron\.com$/"
 coverproxyAllowedHosts[] = "/\.summon\.serialssolutions\.com$/"
 
+; This setting controls the content types allowed through the cover proxy.
+; Note that proxying SVG files is not recommended because of their potential for
+; abuse in cross-site scripting attacks.
+coverproxyAllowedTypes[] = "image/gif"
+coverproxyAllowedTypes[] = "image/jpeg"
+coverproxyAllowedTypes[] = "image/png"
+
 ; This setting controls how cover image URLs are loaded. They could be loaded as
 ; part of main request, or asynchronously. Asynchronous loading is disabled by
 ; default; to enable it, just uncomment the line below.
 ;ajaxcovers = true
 
diff --git a/module/VuFind/src/VuFind/Controller/CoverController.php b/module/VuFind/src/VuFind/Controller/CoverController.php
index 49b6e952ac..3633a65116 100644
--- a/module/VuFind/src/VuFind/Controller/CoverController.php
+++ b/module/VuFind/src/VuFind/Controller/CoverController.php
@@ -31,10 +31,12 @@ namespace VuFind\Controller;
 
 use VuFind\Cover\CachingProxy;
 use VuFind\Cover\Loader;
 use VuFind\Session\Settings as SessionSettings;
 
+use function in_array;
+
 /**
  * Generates covers for book entries
  *
  * @category VuFind
  * @package  Controller
@@ -144,10 +146,24 @@ class CoverController extends \Laminas\Mvc\Controller\AbstractActionController
             }
         }
         return false;
     }
 
+    /**
+     * Is the content type allowed by the cover proxy?
+     *
+     * @param string $contentType Type to check
+     *
+     * @return bool
+     */
+    protected function isValidProxyImageContentType(string $contentType): bool
+    {
+        $validTypes = $this->config['coverproxyAllowedTypes']
+            ?? ['image/gif', 'image/jpeg', 'image/png'];
+        return in_array(strtolower($contentType), array_map('strtolower', $validTypes));
+    }
+
     /**
      * Send image data for display in the view
      *
      * @return \Laminas\Http\Response
      */
@@ -159,11 +175,11 @@ class CoverController extends \Laminas\Mvc\Controller\AbstractActionController
         $url = $this->params()->fromQuery('proxy');
         if (!empty($url) && $this->proxyAllowedForUrl($url)) {
             try {
                 $image = $this->proxy->fetch($url);
                 $contentType = $image?->getHeaders()?->get('content-type')?->getFieldValue() ?? '';
-                if (str_starts_with(strtolower($contentType), 'image/')) {
+                if ($this->isValidProxyImageContentType($contentType)) {
                     return $this->displayImage(
                         $contentType,
                         $image->getContent()
                     );
                 }

Vulnerable code:                 if (str_starts_with(strtolower($contentType), 'image/')) {
Fixed code: ; This setting controls the content types allowed through the cover proxy.
; Note that proxying SVG files is not recommended because of their potential for
; abuse in cross-site scripting attacks.
coverproxyAllowedTypes[] = "image/gif"
coverproxyAllowedTypes[] = "image/jpeg"
coverproxyAllowedTypes[] = "image/png"

use function in_array;

    /**
     * Is the content type allowed by the cover proxy?
     *
     * @param string $contentType Type to check
     *
     * @return bool
     */
    protected function isValidProxyImageContentType(string $contentType): bool
    {
        $validTypes = $this->config['coverproxyAllowedTypes']
            ?? ['image/gif', 'image/jpeg', 'image/png'];
        return in_array(strtolower($contentType), array_map('strtolower', $validTypes));
    }

                if ($this->isValidProxyImageContentType($contentType)) {