diff --git a/.env.example.complete b/.env.example.complete
index 26df8f3cb8e..49d834ff76d 100644
--- a/.env.example.complete
+++ b/.env.example.complete
@@ -281,6 +281,12 @@ ALLOW_CONTENT_SCRIPTS=false
# Contents of the robots.txt file can be overridden, making this option obsolete.
ALLOW_ROBOTS=null
+# Allow server-side fetches to be performed to potentially unknown
+# and user-provided locations. Primarily used in exports when loading
+# in externally referenced assets.
+# Can be 'true' or 'false'.
+ALLOW_UNTRUSTED_SERVER_FETCHING=false
+
# A list of hosts that BookStack can be iframed within.
# Space separated if multiple. BookStack host domain is auto-inferred.
# For Example: ALLOWED_IFRAME_HOSTS="https://example.com https://a.example.com"
diff --git a/app/Config/app.php b/app/Config/app.php
index a09c329ce73..6f1901065a9 100755
--- a/app/Config/app.php
+++ b/app/Config/app.php
@@ -36,6 +36,11 @@
// Even when overridden the WYSIWYG editor may still escape script content.
'allow_content_scripts' => env('ALLOW_CONTENT_SCRIPTS', false),
+ # Allow server-side fetches to be performed to potentially unknown
+ # and user-provided locations. Primarily used in exports when loading
+ # in externally referenced assets.
+ 'allow_untrusted_server_fetching' => env('ALLOW_UNTRUSTED_SERVER_FETCHING', false),
+
// Override the default behaviour for allowing crawlers to crawl the instance.
// May be ignored if view has be overridden or modified.
// Defaults to null since, if not set, 'app-public' status used instead.
diff --git a/app/Config/dompdf.php b/app/Config/dompdf.php
index 71ea716f387..cf07312e8a2 100644
--- a/app/Config/dompdf.php
+++ b/app/Config/dompdf.php
@@ -37,7 +37,7 @@
* Times-Roman, Times-Bold, Times-BoldItalic, Times-Italic,
* Symbol, ZapfDingbats.
*/
- 'DOMPDF_FONT_DIR' => storage_path('fonts/'), // advised by dompdf (https://github.com/dompdf/dompdf/pull/782)
+ 'font_dir' => storage_path('fonts/'), // advised by dompdf (https://github.com/dompdf/dompdf/pull/782)
/**
* The location of the DOMPDF font cache directory.
@@ -47,7 +47,7 @@
*
* Note: This directory must exist and be writable by the webserver process.
*/
- 'DOMPDF_FONT_CACHE' => storage_path('fonts/'),
+ 'font_cache' => storage_path('fonts/'),
/**
* The location of a temporary directory.
@@ -56,7 +56,7 @@
* The temporary directory is required to download remote images and when
* using the PFDLib back end.
*/
- 'DOMPDF_TEMP_DIR' => sys_get_temp_dir(),
+ 'temp_dir' => sys_get_temp_dir(),
/**
* ==== IMPORTANT ====.
@@ -70,7 +70,7 @@
* direct class use like:
* $dompdf = new DOMPDF(); $dompdf->load_html($htmldata); $dompdf->render(); $pdfdata = $dompdf->output();
*/
- 'DOMPDF_CHROOT' => realpath(base_path()),
+ 'chroot' => realpath(base_path()),
/**
* Whether to use Unicode fonts or not.
@@ -81,12 +81,12 @@
* When enabled, dompdf can support all Unicode glyphs. Any glyphs used in a
* document must be present in your fonts, however.
*/
- 'DOMPDF_UNICODE_ENABLED' => true,
+ 'unicode_enabled' => true,
/**
* Whether to enable font subsetting or not.
*/
- 'DOMPDF_ENABLE_FONTSUBSETTING' => false,
+ 'enable_fontsubsetting' => false,
/**
* The PDF rendering backend to use.
@@ -115,7 +115,7 @@
* @link http://www.ros.co.nz/pdf
* @link http://www.php.net/image
*/
- 'DOMPDF_PDF_BACKEND' => 'CPDF',
+ 'pdf_backend' => 'CPDF',
/**
* PDFlib license key.
@@ -141,7 +141,7 @@
* the desired content might be different (e.g. screen or projection view of html file).
* Therefore allow specification of content here.
*/
- 'DOMPDF_DEFAULT_MEDIA_TYPE' => 'print',
+ 'default_media_type' => 'print',
/**
* The default paper size.
@@ -150,7 +150,7 @@
*
* @see CPDF_Adapter::PAPER_SIZES for valid sizes ('letter', 'legal', 'A4', etc.)
*/
- 'DOMPDF_DEFAULT_PAPER_SIZE' => 'a4',
+ 'default_paper_size' => 'a4',
/**
* The default font family.
@@ -159,7 +159,7 @@
*
* @var string
*/
- 'DOMPDF_DEFAULT_FONT' => 'dejavu sans',
+ 'default_font' => 'dejavu sans',
/**
* Image DPI setting.
@@ -194,7 +194,7 @@
*
* @var int
*/
- 'DOMPDF_DPI' => 96,
+ 'dpi' => 96,
/**
* Enable inline PHP.
@@ -208,7 +208,7 @@
*
* @var bool
*/
- 'DOMPDF_ENABLE_PHP' => false,
+ 'enable_php' => false,
/**
* Enable inline Javascript.
@@ -218,7 +218,7 @@
*
* @var bool
*/
- 'DOMPDF_ENABLE_JAVASCRIPT' => false,
+ 'enable_javascript' => false,
/**
* Enable remote file access.
@@ -237,12 +237,12 @@
*
* @var bool
*/
- 'DOMPDF_ENABLE_REMOTE' => true,
+ 'enable_remote' => env('ALLOW_UNTRUSTED_SERVER_FETCHING', false),
/**
* A ratio applied to the fonts height to be more like browsers' line height.
*/
- 'DOMPDF_FONT_HEIGHT_RATIO' => 1.1,
+ 'font_height_ratio' => 1.1,
/**
* Enable CSS float.
@@ -251,12 +251,12 @@
*
* @var bool
*/
- 'DOMPDF_ENABLE_CSS_FLOAT' => true,
+ 'enable_css_float' => true,
/**
* Use the more-than-experimental HTML5 Lib parser.
*/
- 'DOMPDF_ENABLE_HTML5PARSER' => true,
+ 'enable_html5parser' => true,
],
diff --git a/app/Entities/Tools/ExportFormatter.php b/app/Entities/Tools/ExportFormatter.php
index c299f9c7193..05d0ff13466 100644
--- a/app/Entities/Tools/ExportFormatter.php
+++ b/app/Entities/Tools/ExportFormatter.php
@@ -140,7 +140,7 @@ public function bookToPdf(Book $book)
protected function htmlToPdf(string $html): string
{
$containedHtml = $this->containHtml($html);
- $useWKHTML = config('snappy.pdf.binary') !== false;
+ $useWKHTML = config('snappy.pdf.binary') !== false && config('app.allow_untrusted_server_fetching') === true;
if ($useWKHTML) {
$pdf = SnappyPDF::loadHTML($containedHtml);
$pdf->setOption('print-media-type', true);
diff --git a/phpunit.xml b/phpunit.xml
index 75c89ec335f..7e0da05d42f 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -37,6 +37,7 @@
+
diff --git a/tests/Entity/ExportTest.php b/tests/Entity/ExportTest.php
index 7031c3875a7..aebc5f2455f 100644
--- a/tests/Entity/ExportTest.php
+++ b/tests/Entity/ExportTest.php
@@ -366,4 +366,20 @@ public function test_export_option_only_visible_and_accessible_with_permission()
$this->assertPermissionError($resp);
}
}
+
+ public function test_wkhtmltopdf_only_used_when_allow_untrusted_is_true()
+ {
+ /** @var Page $page */
+ $page = Page::query()->first();
+
+ config()->set('snappy.pdf.binary', '/abc123');
+ config()->set('app.allow_untrusted_server_fetching', false);
+
+ $resp = $this->asEditor()->get($page->getUrl('/export/pdf'));
+ $resp->assertStatus(200); // Sucessful response with invalid snappy binary indicates dompdf usage.
+
+ config()->set('app.allow_untrusted_server_fetching', true);
+ $resp = $this->get($page->getUrl('/export/pdf'));
+ $resp->assertStatus(500); // Bad response indicates wkhtml usage
+ }
}
diff --git a/tests/Unit/ConfigTest.php b/tests/Unit/ConfigTest.php
index f45d2013632..207fb7f59e3 100644
--- a/tests/Unit/ConfigTest.php
+++ b/tests/Unit/ConfigTest.php
@@ -76,6 +76,12 @@ public function test_saml2_idp_authn_context_string_parsed_as_space_separated_ar
);
}
+ public function test_dompdf_remote_fetching_controlled_by_allow_untrusted_server_fetching_false()
+ {
+ $this->checkEnvConfigResult('ALLOW_UNTRUSTED_SERVER_FETCHING', 'false', 'dompdf.defines.enable_remote', false);
+ $this->checkEnvConfigResult('ALLOW_UNTRUSTED_SERVER_FETCHING', 'true', 'dompdf.defines.enable_remote', true);
+ }
+
/**
* Set an environment variable of the given name and value
* then check the given config key to see if it matches the given result.