]> BookStack Code Mirror - bookstack/commitdiff
Added test and handling for local_secure_restricted in exports
authorDan Brown <redacted>
Fri, 2 Sep 2022 13:21:43 +0000 (14:21 +0100)
committerDan Brown <redacted>
Fri, 2 Sep 2022 13:21:43 +0000 (14:21 +0100)
app/Entities/Tools/ExportFormatter.php
app/Uploads/ImageService.php
tests/Uploads/ImageTest.php

index 97902db8219c12d5183a97b0e5a3565d4c158378..9e4d63cf7b0ab3c45a04ef88fec4b36cbdc5f5be 100644 (file)
@@ -235,7 +235,7 @@ class ExportFormatter
         $linksOutput = [];
         preg_match_all("/\<a.*href\=(\'|\")(.*?)(\'|\").*?\>/i", $htmlContent, $linksOutput);
 
-        // Replace image src with base64 encoded image strings
+        // Update relative links to be absolute, with instance url
         if (isset($linksOutput[0]) && count($linksOutput[0]) > 0) {
             foreach ($linksOutput[0] as $index => $linkMatch) {
                 $oldLinkString = $linkMatch;
@@ -248,7 +248,6 @@ class ExportFormatter
             }
         }
 
-        // Replace any relative links with system domain
         return $htmlContent;
     }
 
index a82fecdd7b78eff999238fd932d8d90c5703c832..ec2f6da548cc0dbd53e30e696e34c78abc9f6a0e 100644 (file)
@@ -501,6 +501,14 @@ class ImageService
         }
 
         $storagePath = $this->adjustPathForStorageDisk($storagePath);
+
+        // Apply access control when local_secure_restricted images are active
+        if ($this->usingSecureRestrictedImages()) {
+            if (!$this->checkUserHasAccessToRelationOfImageAtPath($storagePath)) {
+                return null;
+            }
+        }
+
         $storage = $this->getStorageDisk();
         $imageData = null;
         if ($storage->exists($storagePath)) {
@@ -548,6 +556,10 @@ class ImageService
      */
     protected function checkUserHasAccessToRelationOfImageAtPath(string $path): bool
     {
+        if (strpos($path, '/uploads/images/') === 0) {
+            $path = substr($path, 15);
+        }
+
         // Strip thumbnail element from path if existing
         $originalPathSplit = array_filter(explode('/', $path), function(string $part) {
             $resizedDir = (strpos($part, 'thumbs-') === 0 || strpos($part, 'scaled-') === 0);
index 3beba20076afdbe3540cda67f984097923302344..2a3023a9eafe470418d3354aaed614d506002afd 100644 (file)
@@ -377,6 +377,39 @@ class ImageTest extends TestCase
         }
     }
 
+    public function test_secure_restricted_image_access_controlled_in_exports()
+    {
+        config()->set('filesystems.images', 'local_secure_restricted');
+        $this->asEditor();
+        $galleryFile = $this->getTestImage('my-secure-restricted-export-test.png');
+
+        /** @var Page $pageA */
+        /** @var Page $pageB */
+        $pageA = Page::query()->first();
+        $pageB = Page::query()->where('id', '!=', $pageA->id)->first();
+        $expectedPath = storage_path('uploads/images/gallery/' . date('Y-m') . '/my-secure-restricted-export-test.png');
+
+        $upload = $this->asEditor()->call('POST', '/images/gallery', ['uploaded_to' => $pageA->id], [], ['file' => $galleryFile], []);
+        $upload->assertOk();
+
+        $imageUrl = json_decode($upload->getContent(), true)['url'];
+        $pageB->html .= "<img src=\"{$imageUrl}\">";
+        $pageB->save();
+
+        $encodedImageContent = base64_encode(file_get_contents($expectedPath));
+        $export = $this->get($pageB->getUrl('/export/html'));
+        $this->assertStringContainsString($encodedImageContent, $export->getContent());
+
+        $this->setEntityRestrictions($pageA, [], []);
+
+        $export = $this->get($pageB->getUrl('/export/html'));
+        $this->assertStringNotContainsString($encodedImageContent, $export->getContent());
+
+        if (file_exists($expectedPath)) {
+            unlink($expectedPath);
+        }
+    }
+
     public function test_image_delete()
     {
         $page = Page::query()->first();