diff --git a/ajax/getAttachmentLocal.php b/ajax/getAttachmentLocal.php deleted file mode 100755 index 5710b9a15..000000000 --- a/ajax/getAttachmentLocal.php +++ /dev/null @@ -1,91 +0,0 @@ -isRequiredIDValid('id')) -{ - $interface->outputXMLErrorPage(-2, 'No attachment ID specified.'); - die(); -} - -$attachmentID = $_POST['id']; - -$attachments = new Attachments(-1); - -$rs = $attachments->get($attachmentID, false); - -if (!isset($rs['directoryName']) || - !isset($rs['storedFilename']) || - md5($rs['directoryName']) != $_POST['directoryNameHash']) -{ - $interface->outputXMLErrorPage(-2, 'Invalid directory name hash.'); - die(); -} - -$directoryName = $rs['directoryName']; -$fileName = $rs['storedFilename']; - -/* Check for the existence of the backup. If it is gone, send the user to a page informing them to press back and generate the backup again. */ -if ($rs['contentType'] == 'catsbackup') -{ - if (!file_exists('attachments/'.$directoryName.'/'.$fileName)) - { - $interface->outputXMLErrorPage(-2, 'The specified backup file no longer exists. Please press back and regenerate the backup before downloading. We are sorry for the inconvenience.'); - die(); - } -} - -$url = 'attachments/'.$directoryName.'/'.$fileName; - -if (!eval(Hooks::get('ATTACHMENT_RETRIEVAL'))) return; - -if (!file_exists('attachments/'.$directoryName.'/'.$fileName)) -{ - $interface->outputXMLErrorPage(-2, 'The file is temporarily unavailable for download. Please try again.'); - die(); -} - -$output = - "\n" . - " 0\n" . - " \n" . - " 1\n" . - "\n"; - -/* Send back the XML data. */ -$interface->outputXMLPage($output); - -?> diff --git a/attachments/.htaccess b/attachments/.htaccess index 8d945254b..bff6cbc7a 100644 --- a/attachments/.htaccess +++ b/attachments/.htaccess @@ -1,8 +1,15 @@ -AddHandler cgi-script .php .php2 .php3 .php4 .php5 .php6 .php7 .php8 .php9 .pl .py .js .jsp .asp .htm .html .$ - Options -ExecCGI -Indexes -#grant access only if files with specific extensions are uploaded - - Require all granted - +# Deny all direct HTTP access to files stored in this directory. +# Attachments must be served through the application (AttachmentsUI) so that authentication and authorization checks are always enforced. + +# For Apache 2.4 and later (using mod_authz_core): deny all requests to this directory. + + Require all denied + + +# For older Apache versions or when mod_authz_core is not available: use the legacy access control syntax to deny all requests to this directory. + + Order deny,allow + Deny from all + diff --git a/js/attachment.js b/js/attachment.js deleted file mode 100755 index 17aaf04d3..000000000 --- a/js/attachment.js +++ /dev/null @@ -1,110 +0,0 @@ -/* - * CATS - * Attachment JavaScript Library - * - * Copyright (C) 2005 - 2007 Cognizo Technologies, Inc. - * - * - * The contents of this file are subject to the CATS Public License - * Version 1.1a (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * http://www.catsone.com/. - * - * Software distributed under the License is distributed on an "AS IS" - * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the - * License for the specific language governing rights and limitations - * under the License. - * - * The Original Code is "CATS Standard Edition". - * - * The Initial Developer of the Original Code is Cognizo Technologies, Inc. - * Portions created by the Initial Developer are Copyright (C) 2005 - 2007 - * (or from the year in which this file was created to the year 2007) by - * Cognizo Technologies, Inc. All Rights Reserved. - * - * - * $Id: attachment.js 3078 2007-09-21 20:25:28Z will $ - */ - -var _spanObject; -var downloadBlock = false; -var downloadBlockUrl = false; -var downloadCancel = false; - - -function doPrepareAndDownload(getVars, url, spanObject, sessionCookie) -{ - if (downloadBlock) - { - if (spanObject != _spanObject) - { - alert ('A file is already being downloaded, please wait...'); - } - return; - } - - downloadBlock = true; - downloadBlockUrl = url; - - spanObject.innerHTML = "
 Preparing Download...
"; - - _spanObject = spanObject; - - var http = AJAX_getXMLHttpObject(); - - /* Build HTTP POST data. */ - var POSTData = '&' + getVars; - - /* Anonymous callback function triggered when HTTP response is received. */ - var callBack = function () - { - if (http.readyState != 4) - { - return; - } - - //alert(http.responseText); - - if (!http.responseXML) - { - var errorMessage = "An error occurred while receiving a response from the server.\n\n" - + http.responseText; - alert(errorMessage); - downloadBlock = false; - return; - } - - /* Return if we have any errors. */ - var errorCodeNode = http.responseXML.getElementsByTagName('errorcode').item(0); - var errorMessageNode = http.responseXML.getElementsByTagName('errormessage').item(0); - if (!errorCodeNode.firstChild || errorCodeNode.firstChild.nodeValue != '0') - { - var errorMessage = "An error occurred while receiving a response from the server.\n\n" - + errorMessageNode.firstChild.nodeValue; - alert(errorMessage); - downloadBlock = false; - return; - } - - if (!downloadCancel) - { - window.location.href = url; - } - - downloadCancel = false; - - setTimeout('downloadBlock = false; if(typeof(_spanObject != "undefined") && typeof(_spanObject.innerHTML != "undefined")) _spanObject.innerHTML = \'\';', 500); - - } - - AJAX_callCATSFunction( - http, - 'getAttachmentLocal', - POSTData, - callBack, - 0, - sessionCookie, - false, - false - ); -} diff --git a/lib/Attachments.php b/lib/Attachments.php index 23b4e6bcc..8f2bf7a11 100755 --- a/lib/Attachments.php +++ b/lib/Attachments.php @@ -955,6 +955,27 @@ public function createFromUpload($dataItemType, $dataItemID, $fileField, return false; } + /* Restrict uploads to a whitelist of allowed file extensions. + * This is a server-side validation which cannot be bypassed by + * manipulating client-side restrictions. + */ + $allowedExtensions = array( + 'bmp', 'csv', 'doc', 'docx', 'heic', + 'jpeg', 'jpg', 'msg', 'odg', 'odt', + 'pages', 'pdf', 'png', 'ppt', 'pptx', + 'rtf', 'tiff', 'wpd', 'wps', 'xls', + 'xlsx', 'xps' + ); + + $extension = FileUtility::getFileExtension($originalFilename); + + if (!in_array($extension, $allowedExtensions, true)) + { + $this->_isError = true; + $this->_error = 'This file type is not allowed for upload.'; + return false; + } + /* This usually indicates an error. */ if ($fileSize <= 0) { diff --git a/lib/FileUtility.php b/lib/FileUtility.php index 7277a9610..584210c03 100755 --- a/lib/FileUtility.php +++ b/lib/FileUtility.php @@ -327,7 +327,15 @@ public static function getFileWithoutExtension($filename, */ public static function getFileExtension($filename) { - return strtolower(substr($filename, strrpos($filename, '.') + 1)); + $lastDotPosition = strrpos($filename, '.'); + + // Treat dotless names and dotfiles as having no extension. + if ($lastDotPosition === false || $lastDotPosition === 0) + { + return ''; + } + + return strtolower(substr($filename, $lastDotPosition + 1)); } /** diff --git a/modules/candidates/CreateImageAttachmentModal.tpl b/modules/candidates/CreateImageAttachmentModal.tpl index 9f2deb326..39c4460b7 100755 --- a/modules/candidates/CreateImageAttachmentModal.tpl +++ b/modules/candidates/CreateImageAttachmentModal.tpl @@ -9,8 +9,8 @@ attachmentsRS as $rowNumber => $attachmentsData): ?>
- - + +
diff --git a/modules/candidates/Questionnaire.tpl b/modules/candidates/Questionnaire.tpl index 1c073102e..ee4a8a656 100755 --- a/modules/candidates/Questionnaire.tpl +++ b/modules/candidates/Questionnaire.tpl @@ -1,5 +1,5 @@ -cData['firstName'].' '.$this->cData['lastName'] . ' Questionnaire', array( 'js/activity.js', 'js/sorttable.js', 'js/match.js', 'js/lib.js', 'js/pipeline.js', 'js/attachment.js')); ?> +cData['firstName'].' '.$this->cData['lastName'] . ' Questionnaire', array( 'js/activity.js', 'js/sorttable.js', 'js/match.js', 'js/lib.js', 'js/pipeline.js')); ?> print): ?> active); ?> diff --git a/modules/candidates/Show.tpl b/modules/candidates/Show.tpl index 92ca84f4e..6e3286b18 100755 --- a/modules/candidates/Show.tpl +++ b/modules/candidates/Show.tpl @@ -4,9 +4,9 @@ use OpenCATS\UI\CandidateQuickActionMenu; use OpenCATS\UI\CandidateDuplicateQuickActionMenu; ?> isPopup): ?> - data['firstName'].' '.$this->data['lastName'], array( 'js/activity.js', 'js/sorttable.js', 'js/match.js', 'js/lib.js', 'js/pipeline.js', 'js/attachment.js', 'modules/candidates/quickAction-candidates.js')); ?> + data['firstName'].' '.$this->data['lastName'], array( 'js/activity.js', 'js/sorttable.js', 'js/match.js', 'js/lib.js', 'js/pipeline.js', 'modules/candidates/quickAction-candidates.js')); ?> - data['firstName'].' '.$this->data['lastName'], array( 'js/activity.js', 'js/sorttable.js', 'js/match.js', 'js/lib.js', 'js/pipeline.js', 'js/attachment.js', 'modules/candidates/quickAction-candidates.js', 'modules/candidates/quickAction-duplicates.js')); ?> + data['firstName'].' '.$this->data['lastName'], array( 'js/activity.js', 'js/sorttable.js', 'js/match.js', 'js/lib.js', 'js/pipeline.js', 'modules/candidates/quickAction-candidates.js', 'modules/candidates/quickAction-duplicates.js')); ?> active); ?> @@ -237,8 +237,8 @@ use OpenCATS\UI\CandidateDuplicateQuickActionMenu; - - + + diff --git a/modules/companies/Show.tpl b/modules/companies/Show.tpl index ff6100d6a..052aa1680 100755 --- a/modules/companies/Show.tpl +++ b/modules/companies/Show.tpl @@ -2,7 +2,7 @@ include_once('./vendor/autoload.php'); use OpenCATS\UI\QuickActionMenu; ?> -data['name'], array( 'js/sorttable.js', 'js/attachment.js')); ?> +data['name'], array( 'js/sorttable.js')); ?> active); ?>
diff --git a/modules/contacts/Show.tpl b/modules/contacts/Show.tpl index 603b3c354..1c61c4899 100755 --- a/modules/contacts/Show.tpl +++ b/modules/contacts/Show.tpl @@ -3,7 +3,7 @@ include_once('./vendor/autoload.php'); use OpenCATS\UI\QuickActionMenu; ?> -data['firstName'].' '.$this->data['lastName'], array( 'js/activity.js', 'js/attachment.js')); ?> +data['firstName'].' '.$this->data['lastName'], array( 'js/activity.js')); ?> active); ?>
diff --git a/modules/joborders/Show.tpl b/modules/joborders/Show.tpl index 3e566aabc..b9ec1daa9 100755 --- a/modules/joborders/Show.tpl +++ b/modules/joborders/Show.tpl @@ -3,9 +3,9 @@ include_once('./vendor/autoload.php'); use OpenCATS\UI\QuickActionMenu; ?> isPopup): ?> - data['title'], array('js/sorttable.js', 'js/match.js', 'js/pipeline.js', 'js/attachment.js')); ?> + data['title'], array('js/sorttable.js', 'js/match.js', 'js/pipeline.js')); ?> - data['title'], array( 'js/sorttable.js', 'js/match.js', 'js/pipeline.js', 'js/attachment.js')); ?> + data['title'], array( 'js/sorttable.js', 'js/match.js', 'js/pipeline.js')); ?> active); ?>
diff --git a/modules/settings/Backup.tpl b/modules/settings/Backup.tpl index 30935f1ba..88f72b906 100755 --- a/modules/settings/Backup.tpl +++ b/modules/settings/Backup.tpl @@ -46,7 +46,7 @@ (_($attachmentsData['fileSize']) ?>)  - + _($attachmentsData['originalFilename']) ?>