CVE-2024-35191 (published 2024-05-20T21:15Z)

Formie is a Craft CMS plugin for creating forms. Prior to 2.1.6, users with access to a form's settings can include malicious Twig code into fields that support Twig. These might be the Submission Title or the Success Message. This code will then be executed upon creating a submission, or rendering the text.  This has been fixed in Formie 2.1.6.


Weakness information:
cwe_id: NVD-CWE-noinfo
cwe_name: Insufficient Information
description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
extended_description: Insufficient Information

Fix information:
repo_url: https://github.com/verbb/formie
commit_details: repo_url: https://github.com/verbb/formie
author: Josh Crawford
author_date: 2024-03-03 16:01:49+11:00
msg: Change all instances of dynamic Twig to use safe, sandboxed environment to protect against potential security issues
repository: repo_url: https://github.com/verbb/formie
repo_name: verbb/formie
description: The most user-friendly forms plugin for Craft CMS.
repo_language: PHP
owner: verbb
file_changes: filename: PluginTrait.php
change_type: ModificationType.MODIFY
diff: @@ -28,6 +28,7 @@ use verbb\formie\services\Syncs;
 use verbb\formie\services\Tokens;
 use verbb\formie\web\assets\forms\FormsAsset;
 use verbb\base\BaseHelper;
+use verbb\base\services\Templates;
 
 use Craft;
 
@@ -182,6 +183,11 @@ trait PluginTrait
         return $this->get('syncs');
     }
 
+    public function getTemplates(): Templates
+    {
+        return $this->get('templates');
+    }
+
     public function getTokens(): Tokens
     {
         return $this->get('tokens');
@@ -222,6 +228,10 @@ trait PluginTrait
             'submissions' => Submissions::class,
             'subscriptions' => Subscriptions::class,
             'syncs' => Syncs::class,
+            'templates' => [
+                'class' => Templates::class,
+                'pluginClass' => Formie::class,
+            ],
             'tokens' => Tokens::class,
             'vite' => [
                 'class' => VitePluginService::class,

programming_language: PHP
filename: Webhook.php
change_type: ModificationType.MODIFY
diff: @@ -1,6 +1,7 @@
 <?php
 namespace verbb\formie\base;
 
+use verbb\formie\Formie;
 use verbb\formie\elements\Submission;
 use verbb\formie\events\ModifyWebhookPayloadEvent;
 
@@ -87,7 +88,7 @@ abstract class Webhook extends Integration
 
     protected function getWebhookUrl($url, Submission $submission): bool|string|null
     {
-        $url = Craft::$app->getView()->renderObjectTemplate($url, $submission);
+        $url = Formie::$plugin->getTemplates()->renderObjectTemplate($url, $submission);
 
         return App::parseEnv($url);
     }

programming_language: PHP
filename: FormsController.php
change_type: ModificationType.MODIFY
diff: @@ -356,7 +356,7 @@ class FormsController extends Controller
 
         if ($this->request->getAcceptsJson()) {
             $url = $this->request->getValidatedBodyParam('redirect');
-            $url = Craft::$app->getView()->renderObjectTemplate($url, $form);
+            $url = Formie::$plugin->getTemplates()->renderObjectTemplate($url, $form);
 
             return $this->asJson([
                 'success' => false,

programming_language: PHP
filename: SubmissionsController.php
change_type: ModificationType.MODIFY
diff: @@ -693,7 +693,7 @@ class SubmissionsController extends Controller
             // Refresh, there's still more pages to complete. Or check if we should "redirect" to a template-defined
             // URL, which is set for every page (commonly the first one, once a submission is available)
             if ($settings->pageRedirectUrl) {
-                $url = $this->getView()->renderObjectTemplate($settings->pageRedirectUrl, $submission);
+                $url = Formie::$plugin->getTemplates()->renderObjectTemplate($settings->pageRedirectUrl, $submission);
 
                 return $this->redirect($url);
             }
@@ -1015,7 +1015,7 @@ class SubmissionsController extends Controller
             $redirect = $form->getRedirectUrl();
         }
 
-        $redirectUrl = Craft::$app->getView()->renderObjectTemplate($redirect, $submission);
+        $redirectUrl = Formie::$plugin->getTemplates()->renderObjectTemplate($redirect, $submission);
 
         $params = array_merge([
             'success' => $success,
@@ -1189,10 +1189,7 @@ class SubmissionsController extends Controller
 
     private function _setTitle($submission, $form): void
     {
-        // Prevent users using long-hand Twig `{{` to prevent injection execution
-        $submissionTitleFormat = str_replace(['{{', '}}'], ['', ''], $form->settings->submissionTitleFormat);
-
-        $submission->title = Variables::getParsedValue($submissionTitleFormat, $submission, $form);
+        $submission->title = Variables::getParsedValue($form->settings->submissionTitleFormat, $submission, $form);
 
         // Set the default title for the submission, so it can save correctly
         if (!$submission->title) {

programming_language: PHP
filename: Submission.php
change_type: ModificationType.MODIFY
diff: @@ -535,10 +535,7 @@ class Submission extends Element
 
     public function updateTitle($form): void
     {
-        // Prevent users using long-hand Twig `{{` to prevent injection execution
-        $submissionTitleFormat = str_replace(['{{', '}}'], ['', ''], $form->settings->submissionTitleFormat);
-
-        if ($customTitle = Variables::getParsedValue($submissionTitleFormat, $this, $form)) {
+        if ($customTitle = Variables::getParsedValue($form->settings->submissionTitleFormat, $this, $form)) {
             $this->title = $customTitle;
 
             // Rather than re-save, directly update the submission record

programming_language: PHP
filename: Hidden.php
change_type: ModificationType.MODIFY
diff: @@ -125,8 +125,6 @@ class Hidden extends FormField implements PreviewableFieldInterface
                 $value = $this->defaultValue;
             }
 
-            // Prevent users using long-hand Twig `{{` to prevent injection execution
-            $value = str_replace(['{{', '}}'], ['{', '}'], $value);
             $value = Variables::getParsedValue($value, $element);
 
             // Immediately update the value for the element, so integrations use the up-to-date value

programming_language: PHP
filename: SubmissionResolver.php
change_type: ModificationType.MODIFY
diff: @@ -93,10 +93,7 @@ class SubmissionResolver extends ElementMutationResolver
         if (!$submission->title) {
             $settings = $form->settings;
 
-            // Prevent users using long-hand Twig `{{` to prevent injection execution
-            $submissionTitleFormat = str_replace(['{{', '}}'], ['', ''], $settings->submissionTitleFormat);
-
-            $submission->title = Variables::getParsedValue($submissionTitleFormat, $submission, $form);
+            $submission->title = Variables::getParsedValue($settings->submissionTitleFormat, $submission, $form);
 
             if (!$submission->title) {
                 $timeZone = Craft::$app->getTimeZone();

programming_language: PHP
filename: Variables.php
change_type: ModificationType.MODIFY
diff: @@ -263,10 +263,8 @@ class Variables
         $variables = Formie::$plugin->getRenderCache()->getVariables($cacheKey);
 
         // Try to parse submission + extra variables
-        $view = Craft::$app->getView();
-
         try {
-            return $view->renderObjectTemplate($value, $submission, $variables);
+            return Formie::$plugin->getTemplates()->renderObjectTemplate($value, $submission, $variables);
         } catch (Throwable $e) {
             Formie::error(Craft::t('formie', 'Failed to render dynamic string “{value}”. Template error: “{message}” {file}:{line}', [
                 'value' => $originalValue,

programming_language: PHP
filename: Emails.php
change_type: ModificationType.MODIFY
diff: @@ -658,7 +658,7 @@ class Emails extends Component
 
         // Generate the filename correctly.
         $filenameFormat = $template->filenameFormat ?? 'Submission-{submission.id}';
-        $fileName = Craft::$app->getView()->renderObjectTemplate($filenameFormat, $variables);
+        $fileName = Formie::$plugin->getTemplates()->renderObjectTemplate($filenameFormat, $variables);
 
         $message->attach($pdfPath, ['fileName' => $fileName . '.pdf', 'contentType' => 'application/pdf']);
 

programming_language: Perl
filename: Rendering.php
change_type: ModificationType.MODIFY
diff: @@ -487,9 +487,9 @@ class Rendering extends Component
             try {
                 $field = $form->getFieldByHandle($key);
 
-                // Prevent users using long-hand Twig `{{` to prevent injection execution. Only an issue for 
+                // Ensure that we protect against developers using Twig to populate values from query strings
                 if (is_string($value)) {
-                    $value = str_replace(['{{', '}}', '{%', '%}'], ['{', '}', '', ''], $value);
+                    $value = Formie::$plugin->getTemplates()->renderString($value);
                 }
 
                 if ($field) {

programming_language: PHP