Skip to content
Permalink
Browse files Browse the repository at this point in the history
Security fix for csv injections.
  • Loading branch information
anuko committed Oct 11, 2020
1 parent d2f7532 commit d947290
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 12 deletions.
9 changes: 9 additions & 0 deletions WEB-INF/lib/common.lib.php
Expand Up @@ -459,3 +459,12 @@ function ttRandomString($length = 32) {
}
return $randomString;
}

// ttNeutralizeForCsv neutralizes user input for export to CSV files
// by removing =, +, -, and @ characters from the beginning of cell values.
// This mitigates a risk of CSV injection, see https://owasp.org/www-community/attacks/CSV_Injection
// Additionally, it replaces each quote character with a double quote.
function ttNeutralizeForCsv($val) {
$result = ltrim($val, '=+-@');
return str_replace('"', '""', $result);
}
2 changes: 1 addition & 1 deletion initialize.php
Expand Up @@ -37,7 +37,7 @@
ini_set('display_errors', 'Off');

// require_once('init_auth.php');
define("APP_VERSION", "1.19.23.5324");
define("APP_VERSION", "1.19.23.5325");
define("APP_DIR", dirname(__FILE__));
define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib");
define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates");
Expand Down
22 changes: 11 additions & 11 deletions tofile.php
Expand Up @@ -218,7 +218,7 @@
foreach ($custom_fields->userFields as $userField) {
$field_name = 'user_field_'.$userField['id'];
$checkbox_control_name = 'show_'.$field_name;
if ($bean->getAttribute($checkbox_control_name)) print ',"'.str_replace('"','""',$userField['label']).'"';
if ($bean->getAttribute($checkbox_control_name)) print ',"'.ttNeutralizeForCsv($userField['label']).'"';
}
}
if ($bean->getAttribute('chclient')) print ',"'.$i18n->get('label.client').'"';
Expand All @@ -229,7 +229,7 @@
foreach ($custom_fields->timeFields as $timeField) {
$field_name = 'time_field_'.$timeField['id'];
$checkbox_control_name = 'show_'.$field_name;
if ($bean->getAttribute($checkbox_control_name)) print ',"'.str_replace('"','""',$timeField['label']).'"';
if ($bean->getAttribute($checkbox_control_name)) print ',"'.ttNeutralizeForCsv($timeField['label']).'"';
}
}
if ($bean->getAttribute('chstart')) print ',"'.$i18n->get('label.start').'"';
Expand All @@ -248,24 +248,24 @@
// Print items.
foreach ($items as $item) {
print '"'.$item['date'].'"';
if ($user->can('view_reports') || $user->can('view_all_reports') || $user->isClient()) print ',"'.str_replace('"','""',$item['user']).'"';
if ($user->can('view_reports') || $user->can('view_all_reports') || $user->isClient()) print ',"'.ttNeutralizeForCsv($item['user']).'"';
// User custom fields.
if ($custom_fields && $custom_fields->userFields) {
foreach ($custom_fields->userFields as $userField) {
$field_name = 'user_field_'.$userField['id'];
$checkbox_control_name = 'show_'.$field_name;
if ($bean->getAttribute($checkbox_control_name)) print ',"'.str_replace('"','""',$item[$field_name]).'"';
if ($bean->getAttribute($checkbox_control_name)) print ',"'.ttNeutralizeForCsv($item[$field_name]).'"';
}
}
if ($bean->getAttribute('chclient')) print ',"'.str_replace('"','""',$item['client']).'"';
if ($bean->getAttribute('chproject')) print ',"'.str_replace('"','""',$item['project']).'"';
if ($bean->getAttribute('chtask')) print ',"'.str_replace('"','""',$item['task']).'"';
if ($bean->getAttribute('chclient')) print ',"'.ttNeutralizeForCsv($item['client']).'"';
if ($bean->getAttribute('chproject')) print ',"'.ttNeutralizeForCsv($item['project']).'"';
if ($bean->getAttribute('chtask')) print ',"'.ttNeutralizeForCsv($item['task']).'"';
// Time custom fields.
if ($custom_fields && $custom_fields->timeFields) {
foreach ($custom_fields->timeFields as $timeField) {
$field_name = 'time_field_'.$timeField['id'];
$checkbox_control_name = 'show_'.$field_name;
if ($bean->getAttribute($checkbox_control_name)) print ',"'.str_replace('"','""',$item[$field_name]).'"';
if ($bean->getAttribute($checkbox_control_name)) print ',"'.ttNeutralizeForCsv($item[$field_name]).'"';
}
}
if ($bean->getAttribute('chstart')) print ',"'.$item['start'].'"';
Expand All @@ -277,7 +277,7 @@
print ',"'.$val.'"';
}
if ($bean->getAttribute('chunits')) print ',"'.$item['units'].'"';
if ($bean->getAttribute('chnote')) print ',"'.str_replace('"','""',$item['note']).'"';
if ($bean->getAttribute('chnote')) print ',"'.ttNeutralizeForCsv($item['note']).'"';
if ($bean->getAttribute('chcost')) {
if ($user->can('manage_invoices') || $user->isClient())
print ',"'.$item['cost'].'"';
Expand All @@ -290,8 +290,8 @@
$ip = $item['modified'] ? $item['modified_ip'].' '.$item['modified'] : $item['created_ip'].' '.$item['created'];
print ',"'.$ip.'"';
}
if ($bean->getAttribute('chinvoice')) print ',"'.str_replace('"','""',$item['invoice']).'"';
if ($bean->getAttribute('chtimesheet')) print ',"'.str_replace('"','""',$item['timesheet_name']).'"';
if ($bean->getAttribute('chinvoice')) print ',"'.ttNeutralizeForCsv($item['invoice']).'"';
if ($bean->getAttribute('chtimesheet')) print ',"'.ttNeutralizeForCsv($item['timesheet_name']).'"';
print "\n";
}
}
Expand Down

0 comments on commit d947290

Please sign in to comment.