Skip to content

Commit 036c2a4

Browse files
committed
Use sandboxed Twig rendering for user-supplied and admin-configured templates
1 parent f209cf1 commit 036c2a4

9 files changed

Lines changed: 22 additions & 21 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## 5.6.7 - 2026-07-01
44

5+
- Fixed a security vulnerability where Twig templates rendered from user-supplied input were not sandboxed.
56
- Fixed a bug where purchasables’ stock caches weren’t getting cleared after orders were completed.
67
- Fixed a bug where `InventoryRestockMovement` would incorrectly validate movements with only one invalid transaction type.
78
- Fixed a bug where inventory search was case-sensitive on PostgreSQL. ([#4326](https://github.com/craftcms/commerce/issues/4326))

src/controllers/DownloadsController.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ public function actionPdf(): Response
177177
// Set previous language back
178178
Locale::switchAppLanguage($originalLanguage, $originalFormattingLocale->id);
179179

180-
$fileName = $this->getView()->renderObjectTemplate((string)$pdf->fileNameFormat, $order);
180+
$fileName = $this->getView()->renderSandboxedObjectTemplate((string)$pdf->fileNameFormat, $order);
181181
if (!$fileName) {
182182
$fileName = $pdf->handle . '-' . $order->number;
183183
}

src/controllers/PaymentsController.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -363,12 +363,12 @@ public function actionPay(): ?Response
363363
// Save the return and cancel URLs to the order
364364
$returnUrl = $this->request->getValidatedBodyParam('redirect');
365365
if ($returnUrl !== null) {
366-
$order->returnUrl = $this->getView()->renderObjectTemplate($returnUrl, $order);
366+
$order->returnUrl = $this->getView()->renderSandboxedObjectTemplate($returnUrl, $order);
367367
}
368368

369369
$cancelUrl = $this->request->getValidatedBodyParam('cancelUrl');
370370
if ($cancelUrl !== null) {
371-
$order->cancelUrl = $this->getView()->renderObjectTemplate($cancelUrl, $order);
371+
$order->cancelUrl = $this->getView()->renderSandboxedObjectTemplate($cancelUrl, $order);
372372
}
373373

374374
// Do one final save to confirm the price does not change out from under the customer. Also removes any out of stock items etc.

src/controllers/SubscriptionsController.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ public function actionSubscribe(): ?Response
238238
}
239239

240240
if ($subscription && $returnUrl) {
241-
$returnUrl = $this->getView()->renderObjectTemplate($returnUrl, $subscription);
241+
$returnUrl = $this->getView()->renderSandboxedObjectTemplate($returnUrl, $subscription);
242242
$subscriptionRecord = SubscriptionRecord::findOne($subscription->id);
243243
$subscriptionRecord->returnUrl = $returnUrl;
244244
$subscriptionRecord->save();

src/elements/Order.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1836,7 +1836,7 @@ public function markAsComplete(): bool
18361836
$referenceTemplate = $this->getStore()->getOrderReferenceFormat();
18371837

18381838
try {
1839-
$baseReference = Craft::$app->getView()->renderObjectTemplate($referenceTemplate, $this);
1839+
$baseReference = Craft::$app->getView()->renderSandboxedObjectTemplate($referenceTemplate, $this);
18401840

18411841
// Check if this reference already exists and append suffix if needed
18421842
$suffix = 0;

src/elements/Product.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,7 +1065,7 @@ protected function uiLabel(): ?string
10651065
if ($this->typeId) {
10661066
$uiLabelFormat = $this->getType()->productUiLabelFormat;
10671067
if ($uiLabelFormat !== '{title}') {
1068-
$uiLabel = Craft::$app->getView()->renderObjectTemplate($uiLabelFormat, $this);
1068+
$uiLabel = Craft::$app->getView()->renderSandboxedObjectTemplate($uiLabelFormat, $this);
10691069
if ($uiLabel !== '') {
10701070
return $uiLabel;
10711071
}
@@ -1765,7 +1765,7 @@ public function updateTitle(): void
17651765
// Set Craft to the entry's site's language, in case the title format has any static translations
17661766
$language = Craft::$app->language;
17671767
Craft::$app->language = $this->getSite()->language;
1768-
$title = Craft::$app->getView()->renderObjectTemplate($productType->productTitleFormat, $this);
1768+
$title = Craft::$app->getView()->renderSandboxedObjectTemplate($productType->productTitleFormat, $this);
17691769
if ($title !== '') {
17701770
$this->title = $title;
17711771
}
@@ -1784,7 +1784,7 @@ public function beforeValidate(): bool
17841784
foreach ($this->getVariants(true) as $variant) {
17851785
if (!$variant->sku && $type->skuFormat) {
17861786
try {
1787-
$variant->sku = Craft::$app->getView()->renderObjectTemplate($type->skuFormat, $variant);
1787+
$variant->sku = Craft::$app->getView()->renderSandboxedObjectTemplate($type->skuFormat, $variant);
17881788
} catch (\Exception $e) {
17891789
Craft::error('Craft Commerce could not generate the supplied SKU format: ' . $e->getMessage(), __METHOD__);
17901790
$variant->sku = '';

src/elements/Variant.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ protected function uiLabel(): ?string
266266
if ($owner) {
267267
$uiLabelFormat = $owner->getType()->variantUiLabelFormat;
268268
if ($uiLabelFormat !== '{title}') {
269-
$uiLabel = Craft::$app->getView()->renderObjectTemplate($uiLabelFormat, $this);
269+
$uiLabel = Craft::$app->getView()->renderSandboxedObjectTemplate($uiLabelFormat, $this);
270270
if ($uiLabel !== '') {
271271
return $uiLabel;
272272
}
@@ -679,7 +679,7 @@ public function getDescription(): string
679679
$description = $this->title;
680680

681681
if ($format = $this->getOwner()->getType()->descriptionFormat) {
682-
if ($rendered = Craft::$app->getView()->renderObjectTemplate($format, $this)) {
682+
if ($rendered = Craft::$app->getView()->renderSandboxedObjectTemplate($format, $this)) {
683683
$description = $rendered;
684684
}
685685
}
@@ -706,7 +706,7 @@ public function updateTitle(Product $product): void
706706
// Set Craft to the product's site's language, in case the title format has any static translations
707707
$language = Craft::$app->language;
708708
Craft::$app->language = $this->getSite()->language;
709-
$this->title = Craft::$app->getView()->renderObjectTemplate($type->variantTitleFormat, $this);
709+
$this->title = Craft::$app->getView()->renderSandboxedObjectTemplate($type->variantTitleFormat, $this);
710710
Craft::$app->language = $language;
711711
}
712712
}
@@ -725,7 +725,7 @@ public function updateSku(Product $product): void
725725
// Set Craft to the product’s site’s language, in case the title format has any static translations
726726
$language = Craft::$app->language;
727727
Craft::$app->language = $this->getSite()->language;
728-
$this->sku = Craft::$app->getView()->renderObjectTemplate($type->skuFormat, $this);
728+
$this->sku = Craft::$app->getView()->renderSandboxedObjectTemplate($type->skuFormat, $this);
729729

730730
$skuExistsQuery = function(string $sku, ?int $id) {
731731
$query = (new Query())

src/elements/actions/DownloadOrderPdfAction.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ public function performAction(ElementQueryInterface $query): bool
192192
*/
193193
private function _pdfFileName(Pdf $pdf, Order $order): string
194194
{
195-
$fileName = Craft::$app->getView()->renderObjectTemplate($pdf->fileNameFormat, $order);
195+
$fileName = Craft::$app->getView()->renderSandboxedObjectTemplate($pdf->fileNameFormat, $order);
196196
if (!$fileName) {
197197
$fileName = $pdf->handle . '-' . $order->number;
198198
}

src/services/Emails.php

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,7 @@ public function sendEmail(Email $email, Order $order, ?OrderHistory $orderHistor
503503
if ($email->recipientType == EmailRecord::TYPE_CUSTOM) {
504504
// To:
505505
try {
506-
$emails = $view->renderString($email->getTo(), $renderVariables);
506+
$emails = $view->renderSandboxedString($email->getTo(), $renderVariables);
507507
$emails = preg_split('/[\s,]+/', $emails);
508508

509509
$newEmail->setTo($emails);
@@ -541,7 +541,7 @@ public function sendEmail(Email $email, Order $order, ?OrderHistory $orderHistor
541541
// BCC:
542542
if ($bccSetting = $email->getBcc()) {
543543
try {
544-
$bcc = $view->renderString($bccSetting, $renderVariables);
544+
$bcc = $view->renderSandboxedString($bccSetting, $renderVariables);
545545
$bcc = str_replace(';', ',', $bcc);
546546
$bcc = preg_split('/[\s,]+/', $bcc);
547547

@@ -571,7 +571,7 @@ public function sendEmail(Email $email, Order $order, ?OrderHistory $orderHistor
571571
// CC:
572572
if ($ccSetting = $email->getCc()) {
573573
try {
574-
$cc = $view->renderString($ccSetting, $renderVariables);
574+
$cc = $view->renderSandboxedString($ccSetting, $renderVariables);
575575
$cc = str_replace(';', ',', $cc);
576576
$cc = preg_split('/[\s,]+/', $cc);
577577

@@ -601,7 +601,7 @@ public function sendEmail(Email $email, Order $order, ?OrderHistory $orderHistor
601601
if ($email->replyTo) {
602602
// Reply To:
603603
try {
604-
$newEmail->setReplyTo($view->renderString($email->replyTo, $renderVariables));
604+
$newEmail->setReplyTo($view->renderSandboxedString($email->replyTo, $renderVariables));
605605
} catch (\Exception $e) {
606606
Craft::$app->getErrorHandler()->logException($e);
607607

@@ -624,7 +624,7 @@ public function sendEmail(Email $email, Order $order, ?OrderHistory $orderHistor
624624

625625
// Subject:
626626
try {
627-
$newEmail->setSubject($view->renderString($email->subject, $renderVariables));
627+
$newEmail->setSubject($view->renderSandboxedString($email->subject, $renderVariables));
628628
} catch (\Exception $e) {
629629
Craft::$app->getErrorHandler()->logException($e);
630630

@@ -646,7 +646,7 @@ public function sendEmail(Email $email, Order $order, ?OrderHistory $orderHistor
646646

647647
// Template Path
648648
try {
649-
$templatePath = $view->renderString($email->templatePath, $renderVariables);
649+
$templatePath = $view->renderSandboxedString($email->templatePath, $renderVariables);
650650
} catch (\Exception $e) {
651651
Craft::$app->getErrorHandler()->logException($e);
652652

@@ -687,7 +687,7 @@ public function sendEmail(Email $email, Order $order, ?OrderHistory $orderHistor
687687

688688
if ($email->plainTextTemplatePath) {
689689
try {
690-
$plainTextTemplatePath = $view->renderString($email->plainTextTemplatePath, $renderVariables);
690+
$plainTextTemplatePath = $view->renderSandboxedString($email->plainTextTemplatePath, $renderVariables);
691691
} catch (\Exception $e) {
692692
Craft::$app->getErrorHandler()->logException($e);
693693

@@ -753,7 +753,7 @@ public function sendEmail(Email $email, Order $order, ?OrderHistory $orderHistor
753753
$defaultFileName = $pdf->handle . '-' . $order->number;
754754
if ($pdf->fileNameFormat) {
755755
try {
756-
$fileName = $view->renderObjectTemplate($pdf->fileNameFormat, $order);
756+
$fileName = $view->renderSandboxedObjectTemplate($pdf->fileNameFormat, $order);
757757
} catch (\Throwable) {
758758
$fileName = $defaultFileName;
759759
}

0 commit comments

Comments
 (0)