From 7e9111012d54e0feae92598b508f99cb035167d0 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Wed, 20 Jan 2021 17:26:27 +0000 Subject: [PATCH 001/432] Dutch translation improved. --- WEB-INF/resources/nl.lang.php | 12 +++--------- initialize.php | 2 +- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/WEB-INF/resources/nl.lang.php b/WEB-INF/resources/nl.lang.php index 6d51082c..70750349 100644 --- a/WEB-INF/resources/nl.lang.php +++ b/WEB-INF/resources/nl.lang.php @@ -390,16 +390,12 @@ // Login form. See example at https://timetracker.anuko.com/login.php. 'form.login.forgot_password' => 'Wachtwoord vergeten?', - // TODO: re-translate form.login.about as it has changed. - // 'form.login.about' => 'Anuko Time Tracker is an open source time tracking system.', -'form.login.about' => 'Anuko Time Tracker is een eenvoudig en gemakkelijk te gebruiken open source tijdregistratiesysteem.', +'form.login.about' => 'Anuko Time Tracker is een open source tijdregistratiesysteem.', // Resetting Password form. See example at https://timetracker.anuko.com/password_reset.php. 'form.reset_password.message' => 'Het verzoek om het wachtwoord te herstellen is verzonden per email.', 'form.reset_password.email_subject' => 'Anuko Time Tracker wachtwoord herstel verzoek', -// TODO: retranslate form.reset_password.email_body as the string has changed. -// 'form.reset_password.email_body' => "Dear User,\n\nSomeone from IP %s requested your Anuko Time Tracker password reset. Please visit this link if you want to reset your password.\n\n%s\n\nAnuko Time Tracker is an open source time tracking system. Visit https://www.anuko.com for more information.\n\n", -'form.reset_password.email_body' => "Geachte medewerker,\n\nIemand, met IP adres %s, heeft verzocht uw wachtwoord in Anuko Time Tracker te herstellen. Klik op deze link als u uw wachtwoord wil wijzigen.\n\n%s\n\nAnuko Time Tracker is een eenvoudig en gemakkelijk te gebruiken open source tijdregistratiesysteem. Bezoek https://www.anuko.com voor meer informatie.\n\n", +'form.reset_password.email_body' => "Beste gebruiker,\n\nIemand, met IP adres %s, heeft verzocht uw wachtwoord in Anuko Time Tracker te herstellen. Klik op deze link als u uw wachtwoord wil wijzigen.\n\n%s\n\nAnuko Time Tracker is een open source tijdregistratiesysteem. Bezoek https://www.anuko.com voor meer informatie.\n\n", // Changing Password form. See example at https://timetracker.anuko.com/password_change.php?ref=1. 'form.change_password.tip' => 'Voer het nieuwe wachtwoord in en klik op Bewaren.', @@ -546,9 +542,7 @@ // Mail form. See example at https://timetracker.anuko.com/report_send.php when emailing a report. 'form.mail.to' => 'Aan', 'form.mail.report_subject' => 'Time Tracker rapport', -// TODO: retranslate form.mail.footer as the English string has changed. -// 'form.mail.footer' => 'Anuko Time Tracker is an open source
time tracking system. Visit www.anuko.com for more information.', -'form.mail.footer' => 'Anuko Time Tracker is een eenvoudig en gemakkelijk te gebruiken open source tijdregistratiesysteem. Bezoek www.anuko.com voor meer informatie.', +'form.mail.footer' => 'Anuko Time Tracker is een open source tijdregistratiesysteem. Bezoek www.anuko.com voor meer informatie.', 'form.mail.report_sent' => 'Rapport is verzonden.', 'form.mail.invoice_sent' => 'Factuur is verzonden.', diff --git a/initialize.php b/initialize.php index 6ccd4533..f955479f 100644 --- a/initialize.php +++ b/initialize.php @@ -13,7 +13,7 @@ ini_set('display_errors', 'Off'); // require_once('init_auth.php'); -define("APP_VERSION", "1.19.23.5409"); +define("APP_VERSION", "1.19.23.5410"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 13ad968d9729dbff50acdeb652bba5e298633c0e Mon Sep 17 00:00:00 2001 From: Josh Bowers Date: Wed, 27 Jan 2021 07:06:47 -0800 Subject: [PATCH 002/432] extra '+' character and end of line causing error (#105) PHP Fatal error: Uncaught TypeError: Unsupported operand types: Smarty * int in ./time_edit.php:453 --- time_edit.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/time_edit.php b/time_edit.php index 89988409..4065bf08 100644 --- a/time_edit.php +++ b/time_edit.php @@ -449,7 +449,7 @@ $smarty->assign('entry_date', $cl_date); } $smarty->assign('show_client', $showClient); -$smarty->assign('show_billable', $showBillable);+ +$smarty->assign('show_billable', $showBillable); $smarty->assign('show_paid_status', $showPaidStatus); $smarty->assign('show_project', $showProject); $smarty->assign('show_task', $showTask); From 87b5c171f0ddb7ba24e5cf86a5238f1564e12459 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sat, 20 Feb 2021 21:03:38 +0000 Subject: [PATCH 003/432] A couple of minor fixes to remove some php8 warnings. --- WEB-INF/lib/DateAndTime.class.php | 3 ++- WEB-INF/lib/ttUser.class.php | 1 + initialize.php | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/WEB-INF/lib/DateAndTime.class.php b/WEB-INF/lib/DateAndTime.class.php index 20f27426..1184bd85 100644 --- a/WEB-INF/lib/DateAndTime.class.php +++ b/WEB-INF/lib/DateAndTime.class.php @@ -325,6 +325,7 @@ function preprocessFormatString($format) { // replace locale-dependent strings $format = str_replace('%a', mb_substr($i18n->getWeekDayName($this->mDay), 0, 3, 'utf-8'), $format); $format = str_replace('%A', $i18n->getWeekDayName($this->mDay), $format); + /* This block is commented out because we currently do not use these formatters. $abbrev_month = mb_substr($i18n->monthNames[$this->mMonth], 0, 3, 'utf-8'); $format = str_replace('%b', $abbrev_month, $format); $format = str_replace('%h', $abbrev_month, $format); @@ -332,7 +333,7 @@ function preprocessFormatString($format) { $format = str_replace('%Z', date('O'), $format); // format as 'O' for consistency with JS strftime if (strpos($format, '%c') !== false) { $format = str_replace('%c', $this->preprocessFormatString('%a %d %b %Y %T %Z'), $format); - } + }*/ return $format; } diff --git a/WEB-INF/lib/ttUser.class.php b/WEB-INF/lib/ttUser.class.php index 72b829d3..c006350f 100644 --- a/WEB-INF/lib/ttUser.class.php +++ b/WEB-INF/lib/ttUser.class.php @@ -488,6 +488,7 @@ function getUsers($options) { $includeSelf = isset($options['include_self']); $select_part = 'select u.id, u.group_id, u.name'; + $include_quota = false; if (isset($options['include_login'])) { $select_part .= ', u.login'; // Piggy-back on include_login to see if we must also include quota_percent. diff --git a/initialize.php b/initialize.php index f955479f..55b1a103 100644 --- a/initialize.php +++ b/initialize.php @@ -13,7 +13,7 @@ ini_set('display_errors', 'Off'); // require_once('init_auth.php'); -define("APP_VERSION", "1.19.23.5410"); +define("APP_VERSION", "1.19.23.5411"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From f3f9921ce9d15b84658b40bf1cefe4870490e0ab Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sat, 20 Feb 2021 21:56:04 +0000 Subject: [PATCH 004/432] A couple more fixes for php8. --- WEB-INF/lib/form/Calendar.class.php | 2 +- WEB-INF/lib/ttUser.class.php | 4 +++- initialize.php | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/WEB-INF/lib/form/Calendar.class.php b/WEB-INF/lib/form/Calendar.class.php index d335e150..337737f1 100644 --- a/WEB-INF/lib/form/Calendar.class.php +++ b/WEB-INF/lib/form/Calendar.class.php @@ -80,7 +80,7 @@ function getHtml() { $firstOfPreviousMonth = strftime(DB_DATEFORMAT, $firstOfPreviousMonth2AM); // Print calendar header. - $html .= "\n\n\n"; + $html = "\n\n\n"; $html .= ''."\n"; $html .= '
'; $html .= '
'; diff --git a/WEB-INF/lib/ttUser.class.php b/WEB-INF/lib/ttUser.class.php index c006350f..b01ffe68 100644 --- a/WEB-INF/lib/ttUser.class.php +++ b/WEB-INF/lib/ttUser.class.php @@ -347,7 +347,9 @@ function getAssignedProjects($options = null) $group_id = $this->getGroup(); $org_id = $this->org_id; - if ($options['include_files']) { + $filePart = ''; + $fileJoin = ''; + if (isset($options['include_files'])) { $filePart = ', if(Sub1.entity_id is null, 0, 1) as has_files'; $fileJoin = " left join (select distinct entity_id from tt_files". " where entity_type = 'project' and group_id = $group_id and org_id = $org_id and status = 1) Sub1". diff --git a/initialize.php b/initialize.php index 55b1a103..1aa264cc 100644 --- a/initialize.php +++ b/initialize.php @@ -13,7 +13,7 @@ ini_set('display_errors', 'Off'); // require_once('init_auth.php'); -define("APP_VERSION", "1.19.23.5411"); +define("APP_VERSION", "1.19.23.5412"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 6a5b24018a7c9d5744c84e8135d588b81b9ae6cf Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sat, 20 Feb 2021 23:11:47 +0000 Subject: [PATCH 005/432] Updated smarty to version 3.1.39 for better php8 support. --- WEB-INF/lib/smarty/Smarty.class.php | 7 +++---- WEB-INF/lib/smarty/SmartyBC.class.php | 2 +- .../smarty_internal_compile_function.php | 5 +++++ ...internal_compile_private_special_variable.php | 8 ++++++-- .../smarty_internal_config_file_compiler.php | 4 ++-- .../sysplugins/smarty_internal_errorhandler.php | 2 +- .../smarty_internal_parsetree_template.php | 16 ++++++++-------- WEB-INF/templates/time2.tpl | 2 ++ initialize.php | 2 +- 9 files changed, 29 insertions(+), 19 deletions(-) diff --git a/WEB-INF/lib/smarty/Smarty.class.php b/WEB-INF/lib/smarty/Smarty.class.php index 9e0ca767..375bab13 100644 --- a/WEB-INF/lib/smarty/Smarty.class.php +++ b/WEB-INF/lib/smarty/Smarty.class.php @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. + * version 3.0 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -27,7 +27,6 @@ * @author Uwe Tews * @author Rodney Rehm * @package Smarty - * @version 3.1.34-dev */ /** * set SMARTY_DIR to absolute path to Smarty library files. @@ -112,7 +111,7 @@ class Smarty extends Smarty_Internal_TemplateBase /** * smarty version */ - const SMARTY_VERSION = '3.1.36'; + const SMARTY_VERSION = '3.1.39'; /** * define variable scopes */ @@ -800,7 +799,7 @@ public function addTemplateDir($template_dir, $key = null, $isConfig = false) * @param mixed $index index of directory to get, null to get all * @param bool $isConfig true for config_dir * - * @return array list of template directories, or directory of $index + * @return array|string list of template directories, or directory of $index */ public function getTemplateDir($index = null, $isConfig = false) { diff --git a/WEB-INF/lib/smarty/SmartyBC.class.php b/WEB-INF/lib/smarty/SmartyBC.class.php index 836f9815..0550e46d 100644 --- a/WEB-INF/lib/smarty/SmartyBC.class.php +++ b/WEB-INF/lib/smarty/SmartyBC.class.php @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. + * version 3.0 of the License, or (at your option) any later version. * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_function.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_function.php index 6e408ca7..d0f2b0f4 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_function.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_function.php @@ -58,6 +58,11 @@ public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler) } unset($_attr[ 'nocache' ]); $_name = trim($_attr[ 'name' ], '\'"'); + + if (!preg_match('/^[a-zA-Z0-9_\x80-\xff]+$/', $_name)) { + $compiler->trigger_template_error("Function name contains invalid characters: {$_name}", null, true); + } + $compiler->parent_compiler->tpl_function[ $_name ] = array(); $save = array( $_attr, $compiler->parser->current_buffer, $compiler->template->compiled->has_nocache_code, diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_private_special_variable.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_private_special_variable.php index b317c9f3..d53ef51f 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_private_special_variable.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_compile_private_special_variable.php @@ -81,6 +81,10 @@ public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler, $ case 'template': return 'basename($_smarty_tpl->source->filepath)'; case 'template_object': + if (isset($compiler->smarty->security_policy)) { + $compiler->trigger_template_error("(secure mode) template_object not permitted"); + break; + } return '$_smarty_tpl'; case 'current_dir': return 'dirname($_smarty_tpl->source->filepath)'; @@ -94,9 +98,9 @@ public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler, $ break; } if (strpos($_index[ 1 ], '$') === false && strpos($_index[ 1 ], '\'') === false) { - return "@constant('{$_index[1]}')"; + return "(defined('{$_index[1]}') ? constant('{$_index[1]}') : null)"; } else { - return "@constant({$_index[1]})"; + return "(defined({$_index[1]}) ? constant({$_index[1]}) : null)"; } // no break case 'config': diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_config_file_compiler.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_config_file_compiler.php index a842fa8f..90c5dcef 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_config_file_compiler.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_config_file_compiler.php @@ -115,7 +115,7 @@ public function compileTemplate(Smarty_Internal_Template $template) $this->smarty->_debug->start_compile($this->template); } // init the lexer/parser to compile the config file - /* @var Smarty_Internal_ConfigFileLexer $this ->lex */ + /* @var Smarty_Internal_ConfigFileLexer $this->lex */ $this->lex = new $this->lexer_class( str_replace( array( @@ -127,7 +127,7 @@ public function compileTemplate(Smarty_Internal_Template $template) ) . "\n", $this ); - /* @var Smarty_Internal_ConfigFileParser $this ->parser */ + /* @var Smarty_Internal_ConfigFileParser $this->parser */ $this->parser = new $this->parser_class($this->lex, $this); if (function_exists('mb_internal_encoding') && function_exists('ini_get') diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_errorhandler.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_errorhandler.php index 0ba00659..56dca18f 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_errorhandler.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_errorhandler.php @@ -65,7 +65,7 @@ public static function muteExpectedErrors() * * @return bool */ - public static function mutingErrorHandler($errno, $errstr, $errfile, $errline, $errcontext) + public static function mutingErrorHandler($errno, $errstr, $errfile, $errline, $errcontext = array()) { $_is_muted_directory = false; // add the SMARTY_DIR to the list of muted directories diff --git a/WEB-INF/lib/smarty/sysplugins/smarty_internal_parsetree_template.php b/WEB-INF/lib/smarty/sysplugins/smarty_internal_parsetree_template.php index 477232ef..ab4c3ec3 100644 --- a/WEB-INF/lib/smarty/sysplugins/smarty_internal_parsetree_template.php +++ b/WEB-INF/lib/smarty/sysplugins/smarty_internal_parsetree_template.php @@ -127,12 +127,12 @@ public function to_smarty_php(Smarty_Internal_Templateparser $parser) } private function getChunkedSubtrees() { - $chunks = []; + $chunks = array(); $currentMode = null; - $currentChunk = []; + $currentChunk = array(); for ($key = 0, $cnt = count($this->subtrees); $key < $cnt; $key++) { - if ($this->subtrees[ $key ]->data === '' && in_array($currentMode, ['textstripped', 'text', 'tag'])) { + if ($this->subtrees[ $key ]->data === '' && in_array($currentMode, array('textstripped', 'text', 'tag'))) { continue; } @@ -150,19 +150,19 @@ private function getChunkedSubtrees() { if ($newMode == $currentMode) { $currentChunk[] = $this->subtrees[ $key ]; } else { - $chunks[] = [ + $chunks[] = array( 'mode' => $currentMode, 'subtrees' => $currentChunk - ]; + ); $currentMode = $newMode; - $currentChunk = [$this->subtrees[ $key ]]; + $currentChunk = array($this->subtrees[ $key ]); } } if ($currentMode && $currentChunk) { - $chunks[] = [ + $chunks[] = array( 'mode' => $currentMode, 'subtrees' => $currentChunk - ]; + ); } return $chunks; } diff --git a/WEB-INF/templates/time2.tpl b/WEB-INF/templates/time2.tpl index 487bff7c..65216efb 100644 --- a/WEB-INF/templates/time2.tpl +++ b/WEB-INF/templates/time2.tpl @@ -234,3 +234,5 @@ License: See license.txt *} {/if}
+ +{$smarty.version} diff --git a/initialize.php b/initialize.php index 1aa264cc..771995d8 100644 --- a/initialize.php +++ b/initialize.php @@ -13,7 +13,7 @@ ini_set('display_errors', 'Off'); // require_once('init_auth.php'); -define("APP_VERSION", "1.19.23.5412"); +define("APP_VERSION", "1.19.23.5413"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 85823f48d86f438909af947b57038a1090283030 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Fri, 26 Feb 2021 16:24:51 +0000 Subject: [PATCH 006/432] A minor fix to dbinstall.php to address a migration issue with custom fields. --- dbinstall.php | 4 ++-- initialize.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dbinstall.php b/dbinstall.php index 59d3923f..9960e1ad 100644 --- a/dbinstall.php +++ b/dbinstall.php @@ -691,10 +691,10 @@ function ttGenerateKeys() { print "Updated $clients_updated clients...
\n"; } - // The update_custom_fields function updates option_id field field in tt_custom_field_log table. + // The update_custom_fields function updates option_id field in tt_custom_field_log table. if ($_POST['update_custom_fields']) { $mdb2 = getConnection(); - $sql = "update tt_custom_field_log set option_id = value where field_id in (select id from tt_custom_fields where type = 2)"; + $sql = "update tt_custom_field_log set option_id = value where option_id is null and value is not null and field_id in (select id from tt_custom_fields where type = 2)"; $affected = $mdb2->exec($sql); if (is_a($affected, 'PEAR_Error')) die($affected->getMessage()); diff --git a/initialize.php b/initialize.php index 771995d8..5323d00b 100644 --- a/initialize.php +++ b/initialize.php @@ -13,7 +13,7 @@ ini_set('display_errors', 'Off'); // require_once('init_auth.php'); -define("APP_VERSION", "1.19.23.5413"); +define("APP_VERSION", "1.19.23.5414"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 40f3d9345adc20e6f28eb9f59e2489aff87fecf5 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sat, 27 Feb 2021 16:00:53 +0000 Subject: [PATCH 007/432] Fixed a critical security vulnerability with password resets. --- WEB-INF/templates/report.tpl | 4 ++++ initialize.php | 2 +- password_reset.php | 5 ++++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/WEB-INF/templates/report.tpl b/WEB-INF/templates/report.tpl index f7ef4ff2..0a13ea83 100644 --- a/WEB-INF/templates/report.tpl +++ b/WEB-INF/templates/report.tpl @@ -117,6 +117,7 @@ {if $bean->getAttribute('chtimesheet')}{/if} {if $bean->getAttribute('chfiles')}{/if} {if $use_checkboxes}{/if} + {* {* column for edit icons *}   {/if} @@ -171,6 +172,7 @@ {if 1 == $item.type}{/if} {if 2 == $item.type}{/if} {/if} + {* {$i18n.label.edit}{* column for edit icons *} {if $note_on_separate_row && $bean->getAttribute('chnote') && $item.note} @@ -220,6 +222,7 @@ {if $bean->getAttribute('chtimesheet')}{/if} {if $bean->getAttribute('chfiles')}{/if} {if $use_checkboxes}{/if} + {* {* column for edit icons *} {/if} @@ -261,6 +264,7 @@ {if $bean->getAttribute('chtimesheet')}{/if} {if $bean->getAttribute('chfiles')}{/if} {if $use_checkboxes}{/if} + {* {* column for edit icons *} {/if} diff --git a/initialize.php b/initialize.php index 5323d00b..e2aeb6c8 100644 --- a/initialize.php +++ b/initialize.php @@ -13,7 +13,7 @@ ini_set('display_errors', 'Off'); // require_once('init_auth.php'); -define("APP_VERSION", "1.19.23.5414"); +define("APP_VERSION", "1.19.24.5415"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); diff --git a/password_reset.php b/password_reset.php index 5beafb7f..53099b9a 100644 --- a/password_reset.php +++ b/password_reset.php @@ -46,7 +46,10 @@ if ($err->no()) { // Prepare and save a temporary reference for user. - $temp_ref = md5(uniqid()); + $cryptographically_strong = true; + $random_bytes = openssl_random_pseudo_bytes(16, $cryptographically_strong); + if ($random_bytes === false) die ("openssl_random_pseudo_bytes function call failed..."); + $temp_ref = bin2hex($random_bytes); ttUserHelper::saveTmpRef($temp_ref, $user->id); $user_i18n = null; From 1339c3a043a916cd7f5142c3aec4a956b598f69c Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sat, 27 Feb 2021 16:19:09 +0000 Subject: [PATCH 008/432] Added protection against brute force guessing of random codes used in password resets. --- WEB-INF/lib/ttUserHelper.class.php | 5 +++++ initialize.php | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/WEB-INF/lib/ttUserHelper.class.php b/WEB-INF/lib/ttUserHelper.class.php index eac05730..accabe1f 100644 --- a/WEB-INF/lib/ttUserHelper.class.php +++ b/WEB-INF/lib/ttUserHelper.class.php @@ -82,6 +82,11 @@ static function getUserByEmail($email) { static function getUserIdByTmpRef($ref) { $mdb2 = getConnection(); + // Some protection for brute force attacks to guess a reference for user. + // This limits an available window for brute force guessing to 1 hour. + $sql = "delete from tt_tmp_refs where created < now() - interval 1 hour"; + $affected = $mdb2->exec($sql); + $sql = "select user_id from tt_tmp_refs where ref = ".$mdb2->quote($ref); $res = $mdb2->query($sql); diff --git a/initialize.php b/initialize.php index e2aeb6c8..63215b71 100644 --- a/initialize.php +++ b/initialize.php @@ -13,7 +13,7 @@ ini_set('display_errors', 'Off'); // require_once('init_auth.php'); -define("APP_VERSION", "1.19.24.5415"); +define("APP_VERSION", "1.19.24.5416"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 9a8ee13a037931d29130379c9ad193613b45998c Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sat, 27 Feb 2021 17:35:22 +0000 Subject: [PATCH 009/432] Fixed SQL injection vulnerability in group_edit.php. --- group_edit.php | 6 ++++-- initialize.php | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/group_edit.php b/group_edit.php index c8ad2e14..f60a1133 100644 --- a/group_edit.php +++ b/group_edit.php @@ -32,15 +32,17 @@ import('ttRoleHelper'); import('ttConfigHelper'); +$id = (int)$request->getParameter('id'); + // Access checks. // There are 4 distinct situations: // 1) Editing home group in get or post. // 2) Editing a subgroup in get or post. // We'll check access separately as it is about different right checks. if ($request->isGet()) { - $group_id = $request->getParameter('id') ? $request->getParameter('id') : $user->getGroup(); + $group_id = $request->getParameter('id') ? (int)$request->getParameter('id') : $user->getGroup(); } else { - $group_id = $request->getParameter('group') ? $request->getParameter('group') : $user->getGroup(); + $group_id = $request->getParameter('group') ? (int)$request->getParameter('group') : $user->getGroup(); } $home_group = $user->group_id == $group_id; if ($home_group) { diff --git a/initialize.php b/initialize.php index 63215b71..f069495f 100644 --- a/initialize.php +++ b/initialize.php @@ -13,7 +13,7 @@ ini_set('display_errors', 'Off'); // require_once('init_auth.php'); -define("APP_VERSION", "1.19.24.5416"); +define("APP_VERSION", "1.19.25.5417"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 2b34dc2006121e466513b50015262c6936c876bf Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sat, 27 Feb 2021 17:37:58 +0000 Subject: [PATCH 010/432] Removed garbage introduced in previous commit. --- group_edit.php | 2 -- initialize.php | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/group_edit.php b/group_edit.php index f60a1133..49bd1e61 100644 --- a/group_edit.php +++ b/group_edit.php @@ -32,8 +32,6 @@ import('ttRoleHelper'); import('ttConfigHelper'); -$id = (int)$request->getParameter('id'); - // Access checks. // There are 4 distinct situations: // 1) Editing home group in get or post. diff --git a/initialize.php b/initialize.php index f069495f..7863d0ab 100644 --- a/initialize.php +++ b/initialize.php @@ -13,7 +13,7 @@ ini_set('display_errors', 'Off'); // require_once('init_auth.php'); -define("APP_VERSION", "1.19.25.5417"); +define("APP_VERSION", "1.19.25.5418"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From fdf67755ae8396162b19332e3b10763c9ea274b2 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sat, 27 Feb 2021 17:49:19 +0000 Subject: [PATCH 011/432] More protection against sql injections. --- group_edit.php | 28 ++-------------------------- groups.php | 30 +++--------------------------- initialize.php | 2 +- 3 files changed, 6 insertions(+), 54 deletions(-) diff --git a/group_edit.php b/group_edit.php index 49bd1e61..d972ba38 100644 --- a/group_edit.php +++ b/group_edit.php @@ -1,30 +1,6 @@ isPost()) { - $group_id = $request->getParameter('group'); + $group_id = (int)$request->getParameter('group'); $user->setOnBehalfGroup($group_id); } else { $group_id = $user->getGroup(); diff --git a/initialize.php b/initialize.php index 7863d0ab..40ccda78 100644 --- a/initialize.php +++ b/initialize.php @@ -13,7 +13,7 @@ ini_set('display_errors', 'Off'); // require_once('init_auth.php'); -define("APP_VERSION", "1.19.25.5418"); +define("APP_VERSION", "1.19.25.5419"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 00885b9872c31eb3298b047ee2a27b278248d4c5 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sat, 27 Feb 2021 18:22:24 +0000 Subject: [PATCH 012/432] Added casts to int to sanitize some post parameters. --- admin_group_add.php | 28 ++-------------------------- admin_group_delete.php | 28 ++-------------------------- admin_group_edit.php | 28 ++-------------------------- admin_groups.php | 28 ++-------------------------- admin_options.php | 28 ++-------------------------- cf_custom_field_add.php | 36 +++++------------------------------- initialize.php | 2 +- 7 files changed, 16 insertions(+), 162 deletions(-) diff --git a/admin_group_add.php b/admin_group_add.php index c42644f0..45aa3226 100644 --- a/admin_group_add.php +++ b/admin_group_add.php @@ -1,30 +1,6 @@ isPost()) { $cl_field_name = trim($request->getParameter('name')); - $cl_entity_type = $request->getParameter('entity'); - $cl_field_type = $request->getParameter('type'); - $cl_required = $request->getParameter('required'); - if (!$cl_required) - $cl_required = 0; + $cl_entity_type = (int)$request->getParameter('entity'); + $cl_field_type = (int)$request->getParameter('type'); + $cl_required = (int)$request->getParameter('required'); } $form = new Form('fieldForm'); diff --git a/initialize.php b/initialize.php index 40ccda78..c5edb812 100644 --- a/initialize.php +++ b/initialize.php @@ -13,7 +13,7 @@ ini_set('display_errors', 'Off'); // require_once('init_auth.php'); -define("APP_VERSION", "1.19.25.5419"); +define("APP_VERSION", "1.19.25.5420"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From bc006920353e50e27d815a8f5d529269080db40e Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sat, 27 Feb 2021 18:50:14 +0000 Subject: [PATCH 013/432] More sanitizing of user input. --- cf_custom_field_delete.php | 28 ++-------------------------- cf_custom_field_edit.php | 28 ++-------------------------- cf_custom_fields.php | 28 ++-------------------------- cf_dropdown_option_add.php | 28 ++-------------------------- cf_dropdown_option_delete.php | 30 +++--------------------------- cf_dropdown_option_edit.php | 30 +++--------------------------- cf_dropdown_options.php | 28 ++-------------------------- 7 files changed, 16 insertions(+), 184 deletions(-) diff --git a/cf_custom_field_delete.php b/cf_custom_field_delete.php index c8a13369..ba222c65 100644 --- a/cf_custom_field_delete.php +++ b/cf_custom_field_delete.php @@ -1,30 +1,6 @@ getParameter('id'); +$cl_id = (int)$request->getParameter('id'); $option = CustomFields::getOptionName($cl_id); if (!$option) { header('Location: access_denied.php'); diff --git a/cf_dropdown_option_edit.php b/cf_dropdown_option_edit.php index 4f1103d7..4cea7358 100644 --- a/cf_dropdown_option_edit.php +++ b/cf_dropdown_option_edit.php @@ -1,30 +1,6 @@ getParameter('id'); +$cl_id = (int)$request->getParameter('id'); $cl_name = CustomFields::getOptionName($cl_id); if (!$cl_name) { header('Location: access_denied.php'); diff --git a/cf_dropdown_options.php b/cf_dropdown_options.php index 3b4663f8..06f53a7d 100644 --- a/cf_dropdown_options.php +++ b/cf_dropdown_options.php @@ -1,30 +1,6 @@ Date: Sat, 27 Feb 2021 23:13:24 +0000 Subject: [PATCH 014/432] Added edit icons on reports to allow for quick edits. --- WEB-INF/lib/ttExpenseHelper.class.php | 66 ++++++++++++++++----------- WEB-INF/lib/ttReportHelper.class.php | 14 +++--- WEB-INF/lib/ttTimeHelper.class.php | 66 ++++++++++++++++----------- WEB-INF/lib/ttUserHelper.class.php | 28 +----------- WEB-INF/templates/report.tpl | 13 ++++-- expense_edit.php | 1 + initialize.php | 2 +- time_edit.php | 1 + 8 files changed, 102 insertions(+), 89 deletions(-) diff --git a/WEB-INF/lib/ttExpenseHelper.class.php b/WEB-INF/lib/ttExpenseHelper.class.php index 303ca576..0de7eca1 100644 --- a/WEB-INF/lib/ttExpenseHelper.class.php +++ b/WEB-INF/lib/ttExpenseHelper.class.php @@ -1,30 +1,6 @@ isUserValid($user_id); + + if (!$user_valid) return false; + + // Set on behalf user. + $user->setOnBehalfUser($user_id); + // Get on behalf record. + return ttExpenseHelper::getItem($id); + } + + // getUserForItem - retrieves user id for an expense item. + static function getUserForItem($id) { + global $user; + + $group_id = $user->getGroup(); + $org_id = $user->org_id; + + $mdb2 = getConnection(); + + // Obtain user_id for the expense item. + $sql = "select ei.user_id from tt_expense_items ei ". + " where ei.id = $id and ei.group_id = $group_id and ei.org_id = $org_id and ei.status = 1"; + $res = $mdb2->query($sql); + if (is_a($res, 'PEAR_Error')) return false; + if (!$res->numRows()) return false; + + $val = $res->fetchRow(); + $user_id = $val['user_id']; + return $user_id; + } + // getItemForFileView - retrieves an expense item identified by its id for // attachment view operation. // diff --git a/WEB-INF/lib/ttReportHelper.class.php b/WEB-INF/lib/ttReportHelper.class.php index 294225e0..b24890e7 100644 --- a/WEB-INF/lib/ttReportHelper.class.php +++ b/WEB-INF/lib/ttReportHelper.class.php @@ -350,9 +350,10 @@ static function getItems($options) { array_push($fields, "cast(l.billable * coalesce(upb.rate, 0) * time_to_sec(l.duration)/3600 as decimal(10,2)) as cost"); // Use project rate for user. array_push($fields, "null as expense"); } - // Add approved. - if ($options['show_approved']) - array_push($fields, 'l.approved'); + // Add the fields used to determine if we show an edit icon for record. + array_push($fields, 'l.approved'); + array_push($fields, 'l.timesheet_id'); + array_push($fields, 'l.invoice_id'); // Add paid status. if ($canViewReports && $options['show_paid']) array_push($fields, 'l.paid'); @@ -513,9 +514,10 @@ static function getItems($options) { array_push($fields, 'ei.name as note'); array_push($fields, 'ei.cost as cost'); array_push($fields, 'ei.cost as expense'); - // Add approved. - if ($options['show_approved']) - array_push($fields, 'ei.approved'); + // Add the fields used to determine if we show an edit icon for record. + array_push($fields, 'ei.approved'); + array_push($fields, 'null as timesheet_id'); + array_push($fields, 'ei.invoice_id'); // Add paid status. if ($canViewReports && $options['show_paid']) array_push($fields, 'ei.paid'); diff --git a/WEB-INF/lib/ttTimeHelper.class.php b/WEB-INF/lib/ttTimeHelper.class.php index faa1197c..f0b1ea10 100644 --- a/WEB-INF/lib/ttTimeHelper.class.php +++ b/WEB-INF/lib/ttTimeHelper.class.php @@ -1,30 +1,6 @@ isUserValid($user_id); + + if (!$user_valid) return false; + + // Set on behalf user. + $user->setOnBehalfUser($user_id); + // Get on behalf record. + return ttTimeHelper::getRecord($id); + } + + // getUserForRecord - retrieves user id for a time record. + static function getUserForRecord($id) { + global $user; + + $group_id = $user->getGroup(); + $org_id = $user->org_id; + + $mdb2 = getConnection(); + + // Obtain user_id for the time record. + $sql = "select l.user_id from tt_log l ". + " where l.id = $id and l.group_id = $group_id and l.org_id = $org_id and l.status = 1"; + $res = $mdb2->query($sql); + if (is_a($res, 'PEAR_Error')) return false; + if (!$res->numRows()) return false; + + $val = $res->fetchRow(); + $user_id = $val['user_id']; + return $user_id; + } + // getRecordForFileView - retrieves a time record identified by its id for // attachment view operation. // diff --git a/WEB-INF/lib/ttUserHelper.class.php b/WEB-INF/lib/ttUserHelper.class.php index accabe1f..d4fc8d80 100644 --- a/WEB-INF/lib/ttUserHelper.class.php +++ b/WEB-INF/lib/ttUserHelper.class.php @@ -1,30 +1,6 @@ getAttribute('chtimesheet')}{/if} {if $bean->getAttribute('chfiles')}{/if} {if $use_checkboxes}{/if} - {* {* column for edit icons *} + {* column for edit icons *}   {/if} @@ -172,7 +172,12 @@ {if 1 == $item.type}{/if} {if 2 == $item.type}{/if} {/if} - {* {$i18n.label.edit}{* column for edit icons *} + {if $item.approved || $item.timesheet_id || $item.invoice_id} +   + {else} + {if 1 == $item.type}{$i18n.label.edit}{/if} + {if 2 == $item.type}{$i18n.label.edit}{/if} + {/if} {if $note_on_separate_row && $bean->getAttribute('chnote') && $item.note} @@ -222,7 +227,7 @@ {if $bean->getAttribute('chtimesheet')}{/if} {if $bean->getAttribute('chfiles')}{/if} {if $use_checkboxes}{/if} - {* {* column for edit icons *} + {* column for edit icons *} {/if} @@ -264,7 +269,7 @@ {if $bean->getAttribute('chtimesheet')}{/if} {if $bean->getAttribute('chfiles')}{/if} {if $use_checkboxes}{/if} - {* {* column for edit icons *} + {* column for edit icons *} {/if} diff --git a/expense_edit.php b/expense_edit.php index 092f33c1..5d1e7504 100644 --- a/expense_edit.php +++ b/expense_edit.php @@ -21,6 +21,7 @@ $cl_id = (int)$request->getParameter('id'); // Get the expense item we are editing. $expense_item = ttExpenseHelper::getItem($cl_id); +if (!$expense_item) $expense_item = ttExpenseHelper::getOnBehalfItem($cl_id); if (!$expense_item || $expense_item['approved'] || $expense_item['invoice_id']) { // Prohibit editing not ours, approved, or invoiced items. header('Location: access_denied.php'); diff --git a/initialize.php b/initialize.php index c5edb812..3a08a9e7 100644 --- a/initialize.php +++ b/initialize.php @@ -13,7 +13,7 @@ ini_set('display_errors', 'Off'); // require_once('init_auth.php'); -define("APP_VERSION", "1.19.25.5420"); +define("APP_VERSION", "1.19.26.5421"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); diff --git a/time_edit.php b/time_edit.php index 4065bf08..774add2c 100644 --- a/time_edit.php +++ b/time_edit.php @@ -18,6 +18,7 @@ } $cl_id = (int)$request->getParameter('id'); $time_rec = ttTimeHelper::getRecord($cl_id); +if (!$time_rec) $time_rec = ttTimeHelper::getOnBehalfRecord($cl_id); if (!$time_rec || $time_rec['approved'] || $time_rec['timesheet_id'] || $time_rec['invoice_id']) { // Prohibit editing not ours, approved, assigned to timesheet, or invoiced records. header('Location: access_denied.php'); From 9c9e7d7976fed5b80c0d9dd74b7424f890584fc8 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Fri, 5 Mar 2021 21:06:24 +0000 Subject: [PATCH 015/432] Sanitizing of user input on charts.php. --- WEB-INF/templates/time2.tpl | 2 -- charts.php | 4 ++-- initialize.php | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/WEB-INF/templates/time2.tpl b/WEB-INF/templates/time2.tpl index 65216efb..487bff7c 100644 --- a/WEB-INF/templates/time2.tpl +++ b/WEB-INF/templates/time2.tpl @@ -234,5 +234,3 @@ License: See license.txt *} {/if} - -{$smarty.version} diff --git a/charts.php b/charts.php index d4fb52b3..026f9746 100644 --- a/charts.php +++ b/charts.php @@ -35,7 +35,7 @@ exit(); } if ($request->isPost() && $request->getParameter('user')) { - if (!$user->isUserValid($request->getParameter('user'))) { + if (!$user->isUserValid((int)$request->getParameter('user'))) { header('Location: access_denied.php'); // Wrong user id on post. exit(); } @@ -45,7 +45,7 @@ // Determine user for which we display this page. $userChanged = $request->getParameter('user_changed'); if ($request->isPost() && $userChanged) { - $user_id = $request->getParameter('user'); + $user_id = (int)$request->getParameter('user'); $user->setOnBehalfUser($user_id); } else { $user_id = $user->getUser(); diff --git a/initialize.php b/initialize.php index 3a08a9e7..faa91203 100644 --- a/initialize.php +++ b/initialize.php @@ -13,7 +13,7 @@ ini_set('display_errors', 'Off'); // require_once('init_auth.php'); -define("APP_VERSION", "1.19.26.5421"); +define("APP_VERSION", "1.19.26.5422"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From d0af0f472a64e19dfb48473cca1c374363fb6471 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Fri, 5 Mar 2021 21:24:18 +0000 Subject: [PATCH 016/432] Some more sanitizing of post parameters. --- charts.php | 6 +++--- initialize.php | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/charts.php b/charts.php index 026f9746..9dc29f58 100644 --- a/charts.php +++ b/charts.php @@ -43,7 +43,7 @@ // End of access checks. // Determine user for which we display this page. -$userChanged = $request->getParameter('user_changed'); +$userChanged = (int)$request->getParameter('user_changed'); if ($request->isPost() && $userChanged) { $user_id = (int)$request->getParameter('user'); $user->setOnBehalfUser($user_id); @@ -63,12 +63,12 @@ $_SESSION['date'] = $cl_date; if ($request->isPost()) { - $cl_interval = $request->getParameter('interval'); + $cl_interval = (int)$request->getParameter('interval'); if (!$cl_interval) $cl_interval = INTERVAL_THIS_MONTH; $_SESSION['chart_interval'] = $cl_interval; $uc->setValue(SYSC_CHART_INTERVAL, $cl_interval); - $cl_type = $request->getParameter('type'); + $cl_type = (int)$request->getParameter('type'); if (!$cl_type) $cl_type = ttChartHelper::adjustType($cl_type); $_SESSION['chart_type'] = $cl_type; $uc->setValue(SYSC_CHART_TYPE, $cl_type); diff --git a/initialize.php b/initialize.php index faa91203..f5494137 100644 --- a/initialize.php +++ b/initialize.php @@ -13,7 +13,7 @@ ini_set('display_errors', 'Off'); // require_once('init_auth.php'); -define("APP_VERSION", "1.19.26.5422"); +define("APP_VERSION", "1.19.26.5423"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 2eaab13803846b91c90d49680099968731d4718e Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sat, 6 Mar 2021 15:12:37 +0000 Subject: [PATCH 017/432] A bit more of sanitizing work for posts. --- WEB-INF/lib/ttUser.class.php | 14 ++++++++------ cron.php | 28 ++-------------------------- custom_css.php | 28 ++-------------------------- dbinstall.php | 28 ++-------------------------- display_options.php | 36 ++++++------------------------------ initialize.php | 2 +- 6 files changed, 21 insertions(+), 115 deletions(-) diff --git a/WEB-INF/lib/ttUser.class.php b/WEB-INF/lib/ttUser.class.php index b01ffe68..fcfc1b9d 100644 --- a/WEB-INF/lib/ttUser.class.php +++ b/WEB-INF/lib/ttUser.class.php @@ -617,6 +617,7 @@ function getUserDetails($user_id) { $mdb2 = getConnection(); $group_id = $this->getGroup(); $org_id = $this->org_id; + $uid = (int)$user_id; // Determine max rank. If we are searching in on behalf group // then rank restriction does not apply. @@ -624,7 +625,7 @@ function getUserDetails($user_id) { $sql = "select u.id, u.name, u.login, u.role_id, u.client_id, u.status, u.rate, u.quota_percent, u.email from tt_users u". " left join tt_roles r on (u.role_id = r.id)". - " where u.id = $user_id and u.group_id = $group_id and u.org_id = $org_id and u.status is not null". + " where u.id = $uid and u.group_id = $group_id and u.org_id = $org_id and u.status is not null". " and (r.rank < $max_rank or (r.rank = $max_rank and u.id = $this->id))"; // Users with lesser roles or self. $res = $mdb2->query($sql); if (!is_a($res, 'PEAR_Error')) { @@ -897,6 +898,7 @@ function setOnBehalfGroup($group_id) { // setOnBehalfUser sets on behalf user both the object and the session. function setOnBehalfUser($user_id) { + $uid = (int)$user_id; // In case we forgot to sanitize $user_id before getting here. // Unset things first. $this->behalf_id = null; @@ -906,16 +908,16 @@ function setOnBehalfUser($user_id) { unset($_SESSION['behalf_name']); // No need to set if user is us. - if ($user_id == $this->id) return; + if ($uid == $this->id) return; // No need to set if user id is not valid. - if (!$this->isUserValid($user_id)) return; + if (!$this->isUserValid($uid)) return; // We are good to set on behalf user. - $onBehalfUserName = ttUserHelper::getUserName($user_id); - $_SESSION['behalf_id'] = $user_id; + $onBehalfUserName = ttUserHelper::getUserName($uid); + $_SESSION['behalf_id'] = $uid; $_SESSION['behalf_name'] = $onBehalfUserName; - $this->behalf_id = $user_id; + $this->behalf_id = $uid; $this->behalf_name = $onBehalfUserName; $this->behalfUser = new ttBehalfUser($this->behalf_id, $this->org_id); diff --git a/cron.php b/cron.php index 331b4f36..5adced71 100644 --- a/cron.php +++ b/cron.php @@ -1,30 +1,6 @@ getConfigHelper(); if ($request->isPost()) { - $cl_time_note_on_separate_row = $request->getParameter('time_note_on_separate_row'); - $cl_time_not_complete_days = $request->getParameter('time_not_complete_days'); - $cl_record_custom_fields = $request->getParameter('record_custom_fields'); - $cl_report_note_on_separate_row = $request->getParameter('report_note_on_separate_row'); + $cl_time_note_on_separate_row = (bool)$request->getParameter('time_note_on_separate_row'); + $cl_time_not_complete_days = (bool)$request->getParameter('time_not_complete_days'); + $cl_record_custom_fields = (bool)$request->getParameter('record_custom_fields'); + $cl_report_note_on_separate_row = (bool)$request->getParameter('report_note_on_separate_row'); $cl_custom_css = trim($request->getParameter('custom_css')); } else { $cl_time_note_on_separate_row = $config->getDefinedValue('time_note_on_separate_row'); diff --git a/initialize.php b/initialize.php index f5494137..eb9fd4b9 100644 --- a/initialize.php +++ b/initialize.php @@ -13,7 +13,7 @@ ini_set('display_errors', 'Off'); // require_once('init_auth.php'); -define("APP_VERSION", "1.19.26.5423"); +define("APP_VERSION", "1.19.26.5424"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 477f47a63e8e476f6c84edff830bba03c7610667 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sat, 6 Mar 2021 15:55:04 +0000 Subject: [PATCH 018/432] Some more sanitizing. --- expense_edit.php | 4 ++-- expenses.php | 6 +++--- initialize.php | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/expense_edit.php b/expense_edit.php index 5d1e7504..9c85bdb7 100644 --- a/expense_edit.php +++ b/expense_edit.php @@ -43,14 +43,14 @@ $cl_item_name = trim($request->getParameter('item_name')); $cl_cost = trim($request->getParameter('cost')); if ($user->isPluginEnabled('ps')) - $cl_paid = $request->getParameter('paid'); + $cl_paid = (bool)$request->getParameter('paid'); } else { $cl_date = $item_date->toString($user->getDateFormat()); $cl_client = $expense_item['client_id']; $cl_project = $expense_item['project_id']; $cl_item_name = $expense_item['name']; $cl_cost = $expense_item['cost']; - $cl_paid = $expense_item['paid']; + $cl_paid = (bool)$expense_item['paid']; } // Initialize elements of 'expenseItemForm'. diff --git a/expenses.php b/expenses.php index 170f0cd3..e82d9d37 100644 --- a/expenses.php +++ b/expenses.php @@ -33,7 +33,7 @@ exit(); } if ($request->isPost() && $request->getParameter('user')) { - if (!$user->isUserValid($request->getParameter('user'))) { + if (!$user->isUserValid((int)$request->getParameter('user'))) { header('Location: access_denied.php'); // Wrong user id on post. exit(); } @@ -41,9 +41,9 @@ // End of access checks. // Determine user for which we display this page. -$userChanged = $request->getParameter('user_changed'); +$userChanged = (int)$request->getParameter('user_changed'); if ($request->isPost() && $userChanged) { - $user_id = $request->getParameter('user'); + $user_id = (int)$request->getParameter('user'); $user->setOnBehalfUser($user_id); } else { $user_id = $user->getUser(); diff --git a/initialize.php b/initialize.php index eb9fd4b9..344ec822 100644 --- a/initialize.php +++ b/initialize.php @@ -13,7 +13,7 @@ ini_set('display_errors', 'Off'); // require_once('init_auth.php'); -define("APP_VERSION", "1.19.26.5424"); +define("APP_VERSION", "1.19.26.5425"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 3a3c14ffd3a4266aeac52e7da19d7c0fa684792d Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sat, 6 Mar 2021 17:57:57 +0000 Subject: [PATCH 019/432] A bit more sanitizing of post parameters. --- export.php | 28 ++-------------------------- file_download.php | 28 ++-------------------------- group_add.php | 28 ++-------------------------- group_advanced_edit.php | 28 ++-------------------------- group_delete.php | 28 ++-------------------------- group_edit.php | 12 ++++++------ import.php | 28 ++-------------------------- initialize.php | 2 +- invoice_add.php | 30 +++--------------------------- invoice_delete.php | 28 ++-------------------------- invoice_send.php | 28 ++-------------------------- invoice_view.php | 28 ++-------------------------- invoices.php | 28 ++-------------------------- 13 files changed, 30 insertions(+), 294 deletions(-) diff --git a/export.php b/export.php index 2206def2..2bcba2dc 100644 --- a/export.php +++ b/export.php @@ -1,30 +1,6 @@ getParameter('group_changed'); +$groupChanged = (bool)$request->getParameter('group_changed'); if ($request->isPost() && $groupChanged) { $user->setOnBehalfGroup($group_id); } @@ -62,11 +62,11 @@ $cl_tracking_mode = $request->getParameter('tracking_mode'); $cl_project_required = $request->getParameter('project_required'); $cl_record_type = $request->getParameter('record_type'); - $cl_punch_mode = $request->getParameter('punch_mode'); - $cl_allow_overlap = $request->getParameter('allow_overlap'); - $cl_future_entries = $request->getParameter('future_entries'); - $cl_uncompleted_indicators = $request->getParameter('uncompleted_indicators'); - $cl_confirm_save = $request->getParameter('confirm_save'); + $cl_punch_mode = (bool)$request->getParameter('punch_mode'); + $cl_allow_overlap = (bool)$request->getParameter('allow_overlap'); + $cl_future_entries = (bool)$request->getParameter('future_entries'); + $cl_uncompleted_indicators = (bool)$request->getParameter('uncompleted_indicators'); + $cl_confirm_save = (bool)$request->getParameter('confirm_save'); } else { $cl_currency = ($group['currency'] == '' ? CURRENCY_DEFAULT : $group['currency']); $cl_lang = $group['lang']; diff --git a/import.php b/import.php index 938fdb3d..b743b3f5 100644 --- a/import.php +++ b/import.php @@ -1,30 +1,6 @@ isPost()) { $cl_date = $request->getParameter('date'); - $cl_client = $request->getParameter('client'); + $cl_client = (int)$request->getParameter('client'); $cl_project = $request->getParameter('project'); $cl_number = trim($request->getParameter('number')); $cl_start = $request->getParameter('start'); diff --git a/invoice_delete.php b/invoice_delete.php index 89ea372a..5b473dfa 100644 --- a/invoice_delete.php +++ b/invoice_delete.php @@ -1,30 +1,6 @@ Date: Sat, 6 Mar 2021 21:19:21 +0000 Subject: [PATCH 020/432] Some more sanitizing work. --- locking.php | 28 ++---------------- login.php | 28 ++---------------- logout.php | 28 ++---------------- notification_add.php | 28 ++---------------- notification_delete.php | 28 ++---------------- notification_edit.php | 28 ++---------------- notifications.php | 28 ++---------------- password_change.php | 28 ++---------------- plugins.php | 64 +++++++++++++---------------------------- 9 files changed, 36 insertions(+), 252 deletions(-) diff --git a/locking.php b/locking.php index 9b23c876..04c8d941 100644 --- a/locking.php +++ b/locking.php @@ -1,30 +1,6 @@ doLogout(); diff --git a/notification_add.php b/notification_add.php index b7f74325..83cd7a53 100644 --- a/notification_add.php +++ b/notification_add.php @@ -1,30 +1,6 @@ isPost()) { // Plugins that user wants to save for the current group. - $cl_charts = $request->getParameter('charts'); - $cl_clients = $request->getParameter('clients'); - $cl_client_required = $request->getParameter('client_required'); - $cl_invoices = $request->getParameter('invoices'); - $cl_paid_status = $request->getParameter('paid_status'); - $cl_custom_fields = $request->getParameter('custom_fields'); - $cl_expenses = $request->getParameter('expenses'); - $cl_tax_expenses = $request->getParameter('tax_expenses'); - $cl_notifications = $request->getParameter('notifications'); - $cl_locking = $request->getParameter('locking'); - $cl_quotas = $request->getParameter('quotas'); - $cl_week_view = $request->getParameter('week_view'); - $cl_work_units = $request->getParameter('work_units'); - $cl_approval = $request->getParameter('approval'); - $cl_timesheets = $request->getParameter('timesheets'); - $cl_templates = $request->getParameter('templates'); - $cl_attachments = $request->getParameter('attachments'); - $cl_work = $request->getParameter('work'); + $cl_charts = (bool)$request->getParameter('charts'); + $cl_clients = (bool)$request->getParameter('clients'); + $cl_client_required = (bool)$request->getParameter('client_required'); + $cl_invoices = (bool)$request->getParameter('invoices'); + $cl_paid_status = (bool)$request->getParameter('paid_status'); + $cl_custom_fields = (bool)$request->getParameter('custom_fields'); + $cl_expenses = (bool)$request->getParameter('expenses'); + $cl_tax_expenses = (bool)$request->getParameter('tax_expenses'); + $cl_notifications = (bool)$request->getParameter('notifications'); + $cl_locking = (bool)$request->getParameter('locking'); + $cl_quotas = (bool)$request->getParameter('quotas'); + $cl_week_view = (bool)$request->getParameter('week_view'); + $cl_work_units = (bool)$request->getParameter('work_units'); + $cl_approval = (bool)$request->getParameter('approval'); + $cl_timesheets = (bool)$request->getParameter('timesheets'); + $cl_templates = (bool)$request->getParameter('templates'); + $cl_attachments = (bool)$request->getParameter('attachments'); + $cl_work = (bool)$request->getParameter('work'); } else { // Note: we get here in get, and also in post when group changes. // Which plugins do we have enabled in currently selected group? From 5a9248484ea3248963da71b955449ce6f354580b Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sat, 6 Mar 2021 21:27:05 +0000 Subject: [PATCH 021/432] Some cosmetic refactoring. --- initialize.php | 2 +- predefined_expense_add.php | 28 ++------------------------- predefined_expense_delete.php | 36 ++++++----------------------------- predefined_expense_edit.php | 30 +++-------------------------- predefined_expenses.php | 28 ++------------------------- 5 files changed, 14 insertions(+), 110 deletions(-) diff --git a/initialize.php b/initialize.php index 22d73352..c454c833 100644 --- a/initialize.php +++ b/initialize.php @@ -13,7 +13,7 @@ ini_set('display_errors', 'Off'); // require_once('init_auth.php'); -define("APP_VERSION", "1.19.26.5426"); +define("APP_VERSION", "1.19.26.5427"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); diff --git a/predefined_expense_add.php b/predefined_expense_add.php index 2a0ab6be..e300fa43 100644 --- a/predefined_expense_add.php +++ b/predefined_expense_add.php @@ -1,30 +1,6 @@ getParameter('id'); -$predefined_expense = ttPredefinedExpenseHelper::get($cl_predefined_expense_id); +$predefined_expense_id = (int)$request->getParameter('id'); +$predefined_expense = ttPredefinedExpenseHelper::get($predefined_expense_id); if (!$predefined_expense) { header('Location: access_denied.php'); exit(); @@ -51,13 +27,13 @@ $predefined_expense_to_delete = $predefined_expense['name']; $form = new Form('predefinedExpenseDeleteForm'); -$form->addInput(array('type'=>'hidden','name'=>'id','value'=>$cl_predefined_expense_id)); +$form->addInput(array('type'=>'hidden','name'=>'id','value'=>$predefined_expense_id)); $form->addInput(array('type'=>'submit','name'=>'btn_delete','value'=>$i18n->get('label.delete'))); $form->addInput(array('type'=>'submit','name'=>'btn_cancel','value'=>$i18n->get('button.cancel'))); if ($request->isPost()) { if ($request->getParameter('btn_delete')) { - if (ttPredefinedExpenseHelper::delete($cl_predefined_expense_id)) { + if (ttPredefinedExpenseHelper::delete($predefined_expense_id)) { header('Location: predefined_expenses.php'); exit(); } else diff --git a/predefined_expense_edit.php b/predefined_expense_edit.php index 3f0cb0c8..1eca3a2c 100644 --- a/predefined_expense_edit.php +++ b/predefined_expense_edit.php @@ -1,30 +1,6 @@ getParameter('id'); +$predefined_expense_id = (int)$request->getParameter('id'); $predefined_expense = ttPredefinedExpenseHelper::get($predefined_expense_id); if (!$predefined_expense) { header('Location: access_denied.php'); diff --git a/predefined_expenses.php b/predefined_expenses.php index e1c14d80..9f94bd1c 100644 --- a/predefined_expenses.php +++ b/predefined_expenses.php @@ -1,30 +1,6 @@ Date: Sat, 6 Mar 2021 22:24:50 +0000 Subject: [PATCH 022/432] Cosmetic. --- WEB-INF/lib/ttRoleHelper.class.php | 2 +- initialize.php | 2 +- profile_edit.php | 28 ++------------------------ quotas.php | 28 ++------------------------ report.php | 32 ++++-------------------------- report_send.php | 28 ++------------------------ reports.php | 28 ++------------------------ role_add.php | 28 ++------------------------ role_delete.php | 28 ++------------------------ role_edit.php | 28 ++------------------------ 10 files changed, 20 insertions(+), 212 deletions(-) diff --git a/WEB-INF/lib/ttRoleHelper.class.php b/WEB-INF/lib/ttRoleHelper.class.php index 7fa9f8fb..a3753d23 100644 --- a/WEB-INF/lib/ttRoleHelper.class.php +++ b/WEB-INF/lib/ttRoleHelper.class.php @@ -149,7 +149,7 @@ static function delete($role_id) { $group_id = $user->getGroup(); $org_id = $user->org_id; - // Mark the task as deleted. + // Mark the role as deleted. $sql = "update tt_roles set status = NULL where id = $role_id and group_id = $group_id and org_id = $org_id"; $affected = $mdb2->exec($sql); return (!is_a($affected, 'PEAR_Error')); diff --git a/initialize.php b/initialize.php index c454c833..fc27cbaa 100644 --- a/initialize.php +++ b/initialize.php @@ -13,7 +13,7 @@ ini_set('display_errors', 'Off'); // require_once('init_auth.php'); -define("APP_VERSION", "1.19.26.5427"); +define("APP_VERSION", "1.19.26.5428"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); diff --git a/profile_edit.php b/profile_edit.php index d0d3de5f..26091823 100644 --- a/profile_edit.php +++ b/profile_edit.php @@ -1,30 +1,6 @@ getAttribute('chapproved') && ($user->can('approve_reports') || $user->can('approve_all_reports'))) @@ -199,7 +175,7 @@ $item_ids = ttReportHelper::getFromSession(); $time_log_ids = $item_ids['report_item_ids']; $expense_item_ids = $item_ids['report_item_expense_ids']; - // The above code is here beacues the arrays are used in both "Mark paid" and "Assign to invoice" handlers below. + // The above code is here because the arrays are used in both "Mark paid" and "Assign to invoice" handlers below. } if ($err->no()) { diff --git a/report_send.php b/report_send.php index e837f3e5..a2747b95 100644 --- a/report_send.php +++ b/report_send.php @@ -1,30 +1,6 @@ Date: Sun, 7 Mar 2021 17:35:27 +0000 Subject: [PATCH 023/432] Finished work on sanitizing post parameters to reduce risk of SQL injections. --- initialize.php | 2 +- mobile/expenses.php | 30 +++--------------------------- mobile/index.php | 28 ++-------------------------- mobile/login.php | 28 ++-------------------------- mobile/projects.php | 28 ++-------------------------- mobile/tasks.php | 28 ++-------------------------- mobile/time.php | 34 +++++----------------------------- mobile/timer.php | 28 ++-------------------------- mobile/users.php | 28 ++-------------------------- roles.php | 28 ++-------------------------- site_map.php | 28 ++-------------------------- success.php | 28 ++-------------------------- template_add.php | 28 ++-------------------------- template_delete.php | 30 +++--------------------------- template_edit.php | 30 +++--------------------------- templates.php | 28 ++-------------------------- time.php | 6 +++--- timesheet_add.php | 28 ++-------------------------- timesheet_edit.php | 28 ++-------------------------- timesheet_view.php | 28 ++-------------------------- timesheets.php | 34 +++++----------------------------- tofile.php | 28 ++-------------------------- topdf.php | 28 ++-------------------------- week.php | 34 +++++----------------------------- week_view.php | 36 ++++++------------------------------ work_units.php | 34 +++++----------------------------- 26 files changed, 71 insertions(+), 647 deletions(-) diff --git a/initialize.php b/initialize.php index fc27cbaa..fca2f15a 100644 --- a/initialize.php +++ b/initialize.php @@ -13,7 +13,7 @@ ini_set('display_errors', 'Off'); // require_once('init_auth.php'); -define("APP_VERSION", "1.19.26.5428"); +define("APP_VERSION", "1.19.26.5429"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); diff --git a/mobile/expenses.php b/mobile/expenses.php index 463671f4..4f3b097f 100644 --- a/mobile/expenses.php +++ b/mobile/expenses.php @@ -1,30 +1,6 @@ isPost() && $request->getParameter('user')) { - if (!$user->isUserValid($request->getParameter('user'))) { + if (!$user->isUserValid((int)$request->getParameter('user'))) { header('Location: access_denied.php'); // Wrong user id on post. exit(); } diff --git a/mobile/index.php b/mobile/index.php index 88408af2..4da5fab1 100644 --- a/mobile/index.php +++ b/mobile/index.php @@ -1,30 +1,6 @@ isPost()) { - $userChanged = $request->getParameter('user_changed'); // Reused in multiple places below. - if ($userChanged && !($user->can('track_time') && $user->isUserValid($request->getParameter('user')))) { + $userChanged = (bool)$request->getParameter('user_changed'); // Reused in multiple places below. + if ($userChanged && !($user->can('track_time') && $user->isUserValid((int)$request->getParameter('user')))) { header('Location: access_denied.php'); // User changed, but no right or wrong user id. exit(); } @@ -64,7 +40,7 @@ // Determine user for whom we display this page. if ($request->isPost() && $userChanged) { - $user_id = $request->getParameter('user'); + $user_id = (int)$request->getParameter('user'); $user->setOnBehalfUser($user_id); } else { $user_id = $user->getUser(); diff --git a/mobile/timer.php b/mobile/timer.php index e301716a..1d870b1c 100644 --- a/mobile/timer.php +++ b/mobile/timer.php @@ -1,30 +1,6 @@ getParameter('id'); +$cl_template_id = (int)$request->getParameter('id'); $template = ttTemplateHelper::get($cl_template_id); if (!$template) { header('Location: access_denied.php'); diff --git a/template_edit.php b/template_edit.php index 117b8166..e3fa3894 100644 --- a/template_edit.php +++ b/template_edit.php @@ -1,30 +1,6 @@ getParameter('id'); +$cl_template_id = (int)$request->getParameter('id'); $template = ttTemplateHelper::get($cl_template_id); if (!$template) { header('Location: access_denied.php'); diff --git a/templates.php b/templates.php index 45ea2e47..636d4bc1 100644 --- a/templates.php +++ b/templates.php @@ -1,30 +1,6 @@ isPost()) { - $userChanged = $request->getParameter('user_changed'); // Reused in multiple places below. - if ($userChanged && !($user->can('track_time') && $user->isUserValid($request->getParameter('user')))) { + $userChanged = (bool)$request->getParameter('user_changed'); // Reused in multiple places below. + if ($userChanged && !($user->can('track_time') && $user->isUserValid((int)$request->getParameter('user')))) { header('Location: access_denied.php'); // User changed, but no right or wrong user id. exit(); } @@ -36,7 +36,7 @@ // Determine user for whom we display this page. if ($request->isPost() && $userChanged) { - $user_id = $request->getParameter('user'); + $user_id = (int)$request->getParameter('user'); $user->setOnBehalfUser($user_id); } else { $user_id = $user->getUser(); diff --git a/timesheet_add.php b/timesheet_add.php index 7c7633c6..536241d6 100644 --- a/timesheet_add.php +++ b/timesheet_add.php @@ -1,30 +1,6 @@ isPost()) { - $userChanged = $request->getParameter('user_changed'); // Reused in multiple places below. - if ($userChanged && !($user->can('track_time') && $user->isUserValid($request->getParameter('user')))) { + $userChanged = (bool)$request->getParameter('user_changed'); // Reused in multiple places below. + if ($userChanged && !($user->can('track_time') && $user->isUserValid((int)$request->getParameter('user')))) { header('Location: access_denied.php'); // Group changed, but no rght or wrong user id. exit(); } @@ -59,7 +35,7 @@ // Determine user for whom we display this page. if ($request->isPost() && $userChanged) { - $user_id = $request->getParameter('user'); + $user_id = (int)$request->getParameter('user'); $user->setOnBehalfUser($user_id); } else { $user_id = $user->getUser(); diff --git a/tofile.php b/tofile.php index 996ffe9f..b10bdee0 100644 --- a/tofile.php +++ b/tofile.php @@ -1,30 +1,6 @@ isPost()) { - $userChanged = $request->getParameter('user_changed'); // Reused in multiple places below. - if ($userChanged && !($user->can('track_time') && $user->isUserValid($request->getParameter('user')))) { + $userChanged = (bool)$request->getParameter('user_changed'); // Reused in multiple places below. + if ($userChanged && !($user->can('track_time') && $user->isUserValid((int)$request->getParameter('user')))) { header('Location: access_denied.php'); // User changed, but no right or wrong user id. exit(); } @@ -66,7 +42,7 @@ // Determine user for whom we display this page. if ($request->isPost() && $userChanged) { - $user_id = $request->getParameter('user'); + $user_id = (int)$request->getParameter('user'); $user->setOnBehalfUser($user_id); } else { $user_id = $user->getUser(); diff --git a/week_view.php b/week_view.php index 149842e8..1718afd4 100644 --- a/week_view.php +++ b/week_view.php @@ -1,30 +1,6 @@ getConfigHelper(); if ($request->isPost()) { - $cl_week_menu = $request->getParameter('week_menu'); - $cl_week_note = $request->getParameter('week_note'); - $cl_week_list = $request->getParameter('week_list'); - $cl_notes = $request->getParameter('notes'); + $cl_week_menu = (bool)$request->getParameter('week_menu'); + $cl_week_note = (bool)$request->getParameter('week_note'); + $cl_week_list = (bool)$request->getParameter('week_list'); + $cl_notes = (bool)$request->getParameter('notes'); } else { $cl_week_menu = $config->getDefinedValue('week_menu'); $cl_week_note = $config->getDefinedValue('week_note'); diff --git a/work_units.php b/work_units.php index e4d198d5..826f899a 100644 --- a/work_units.php +++ b/work_units.php @@ -1,30 +1,6 @@ getConfig()); if ($request->isPost()) { - $cl_minutes_in_unit = $request->getParameter('minutes_in_unit'); - $cl_1st_unit_threshold = $request->getParameter('1st_unit_threshold'); - $cl_totals_only = $request->getParameter('totals_only'); + $cl_minutes_in_unit = (int)$request->getParameter('minutes_in_unit'); + $cl_1st_unit_threshold = (int)$request->getParameter('1st_unit_threshold'); + $cl_totals_only = (bool)$request->getParameter('totals_only'); } else { $cl_minutes_in_unit = $user->getConfigInt('minutes_in_unit', 15); $cl_1st_unit_threshold = $user->getConfigInt('1st_unit_threshold', 0); From 464291697900e30ae782477930ef45a9cb8e4cc0 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sun, 7 Mar 2021 18:15:58 +0000 Subject: [PATCH 024/432] Removed some more no longer used mobile pages. --- WEB-INF/templates/mobile/expenses.tpl | 226 ------------------------- WEB-INF/templates/mobile/projects.tpl | 76 --------- WEB-INF/templates/mobile/tasks.tpl | 76 --------- WEB-INF/templates/mobile/users.tpl | 95 ----------- initialize.php | 2 +- mobile/expenses.php | 235 -------------------------- mobile/projects.php | 36 ---- mobile/tasks.php | 35 ---- mobile/users.php | 51 ------ 9 files changed, 1 insertion(+), 831 deletions(-) delete mode 100644 WEB-INF/templates/mobile/expenses.tpl delete mode 100644 WEB-INF/templates/mobile/projects.tpl delete mode 100644 WEB-INF/templates/mobile/tasks.tpl delete mode 100644 WEB-INF/templates/mobile/users.tpl delete mode 100644 mobile/expenses.php delete mode 100644 mobile/projects.php delete mode 100644 mobile/tasks.php delete mode 100644 mobile/users.php diff --git a/WEB-INF/templates/mobile/expenses.tpl b/WEB-INF/templates/mobile/expenses.tpl deleted file mode 100644 index 81d7a214..00000000 --- a/WEB-INF/templates/mobile/expenses.tpl +++ /dev/null @@ -1,226 +0,0 @@ - - - - - - - - - -
<<{$timestring}>>
- -{$forms.expensesForm.open} - - - - - -
- -{if $user_dropdown} - - - - -{/if} -{if $user->isPluginEnabled('cl')} - - - - -{/if} -{if $show_project} - - - - -{/if} -{if $predefined_expenses} - - - - - - - - -{/if} - - - - - - - - -
{$i18n.label.user}:{$forms.expensesForm.user.control}
{$i18n.label.client}{if $user->isOptionEnabled('client_required')} (*){/if}:{$forms.expensesForm.client.control}
{$i18n.label.project} (*):{$forms.expensesForm.project.control}
{$i18n.label.expense}:{$forms.expensesForm.predefined_expense.control}
{$i18n.label.quantity}:{$forms.expensesForm.quantity.control}
{$i18n.label.item} (*):{$forms.expensesForm.item_name.control}
{$i18n.label.cost} (*):{$forms.expensesForm.cost.control} {$user->getCurrency()|escape}
-
- - - - - -
{$forms.expensesForm.btn_submit.control}
- - - - - -
-{if $expense_items} - - - {if $user->isPluginEnabled('cl')} - - {/if} - {if $show_project} - - {/if} - - - - {foreach $expense_items as $item} - - {if $user->isPluginEnabled('cl')} - - {/if} - {if $show_project} - - {/if} - - - - {/foreach} -
{$i18n.label.client}{$i18n.label.project}{$i18n.label.item}{$i18n.label.cost}
{$item.client|escape}{$item.project|escape} - {if $item.approved || $item.invoice_id} - {$item.item|escape} - {else} - {$item.item|escape} - {/if} - {$item.cost}
- - - - -
{$i18n.label.day_total}: {$user->getCurrency()|escape} {$day_total}
-{/if} -
-{$forms.expensesForm.close} diff --git a/WEB-INF/templates/mobile/projects.tpl b/WEB-INF/templates/mobile/projects.tpl deleted file mode 100644 index 96d27054..00000000 --- a/WEB-INF/templates/mobile/projects.tpl +++ /dev/null @@ -1,76 +0,0 @@ - - - - - - -
-{if $user->can('manage_projects')} - - {if $inactive_projects} - - {/if} - {if $active_projects} - - - - - {foreach $active_projects as $project} - - - - - {/foreach} - {/if} -
{$i18n.form.projects.active_projects}
{$i18n.label.thing_name}{$i18n.label.description}
{$project.name|escape}{$project.description|escape}
- - - - - -

-
-
- - {if $inactive_projects} - - - - - - - {foreach $inactive_projects as $project} - - - - - {/foreach} -
{$i18n.form.projects.inactive_projects}
{$i18n.label.thing_name}{$i18n.label.description}
{$project.name|escape}{$project.description|escape}
- - - - - -

-
-
- {/if} -{else} - - - - - - {if $active_projects} - {foreach $active_projects as $project} - - - - - {/foreach} - {/if} -
{$i18n.label.thing_name}{$i18n.label.description}
{$project.name|escape}{$project.description|escape}
-{/if} -
diff --git a/WEB-INF/templates/mobile/tasks.tpl b/WEB-INF/templates/mobile/tasks.tpl deleted file mode 100644 index 3fbee1f7..00000000 --- a/WEB-INF/templates/mobile/tasks.tpl +++ /dev/null @@ -1,76 +0,0 @@ - - - - - - -
-{if $user->can('manage_tasks')} - - {if $inactive_tasks} - - {/if} - - - - - {if $active_tasks} - {foreach $active_tasks as $task} - - - - - {/foreach} - {/if} -
{$i18n.form.tasks.active_tasks}
{$i18n.label.thing_name}{$i18n.label.description}
{$task.name|escape}{$task.description|escape}
- - - - - -

-
-
- - {if $inactive_tasks} - - - - - - - {foreach $inactive_tasks as $task} - - - - - {/foreach} -
{$i18n.form.tasks.inactive_tasks}
{$i18n.label.thing_name}{$i18n.label.description}
{$task.name|escape}{$task.description|escape}
- - - - - -

-
-
- {/if} -{else} - - - - - - {if $active_tasks} - {foreach $active_tasks as $task} - - - - - {/foreach} - {/if} -
{$i18n.label.thing_name}{$i18n.label.description}
{$task.name|escape}{$task.description|escape}
- {/if} -
diff --git a/WEB-INF/templates/mobile/users.tpl b/WEB-INF/templates/mobile/users.tpl deleted file mode 100644 index eaeb4770..00000000 --- a/WEB-INF/templates/mobile/users.tpl +++ /dev/null @@ -1,95 +0,0 @@ - - - - - - -
-{if $user->can('manage_users')} - - {if $inactive_users} - - {/if} - {if $active_users} - - - - - - {foreach $active_users as $u} - - - - - - {/foreach} - {/if} -
{$i18n.label.active_users}
{$i18n.label.person_name}{$i18n.label.login}{$i18n.form.users.role}
- {if $uncompleted_indicators} - - {/if} - {if $u.group_id != $user->group_id || $u.rank < $user->rank || ($u.rank == $user->rank && $u.id == $user->id)} - {$u.name|escape} - {else} - {$u.name|escape} - {/if} - {$u.login|escape}{$u.role_name|escape}
- - - - - -

-
-
- - {if $inactive_users} - - - - - - - - {foreach $inactive_users as $u} - - - - - - {/foreach} -
{$i18n.label.inactive_users}
{$i18n.label.person_name}{$i18n.label.login}{$i18n.form.users.role}
- {if $u.group_id != $user->group_id || $u.rank < $user->rank} - {$u.name|escape} - {else} - {$u.name|escape} - {/if} - {$u.login|escape}{$u.role_name|escape}
- - - - - -
-
-
- {/if} -{else} - - - - - - - {foreach $active_users as $u} - - - - - - {/foreach} -
{$i18n.label.person_name}{$i18n.label.login}{$i18n.form.users.role}
{$u.name|escape}{$u.login|escape}{$u.role_name|escape}
-{/if} -
diff --git a/initialize.php b/initialize.php index fca2f15a..cbdfb3cc 100644 --- a/initialize.php +++ b/initialize.php @@ -13,7 +13,7 @@ ini_set('display_errors', 'Off'); // require_once('init_auth.php'); -define("APP_VERSION", "1.19.26.5429"); +define("APP_VERSION", "1.19.26.5430"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); diff --git a/mobile/expenses.php b/mobile/expenses.php deleted file mode 100644 index 4f3b097f..00000000 --- a/mobile/expenses.php +++ /dev/null @@ -1,235 +0,0 @@ -isPluginEnabled('ex')) { - header('Location: feature_disabled.php'); - exit(); -} -if (!$user->exists()) { - header('Location: access_denied.php'); // Nobody to enter expenses for. - exit(); -} -if ($user->behalf_id && (!$user->can('track_expenses') || !$user->checkBehalfId())) { - header('Location: access_denied.php'); // Trying on behalf, but no right or wrong user. - exit(); -} -if (!$user->behalf_id && !$user->can('track_own_expenses') && !$user->adjustBehalfId()) { - header('Location: access_denied.php'); // Trying as self, but no right for self, and noone to work on behalf. - exit(); -} -if ($request->isPost() && $request->getParameter('user')) { - if (!$user->isUserValid((int)$request->getParameter('user'))) { - header('Location: access_denied.php'); // Wrong user id on post. - exit(); - } -} -// End of access checks. - -// Determine user for which we display this page. -$userChanged = $request->getParameter('user_changed'); -if ($request->isPost() && $userChanged) { - $user_id = $request->getParameter('user'); - $user->setOnBehalfUser($user_id); -} else { - $user_id = $user->getUser(); -} - -// Initialize and store date in session. -$cl_date = $request->getParameter('date', @$_SESSION['date']); -$selected_date = new DateAndTime(DB_DATEFORMAT, $cl_date); -if($selected_date->isError()) - $selected_date = new DateAndTime(DB_DATEFORMAT); -if(!$cl_date) - $cl_date = $selected_date->toString(DB_DATEFORMAT); -$_SESSION['date'] = $cl_date; - -// Determine previous and next dates for simple navigation. -$prev_date = date('Y-m-d', strtotime('-1 day', strtotime($cl_date))); -$next_date = date('Y-m-d', strtotime('+1 day', strtotime($cl_date))); - -$tracking_mode = $user->getTrackingMode(); -$show_project = MODE_PROJECTS == $tracking_mode || MODE_PROJECTS_AND_TASKS == $tracking_mode; - -// Initialize variables. -$cl_client = $request->getParameter('client', ($request->isPost() ? null : @$_SESSION['client'])); -$_SESSION['client'] = $cl_client; -$cl_project = $request->getParameter('project', ($request->isPost() ? null : @$_SESSION['project'])); -$_SESSION['project'] = $cl_project; -$cl_item_name = $request->getParameter('item_name'); -$cl_cost = $request->getParameter('cost'); - -// Elements of expensesForm. -$form = new Form('expensesForm'); - -if ($user->can('track_expenses')) { - $rank = $user->getMaxRankForGroup($user->getGroup()); - if ($user->can('track_own_expenses')) - $options = array('status'=>ACTIVE,'max_rank'=>$rank,'include_self'=>true,'self_first'=>true); - else - $options = array('status'=>ACTIVE,'max_rank'=>$rank); - $user_list = $user->getUsers($options); - if (count($user_list) >= 1) { - $form->addInput(array('type'=>'combobox', - 'onchange'=>'this.form.user_changed.value=1;this.form.submit();', - 'name'=>'user', - 'style'=>'width: 250px;', - 'value'=>$user_id, - 'data'=>$user_list, - 'datakeys'=>array('id','name'))); - $form->addInput(array('type'=>'hidden','name'=>'user_changed')); - $smarty->assign('user_dropdown', 1); - } -} - -// Dropdown for clients in MODE_TIME. Use all active clients. -if (MODE_TIME == $tracking_mode && $user->isPluginEnabled('cl')) { - $active_clients = ttGroupHelper::getActiveClients(true); - $form->addInput(array('type'=>'combobox', - 'onchange'=>'fillProjectDropdown(this.value);', - 'name'=>'client', - 'style'=>'width: 250px;', - 'value'=>$cl_client, - 'data'=>$active_clients, - 'datakeys'=>array('id', 'name'), - 'empty'=>array(''=>$i18n->get('dropdown.select')))); - // Note: in other modes the client list is filtered to relevant clients only. See below. -} - -if ($show_project) { - // Dropdown for projects assigned to user. - $project_list = $user->getAssignedProjects(); - $form->addInput(array('type'=>'combobox', - // 'onchange'=>'fillTaskDropdown(this.value);', - 'name'=>'project', - 'style'=>'width: 250px;', - 'value'=>$cl_project, - 'data'=>$project_list, - 'datakeys'=>array('id','name'), - 'empty'=>array(''=>$i18n->get('dropdown.select')))); - - // Dropdown for clients if the clients plugin is enabled. - if ($user->isPluginEnabled('cl')) { - $active_clients = ttGroupHelper::getActiveClients(true); - // We need an array of assigned project ids to do some trimming. - foreach($project_list as $project) - $projects_assigned_to_user[] = $project['id']; - - // Build a client list out of active clients. Use only clients that are relevant to user. - // Also trim their associated project list to only assigned projects (to user). - foreach($active_clients as $client) { - $projects_assigned_to_client = explode(',', $client['projects']); - $intersection = array_intersect($projects_assigned_to_client, $projects_assigned_to_user); - if ($intersection) { - $client['projects'] = implode(',', $intersection); - $client_list[] = $client; - } - } - $form->addInput(array('type'=>'combobox', - 'onchange'=>'fillProjectDropdown(this.value);', - 'name'=>'client', - 'style'=>'width: 250px;', - 'value'=>$cl_client, - 'data'=>$client_list, - 'datakeys'=>array('id', 'name'), - 'empty'=>array(''=>$i18n->get('dropdown.select')))); - } -} -// If predefined expenses are configured, add controls to select an expense and quantity. -$predefined_expenses = ttGroupHelper::getPredefinedExpenses(); -if ($predefined_expenses) { - $form->addInput(array('type'=>'combobox', - 'onchange'=>'recalculateCost();', - 'name'=>'predefined_expense', - 'style'=>'width: 250px;', - 'value'=>$cl_predefined_expense, - 'data'=>$predefined_expenses, - 'datakeys'=>array('id', 'name'), - 'empty'=>array(''=>$i18n->get('dropdown.select')))); - $form->addInput(array('type'=>'text','onchange'=>'recalculateCost();','maxlength'=>'40','name'=>'quantity','style'=>'width: 100px;','value'=>$cl_quantity)); -} -$form->addInput(array('type'=>'text','maxlength'=>'100','name'=>'item_name','style'=>'width: 250px;','value'=>$cl_item_name)); -$form->addInput(array('type'=>'text','maxlength'=>'40','name'=>'cost','style'=>'width: 100px;','value'=>$cl_cost)); -$form->addInput(array('type'=>'calendar','name'=>'date','highlight'=>'expenses','value'=>$cl_date)); // calendar -$form->addInput(array('type'=>'hidden','name'=>'browser_today','value'=>'')); // User current date, which gets filled in on btn_submit click. -$form->addInput(array('type'=>'submit','name'=>'btn_submit','onclick'=>'browser_today.value=get_date()','value'=>$i18n->get('button.submit'))); - -// Submit. -if ($request->isPost()) { - if ($request->getParameter('btn_submit')) { - // Validate user input. - if ($user->isPluginEnabled('cl') && $user->isOptionEnabled('client_required') && !$cl_client) - $err->add($i18n->get('error.client')); - if ($show_project && !$cl_project) - $err->add($i18n->get('error.project')); - if (!ttValidString($cl_item_name)) $err->add($i18n->get('error.field'), $i18n->get('label.item')); - if (!ttValidFloat($cl_cost)) $err->add($i18n->get('error.field'), $i18n->get('label.cost')); - - // Prohibit creating entries in future. - if (!$user->isOptionEnabled('future_entries')) { - $browser_today = new DateAndTime(DB_DATEFORMAT, $request->getParameter('browser_today', null)); - if ($selected_date->after($browser_today)) - $err->add($i18n->get('error.future_date')); - } - if (!ttTimeHelper::canAdd()) $err->add($i18n->get('error.expired')); - // Finished validating input data. - - // Prohibit creating entries in locked range. - if ($user->isDateLocked($selected_date)) - $err->add($i18n->get('error.range_locked')); - - // Insert record. - if ($err->no()) { - if (ttExpenseHelper::insert(array('date'=>$cl_date,'client_id'=>$cl_client, - 'project_id'=>$cl_project,'name'=>$cl_item_name,'cost'=>$cl_cost,'status'=>1))) { - header('Location: expenses.php'); - exit(); - } else - $err->add($i18n->get('error.db')); - } - } elseif ($request->getParameter('onBehalfUser')) { - if($user->can('track_expenses')) { - unset($_SESSION['behalf_id']); - unset($_SESSION['behalf_name']); - - if($on_behalf_id != $user->id) { - $_SESSION['behalf_id'] = $on_behalf_id; - $_SESSION['behalf_name'] = ttUserHelper::getUserName($on_behalf_id); - } - header('Location: expenses.php'); - exit(); - } - } -} - -$smarty->assign('forms', array($form->getName()=>$form->toArray())); -$smarty->assign('show_project', $show_project); -$smarty->assign('next_date', $next_date); -$smarty->assign('prev_date', $prev_date); -$smarty->assign('day_total', ttExpenseHelper::getTotalForDay($cl_date)); -$smarty->assign('expense_items', ttExpenseHelper::getItems($cl_date)); -$smarty->assign('predefined_expenses', $predefined_expenses); -$smarty->assign('client_list', $client_list); -$smarty->assign('project_list', $project_list); -$smarty->assign('timestring', $selected_date->toString($user->getDateFormat())); -$smarty->assign('title', $i18n->get('title.expenses')); -$smarty->assign('content_page_name', 'mobile/expenses.tpl'); -$smarty->display('mobile/index.tpl'); diff --git a/mobile/projects.php b/mobile/projects.php deleted file mode 100644 index c234d984..00000000 --- a/mobile/projects.php +++ /dev/null @@ -1,36 +0,0 @@ -getTrackingMode() && MODE_PROJECTS_AND_TASKS != $user->getTrackingMode()) { - header('Location: feature_disabled.php'); - exit(); -} -// End of access checks. - -if($user->can('manage_projects')) { - $active_projects = ttGroupHelper::getActiveProjects(); - $inactive_projects = ttGroupHelper::getInactiveProjects(); -} else - $active_projects = $user->getAssignedProjects(); - -$smarty->assign('active_projects', $active_projects); -$smarty->assign('inactive_projects', $inactive_projects); -$smarty->assign('title', $i18n->get('title.projects')); -$smarty->assign('content_page_name', 'mobile/projects.tpl'); -$smarty->display('mobile/index.tpl'); diff --git a/mobile/tasks.php b/mobile/tasks.php deleted file mode 100644 index 98530477..00000000 --- a/mobile/tasks.php +++ /dev/null @@ -1,35 +0,0 @@ -getTrackingMode()) { - header('Location: feature_disabled.php'); - exit(); -} -// End of access checks. - -if($user->can('manage_tasks')) { - $active_tasks = ttGroupHelper::getActiveTasks(); - $inactive_tasks = ttGroupHelper::getInactiveTasks(); -} else - $active_tasks = $user->getAssignedTasks(); - -$smarty->assign('active_tasks', $active_tasks); -$smarty->assign('inactive_tasks', $inactive_tasks); -$smarty->assign('title', $i18n->get('title.tasks')); -$smarty->assign('content_page_name', 'mobile/tasks.tpl'); -$smarty->display('mobile/index.tpl'); diff --git a/mobile/users.php b/mobile/users.php deleted file mode 100644 index 45be3fcd..00000000 --- a/mobile/users.php +++ /dev/null @@ -1,51 +0,0 @@ -getMaxRankForGroup($user->getGroup()); -if ($user->can('view_users')) - $options = array('status'=>ACTIVE,'include_clients'=>true,'include_login'=>true,'include_role'=>true); -else /* if ($user->can('manage_users')) */ - $options = array('status'=>ACTIVE,'max_rank'=>$rank,'include_clients'=>true,'include_self'=>true,'include_login'=>true,'include_role'=>true); -$active_users = $user->getUsers($options); - -// Prepare a list of inactive users. -if($user->can('manage_users')) { - $options = array('status'=>INACTIVE,'max_rank'=>$rank,'include_clients'=>true,'include_login'=>true,'include_role'=>true); - $inactive_users = $user->getUsers($options); -} - -$uncompleted_indicators = $user->getConfigOption('uncompleted_indicators'); -if ($uncompleted_indicators) { - // Check each active user if they have an uncompleted time entry. - foreach ($active_users as $key => $active_user) { - $active_users[$key]['has_uncompleted_entry'] = (bool) ttTimeHelper::getUncompleted($active_user['id']); - } - $smarty->assign('uncompleted_indicators', true); -} - -$smarty->assign('active_users', $active_users); -$smarty->assign('inactive_users', $inactive_users); -$smarty->assign('can_delete_manager', $can_delete_manager); -$smarty->assign('title', $i18n->get('title.users')); -$smarty->assign('content_page_name', 'mobile/users.tpl'); -$smarty->display('mobile/index.tpl'); From e77be7eea69df5d52e19f9f25b5b89a0e66a5b8e Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Sun, 11 Apr 2021 21:57:58 +0000 Subject: [PATCH 025/432] An attempt to mitigate cross site request forgery vulnerability. --- WEB-INF/config.php.dist | 5 +++++ WEB-INF/lib/common.lib.php | 39 ++++++++++++++++++++++++++++++++++++++ initialize.php | 2 +- 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/WEB-INF/config.php.dist b/WEB-INF/config.php.dist index 7b6eabbd..1b7b32b0 100644 --- a/WEB-INF/config.php.dist +++ b/WEB-INF/config.php.dist @@ -163,6 +163,11 @@ define('AUTH_MODULE', 'db'); // define('DEBUG', false); // Note: enabling DEBUG breaks redirects as debug output is printed before setting redirect header. Do not enable on production systems. +// HTTP_TARGET - defines http target for cross site request forgery protection. +// It can be used when you access the application via a proxy. +// define('HTTP_TARGET', 'localhost'); + + // Group managers can set monthly work hour quota for years between the following values. // define('MONTHLY_QUOTA_YEAR_START', 2010); // If nothing is specified, it falls back to 2015. // define('MONTHLY_QUOTA_YEAR_END', 2025); // If nothing is specified, it falls back to 2030. diff --git a/WEB-INF/lib/common.lib.php b/WEB-INF/lib/common.lib.php index 389b29c9..52589c6e 100644 --- a/WEB-INF/lib/common.lib.php +++ b/WEB-INF/lib/common.lib.php @@ -363,6 +363,10 @@ function ttAccessAllowed($required_right) exit(); } + // Protection against cross site request forgery. + if (!ttMitigateCSRF()) + return false; + // Check IP restriction, if set. if ($user->allow_ip && !$user->can('override_allow_ip')) { $access_allowed = false; @@ -388,6 +392,41 @@ function ttAccessAllowed($required_right) return false; } +// ttMitigateCSRF verifies request headers in an attempt to block cross site request forgery. +function ttMitigateCSRF() { + // No need to do anything for get requests. + global $request; + if ($request->isGet()) + return true; + + $origin = $_SERVER['HTTP_ORIGIN']; + if ($origin) { + $pos = strpos($origin, '//'); + $origin = substr($origin, $pos+2); // Strip protocol. + } + if (!$origin) { + // Try using referer. + $origin = $_SERVER['HTTP_REFERER']; + if ($origin) { + $pos = strpos($origin, '//'); + $origin = substr($origin, $pos+2); // Strip protocol. + $pos = strpos($origin, '/'); + $origin = substr($origin, 0, $pos); // Leave host only. + } + } + error_log("origin: ".$origin); + $target = defined('HTTP_TARGET') ? HTTP_TARGET : $_SERVER['HTTP_HOST']; + error_log("target: ".$target); + if (strcmp($origin, $target)) { + error_log("Potential cross site request forgery. Origin: '$origin' does not match target: '$target'."); + return false; // Origin and target do not match, + } + + // TODO: review and improve this function for custom ports. + return true; +} + + // ttStartsWith functions checks if a string starts with a given substring. function ttStartsWith($string, $startString) { diff --git a/initialize.php b/initialize.php index cbdfb3cc..aadc0bb0 100644 --- a/initialize.php +++ b/initialize.php @@ -13,7 +13,7 @@ ini_set('display_errors', 'Off'); // require_once('init_auth.php'); -define("APP_VERSION", "1.19.26.5430"); +define("APP_VERSION", "1.19.27.5431"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From e3f8222ee308322942bcebcd86b78ecf19382563 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Mon, 12 Apr 2021 17:14:30 +0000 Subject: [PATCH 026/432] Tested CSRF fix for custom ports - it's working, also removed unnecessary logging. --- WEB-INF/config.php.dist | 2 +- WEB-INF/lib/common.lib.php | 5 +---- initialize.php | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/WEB-INF/config.php.dist b/WEB-INF/config.php.dist index 1b7b32b0..3787cbc9 100644 --- a/WEB-INF/config.php.dist +++ b/WEB-INF/config.php.dist @@ -165,7 +165,7 @@ define('AUTH_MODULE', 'db'); // HTTP_TARGET - defines http target for cross site request forgery protection. // It can be used when you access the application via a proxy. -// define('HTTP_TARGET', 'localhost'); +// define('HTTP_TARGET', 'localhost:8080'); // Group managers can set monthly work hour quota for years between the following values. diff --git a/WEB-INF/lib/common.lib.php b/WEB-INF/lib/common.lib.php index 52589c6e..d5b43f57 100644 --- a/WEB-INF/lib/common.lib.php +++ b/WEB-INF/lib/common.lib.php @@ -414,15 +414,12 @@ function ttMitigateCSRF() { $origin = substr($origin, 0, $pos); // Leave host only. } } - error_log("origin: ".$origin); $target = defined('HTTP_TARGET') ? HTTP_TARGET : $_SERVER['HTTP_HOST']; - error_log("target: ".$target); if (strcmp($origin, $target)) { error_log("Potential cross site request forgery. Origin: '$origin' does not match target: '$target'."); - return false; // Origin and target do not match, + return false; // Origin and target do not match. } - // TODO: review and improve this function for custom ports. return true; } diff --git a/initialize.php b/initialize.php index aadc0bb0..e8588857 100644 --- a/initialize.php +++ b/initialize.php @@ -13,7 +13,7 @@ ini_set('display_errors', 'Off'); // require_once('init_auth.php'); -define("APP_VERSION", "1.19.27.5431"); +define("APP_VERSION", "1.19.27.5432"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From ee6312e1a278cfbb240ae1419d6c63eeff0913f6 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Tue, 13 Apr 2021 22:12:35 +0000 Subject: [PATCH 027/432] Added options for secure ldap authentication. --- WEB-INF/config.php.dist | 4 ++++ WEB-INF/lib/auth/Auth_ldap.class.php | 10 ++++++++++ initialize.php | 2 +- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/WEB-INF/config.php.dist b/WEB-INF/config.php.dist index 3787cbc9..bbf647ce 100644 --- a/WEB-INF/config.php.dist +++ b/WEB-INF/config.php.dist @@ -144,6 +144,8 @@ define('AUTH_MODULE', 'db'); // 'base_dn' => 'ou=People,dc=example,dc=com', // Path of user's base distinguished name in LDAP catalog. // 'user_login_attribute' => 'uid', // LDAP attribute used for login. // 'default_domain' => 'example.com', // Default domain. +// 'tls_cacertdir' => null, // Path to a directory containing CA certificates for secure ldap. +// 'tls_cacertfile' => null, // CA certificate file name for secure ldap. // 'member_of' => array()); // List of groups, membership in which is required for user to be authenticated. @@ -154,6 +156,8 @@ define('AUTH_MODULE', 'db'); // 'type' => 'ad', // Type of server. // 'base_dn' => 'DC=example,DC=com', // Base distinguished name in LDAP catalog. // 'default_domain' => 'example.com', // Default domain. +// 'tls_cacertdir' => null, // Path to a directory containing CA certificates for secure ldap. +// 'tls_cacertfile' => null, // CA certificate file name for secure ldap. // 'member_of' => array()); // List of groups, membership in which is required for user to be authenticated. // Leave it empty if membership is not necessary. Otherwise list CN parts only. // For example: diff --git a/WEB-INF/lib/auth/Auth_ldap.class.php b/WEB-INF/lib/auth/Auth_ldap.class.php index 7f349573..1ccbb037 100644 --- a/WEB-INF/lib/auth/Auth_ldap.class.php +++ b/WEB-INF/lib/auth/Auth_ldap.class.php @@ -105,6 +105,16 @@ function authenticate($login, $password) if (isTrue('DEBUG')) { ldap_set_option($lc, LDAP_OPT_DEBUG_LEVEL, 7); } + // Additional options for secure ldap. + // This insert is based on https://www.anuko.com/forum/viewtopic.php?f=4&t=2091 + // I can't test it at the moment. If things break please let us know! + if (isset($this->params['tls_cacertdir'])) { + ldap_set_option(null, LDAP_OPT_X_TLS_CACERTDIR, $this->params['tls_cacertdir']); + } + if (isset($this->params['tls_cacertfile'])) { + ldap_set_option(null, LDAP_OPT_X_TLS_CACERTFILE, $this->params['tls_cacertfile']); + } + // End of addiitional options for secure ldap. // We need to handle Windows AD and OpenLDAP differently. if ($this->params['type'] == 'ad') { diff --git a/initialize.php b/initialize.php index e8588857..a739b939 100644 --- a/initialize.php +++ b/initialize.php @@ -13,7 +13,7 @@ ini_set('display_errors', 'Off'); // require_once('init_auth.php'); -define("APP_VERSION", "1.19.27.5432"); +define("APP_VERSION", "1.19.28.5433"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); From 4fdaa18d343d0459391e62b40971843fbd0e5924 Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Mon, 19 Apr 2021 21:26:18 +0000 Subject: [PATCH 028/432] Resuming work on php8 warnings. --- WEB-INF/lib/ttTimeHelper.class.php | 2 ++ WEB-INF/templates/header2.tpl | 2 +- WEB-INF/templates/time2.tpl | 2 +- WEB-INF/templates/time_script.tpl | 16 +++++++++------- initialize.php | 2 +- time.php | 1 + 6 files changed, 15 insertions(+), 10 deletions(-) diff --git a/WEB-INF/lib/ttTimeHelper.class.php b/WEB-INF/lib/ttTimeHelper.class.php index f0b1ea10..af2a436a 100644 --- a/WEB-INF/lib/ttTimeHelper.class.php +++ b/WEB-INF/lib/ttTimeHelper.class.php @@ -895,6 +895,8 @@ static function getRecords($date, $includeFiles = false) { $time_fields = ", ".join(', ', $time_fields_array); } + $filePart = ''; + $fileJoin = ''; if ($includeFiles) { $filePart = ', if(Sub1.entity_id is null, 0, 1) as has_files'; $fileJoin = " left join (select distinct entity_id from tt_files". diff --git a/WEB-INF/templates/header2.tpl b/WEB-INF/templates/header2.tpl index 6bc5f65f..74ccb1ad 100644 --- a/WEB-INF/templates/header2.tpl +++ b/WEB-INF/templates/header2.tpl @@ -6,7 +6,7 @@ -{if $i18n.language.rtl} +{if (isset($i18n.language.rtl) && $i18n.language.rtl)} {/if} {if $user->getCustomCss()} diff --git a/WEB-INF/templates/time2.tpl b/WEB-INF/templates/time2.tpl index 487bff7c..4a26f9be 100644 --- a/WEB-INF/templates/time2.tpl +++ b/WEB-INF/templates/time2.tpl @@ -93,7 +93,7 @@ License: See license.txt *}
{/if} -{if $template_dropdown} +{if (isset($template_dropdown) && $template_dropdown)} diff --git a/WEB-INF/templates/time_script.tpl b/WEB-INF/templates/time_script.tpl index 5834ba3c..67c5062c 100644 --- a/WEB-INF/templates/time_script.tpl +++ b/WEB-INF/templates/time_script.tpl @@ -46,23 +46,25 @@ var task_names = new Array(); // Prepare an array of template ids for projects. var template_ids = new Array(); -{if $bind_templates_with_projects} +{if (isset($bind_templates_with_projects) && $bind_templates_with_projects)} {foreach $project_list as $project} template_ids[{$project.id}] = "{$project.templates}"; {/foreach} {/if} // Prepare an array of template names. var template_names = new Array(); -{if $bind_templates_with_projects} +{if (isset($bind_templates_with_projects) && $bind_templates_with_projects) && isset($template_list)} {foreach $template_list as $template} template_names[{$template.id}] = "{$template.name|escape:'javascript'}"; {/foreach} {/if} // Prepare an array of template bodies. var template_bodies = new Array(); -{foreach $template_list as $template} - template_bodies[{$template.id}] = "{$template.content|escape:'javascript'}"; -{/foreach} +{if isset($template_list)} + {foreach $template_list as $template} + template_bodies[{$template.id}] = "{$template.content|escape:'javascript'}"; + {/foreach} +{/if} // The fillNote function populates the Note field with a selected template body. function fillNote(id) { @@ -189,7 +191,7 @@ function fillTaskDropdown(id) { // The fillTemplateDropdown function populates the template combo box with // templates associated with a selected project (project id is passed here as id). function fillTemplateDropdown(id) { -{if !$bind_templates_with_projects} +{if (!isset($bind_templates_with_projects) || !$bind_templates_with_projects)} return; // Do nothing if we are not binding templates with projects, {/if} @@ -234,7 +236,7 @@ function fillTemplateDropdown(id) { // The prepopulateNote function populates the note field with first found template body in Template dropdown. function prepopulateNote() { - {if !$prepopulate_note} + {if (!isset($prepopulate_note) || !$prepopulate_note)} return; {/if} var dropdown = document.getElementById("template"); diff --git a/initialize.php b/initialize.php index a739b939..d34aa56d 100644 --- a/initialize.php +++ b/initialize.php @@ -13,7 +13,7 @@ ini_set('display_errors', 'Off'); // require_once('init_auth.php'); -define("APP_VERSION", "1.19.28.5433"); +define("APP_VERSION", "1.19.28.5434"); define("APP_DIR", dirname(__FILE__)); define("LIBRARY_DIR", APP_DIR."/WEB-INF/lib"); define("TEMPLATE_DIR", APP_DIR."/WEB-INF/templates"); diff --git a/time.php b/time.php index 66cf4da4..90d54c0c 100644 --- a/time.php +++ b/time.php @@ -225,6 +225,7 @@ $largeScreenCalendarRowSpan += 2; // Client dropdown. + $client_list = array(); if ($showClient) { $active_clients = ttGroupHelper::getActiveClients(true); // We need an array of assigned project ids to do some trimming. From 1ce02deaaa3f2c66162a55fe0b62acb1dd94739a Mon Sep 17 00:00:00 2001 From: Nik Okuntseff Date: Mon, 19 Apr 2021 22:11:54 +0000 Subject: [PATCH 029/432] Addressed a few more php8 warnings. --- WEB-INF/lib/ttTimeHelper.class.php | 4 ++-- WEB-INF/templates/header2.tpl | 8 ++++---- WEB-INF/templates/time_edit2.tpl | 2 +- initialize.php | 2 +- time_edit.php | 2 ++ 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/WEB-INF/lib/ttTimeHelper.class.php b/WEB-INF/lib/ttTimeHelper.class.php index af2a436a..78e7a2cf 100644 --- a/WEB-INF/lib/ttTimeHelper.class.php +++ b/WEB-INF/lib/ttTimeHelper.class.php @@ -424,10 +424,10 @@ static function insert($fields) $client = $fields['client']; $project = $fields['project']; $task = $fields['task']; - $invoice = $fields['invoice']; + $invoice = isset($fields['invoice']) ? $fields['invoice'] : null; $note = $fields['note']; $billable = $fields['billable']; - $paid = $fields['paid']; + $paid = isset($fields['paid']) ? $fields['paid'] : null; $start = ttTimeHelper::to24HourFormat($start); if ($finish) { diff --git a/WEB-INF/templates/header2.tpl b/WEB-INF/templates/header2.tpl index 74ccb1ad..65af554e 100644 --- a/WEB-INF/templates/header2.tpl +++ b/WEB-INF/templates/header2.tpl @@ -21,7 +21,7 @@ - +