From 7e7c4cf0a7daff517f37432fc9de4df34981ce16 Mon Sep 17 00:00:00 2001
From: Jared Hancock <jared@osticket.com>
Date: Wed, 8 Jan 2014 12:54:25 -0600
Subject: [PATCH] Fix possible serious errors in dynamic form fields

Fix dropping of materialized view when variable name is changed

Ensure view exists before merging updates

Prevent possible sql injection error in field name used in the materialized
view.

Prevent possible xss error in the display of the field label and variable
name in the admin panel.
---
 include/ajax.tickets.php           |  7 ++++---
 include/class.dynamic_forms.php    | 29 ++++++++++++++++++-----------
 include/staff/dynamic-form.inc.php |  5 +++--
 scp/forms.php                      |  2 ++
 4 files changed, 27 insertions(+), 16 deletions(-)

diff --git a/include/ajax.tickets.php b/include/ajax.tickets.php
index ceb824e2b..376e2a41d 100644
--- a/include/ajax.tickets.php
+++ b/include/ajax.tickets.php
@@ -211,10 +211,11 @@ class TicketsAjaxAPI extends AjaxController {
         foreach (TicketForm::getInstance()->getFields() as $f) {
             if (isset($req[$f->getFormName()])
                     && ($val = $req[$f->getFormName()])) {
-                $name = $f->get('name') ? $f->get('name') : 'field_'.$f->get('id');
-                $cwhere = "cdata.`$name` LIKE '%".db_real_escape($val)."%'";
+                $name = $f->get('name') ? db_real_escape($f->get('name'))
+                    : 'field_'.$f->get('id');
+                $cwhere = "cdata.\"$name\" LIKE '%".db_real_escape($val)."%'";
                 if ($f->getImpl()->hasIdValue() && is_numeric($val))
-                    $cwhere .= " OR cdata.`{$name}_id` = ".db_input($val);
+                    $cwhere .= " OR cdata.\"{$name}_id\" = ".db_input($val);
                 $where .= ' AND ('.$cwhere.')';
                 $cdata_search = true;
             }
diff --git a/include/class.dynamic_forms.php b/include/class.dynamic_forms.php
index ac1be4f3d..69e5a2e40 100644
--- a/include/class.dynamic_forms.php
+++ b/include/class.dynamic_forms.php
@@ -210,15 +210,15 @@ class TicketForm extends DynamicForm {
             if (!$impl->hasData() || $impl->isPresentationOnly())
                 continue;
 
-            $name = ($f->get('name')) ? $f->get('name')
+            $name = ($f->get('name')) ? db_real_escape($f->get('name'))
                 : 'field_'.$f->get('id');
 
             $fields[] = sprintf(
-                'MAX(IF(field.name=\'%1$s\',ans.value,NULL)) as `%1$s`',
+                'MAX(IF(field.name=\'%1$s\',ans.value,NULL)) as "%1$s"',
                 $name);
             if ($impl->hasIdValue()) {
                 $fields[] = sprintf(
-                    'MAX(IF(field.name=\'%1$s\',ans.value_id,NULL)) as `%1$s_id`',
+                    'MAX(IF(field.name=\'%1$s\',ans.value_id,NULL)) as "%1$s_id"',
                     $name);
             }
         }
@@ -261,21 +261,21 @@ class TicketForm extends DynamicForm {
         if (!($e = $answer->getEntry()) || $e->get('object_type') != 'T')
             return;
 
-        // If the `name` column is in the dirty list, we would be renaming a
-        // column. Delete the view instead.
-        if (isset($data['dirty']) && isset($data['dirty']['name']))
-            return self::dropDynamicDataView();
-
         // $record = array();
         // $record[$f] = $answer->value'
         // TicketFormData::objects()->filter(array('ticket_id'=>$a))
         //      ->merge($record);
+        $sql = 'SHOW TABLES LIKE \''.TABLE_PREFIX.'ticket__cdata\'';
+        if (!db_num_rows(db_query($sql)))
+            return;
+
         $f = $answer->getField();
-        $name = $f->get('name') ? $f->get('name') : 'field_'.$f->get('id');
+        $name = $f->get('name') ? db_real_escape($f->get('name'))
+            : 'field_'.$f->get('id');
         $ids = $f->hasIdValue();
-        $fields = sprintf('`%s`=', $name) . db_input($answer->get('value'));
+        $fields = sprintf('"%s"=', $name) . db_input($answer->get('value'));
         if ($f->hasIdValue())
-            $fields .= sprintf(',`%s_id`=', $name) . db_input($answer->getIdValue());
+            $fields .= sprintf(',"%s_id"=', $name) . db_input($answer->getIdValue());
         $sql = 'INSERT INTO `'.TABLE_PREFIX.'ticket__cdata` SET '.$fields
             .', `ticket_id`='.db_input($answer->getEntry()->get('object_id'))
             .' ON DUPLICATE KEY UPDATE '.$fields;
@@ -309,6 +309,13 @@ Signal::connect('model.deleted',
     array('TicketForm', 'dropDynamicDataView'),
     'DynamicFormField',
     function($o) { return $o->getForm()->get('type') == 'T'; });
+// If the `name` column is in the dirty list, we would be renaming a
+// column. Delete the view instead.
+Signal::connect('model.updated',
+    array('TicketForm', 'dropDynamicDataView'),
+    'DynamicFormField',
+    // TODO: Lookup the dynamic form to verify {type == 'T'}
+    function($o, $d) { return isset($d['dirty']) && isset($d['dirty']['name']); });
 
 require_once(INCLUDE_DIR . "class.json.php");
 
diff --git a/include/staff/dynamic-form.inc.php b/include/staff/dynamic-form.inc.php
index a5019a722..a565e1be5 100644
--- a/include/staff/dynamic-form.inc.php
+++ b/include/staff/dynamic-form.inc.php
@@ -123,7 +123,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         <tr>
             <td><i class="icon-sort"></i></td>
             <td><input type="text" size="32" name="label-<?php echo $id; ?>"
-                value="<?php echo $f->get('label'); ?>"/>
+                value="<?php echo Format::htmlchars($f->get('label')); ?>"/>
                 <font class="error"><?php
                     if ($ferrors['label']) echo '<br/>'; echo $ferrors['label']; ?>
             </td>
@@ -161,7 +161,8 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
             </td>
             <td>
                 <input type="text" size="20" name="name-<?php echo $id; ?>"
-                    value="<?php echo $f->get('name'); ?>" <?php echo $force_name ?>/>
+                    value="<?php echo Format::htmlchars($f->get('name'));
+                    ?>" <?php echo $force_name ?>/>
                 <font class="error"><?php
                     if ($ferrors['name']) echo '<br/>'; echo $ferrors['name'];
                 ?></font>
diff --git a/scp/forms.php b/scp/forms.php
index 6f14be6c1..077b3e0ff 100644
--- a/scp/forms.php
+++ b/scp/forms.php
@@ -43,6 +43,8 @@ if($_POST) {
                 }
                 if (in_array($field->get('name'), $names))
                     $field->addError('Field variable name is not unique', 'name');
+                if (preg_match('/[.{}\'"`; ]/u', $field->get('name')))
+                    $field->addError('Invalid character in variable name. Please use letters and numbers only.', 'name');
                 if ($field->get('name'))
                     $names[] = $field->get('name');
                 if ($field->isValid())
-- 
GitLab