...
 
Commits (19)
......@@ -1318,6 +1318,9 @@ SELECT is_primary,
}
}
if (!empty($props['country_id'])) {
if (!CRM_Utils_Rule::commaSeparatedIntegers(implode(',', (array) $props['country_id']))) {
throw new CRM_Core_Exception(ts('Province limit or default country setting is incorrect'));
}
$params['condition'] = 'country_id IN (' . implode(',', (array) $props['country_id']) . ')';
}
break;
......@@ -1330,6 +1333,9 @@ SELECT is_primary,
if ($context != 'get' && $context != 'validate') {
$config = CRM_Core_Config::singleton();
if (!empty($config->countryLimit) && is_array($config->countryLimit)) {
if (!CRM_Utils_Rule::commaSeparatedIntegers(implode(',', $config->countryLimit))) {
throw new CRM_Core_Exception(ts('Available Country setting is incorrect'));
}
$params['condition'] = 'id IN (' . implode(',', $config->countryLimit) . ')';
}
}
......@@ -1338,6 +1344,9 @@ SELECT is_primary,
// Filter county list based on chosen state
case 'county_id':
if (!empty($props['state_province_id'])) {
if (!CRM_Utils_Rule::commaSeparatedIntegers(implode(',', (array) $props['state_province_id']))) {
throw new CRM_Core_Exception(ts('Can only accept Integers for state_province_id filtering'));
}
$params['condition'] = 'state_province_id IN (' . implode(',', (array) $props['state_province_id']) . ')';
}
break;
......
......@@ -599,7 +599,7 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
if (!empty($customDataSubType)) {
$subtypeClause = array();
foreach ($customDataSubType as $subtype) {
$subtype = CRM_Core_DAO::VALUE_SEPARATOR . $subtype . CRM_Core_DAO::VALUE_SEPARATOR;
$subtype = CRM_Core_DAO::VALUE_SEPARATOR . CRM_Utils_Type::escape($subtype, 'String') . CRM_Core_DAO::VALUE_SEPARATOR;
$subtypeClause[] = "$cgTable.extends_entity_column_value LIKE '%{$subtype}%'";
}
if (!$onlySubType) {
......
......@@ -351,6 +351,12 @@ SELECT f.id, f.label, f.data_type,
foreach ($value as $key => $val) {
$value[$key] = str_replace(['[', ']', ','], ['\[', '\]', '[:comma:]'], $val);
$value[$key] = str_replace('|', '[:separator:]', $value[$key]);
if ($field['data_type'] == 'String') {
$value[$key] = CRM_Utils_Type::escape($value[$key], 'String');
}
elseif ($value) {
$value[$key] = CRM_Utils_Type::escape($value[$key], 'Integer');
}
}
$value = implode(',', $value);
}
......
......@@ -68,12 +68,25 @@ class CRM_Core_Page_File extends CRM_Core_Page {
$mimeType = '';
$path = CRM_Core_Config::singleton()->customFileUploadDir . $fileName;
}
$mimeType = CRM_Utils_Request::retrieveValue('mime-type', 'String', $mimeType, FALSE);
if (!$path) {
CRM_Core_Error::statusBounce('Could not retrieve the file');
}
if (empty($mimeType)) {
$passedInMimeType = self::convertBadMimeAliasTypes(CRM_Utils_Request::retrieveValue('mime-type', 'String', $mimeType, FALSE));
if (!in_array($passedInMimeType, explode(',', Civi::settings()->get('requestableMimeTypes')))) {
throw new CRM_Core_Exception("Supplied mime-type is not accepted");
}
$extension = CRM_Utils_File::getExtensionFromPath($path);
$candidateExtensions = CRM_Utils_File::getAcceptableExtensionsForMimeType($passedInMimeType);
if (!in_array($extension, $candidateExtensions)) {
throw new CRM_Core_Exception("Supplied mime-type does not match file extension");
}
// Now that we have validated mime-type supplied as much as possible lets now set the MimeType variable/
$mimeType = $passedInMimeType;
}
$buffer = file_get_contents($path);
if (!$buffer) {
CRM_Core_Error::statusBounce('The file is either empty or you do not have permission to retrieve the file');
......@@ -101,4 +114,33 @@ class CRM_Core_Page_File extends CRM_Core_Page {
}
}
/**
* Translate one mime type to another.
*
* Certain non-standard/weird MIME types have been common. Unfortunately, because
* of the way this controller is used, the weird types may baked-into URLs.
* We clean these up for compatibility.
*
* @param string $type
* Ex: 'image/jpg'
* @return string
* Ex: 'image/jpeg'.
*/
protected static function convertBadMimeAliasTypes($type) {
$badTypes = [
// Before PNG format was ubiquitous, it was image/x-png?
'image/x-png' => 'image/png',
// People see "image/gif" and "image/png" and wrongly guess "image/jpg"?
'image/jpg' => 'image/jpeg',
'image/tif' => 'image/tiff',
'image/svg' => 'image/svg+xml',
// StackExchange attributes "pjpeg" to some quirk in an old version of IE?
'image/pjpeg' => 'image/jpeg',
];
return isset($badTypes[$type]) ? $badTypes[$type] : $type;
}
}
......@@ -520,7 +520,8 @@ ORDER BY start_date desc
if (is_array($value)) {
$type = implode(',', $value);
}
$clauses[] = "event_type_id IN ({$type})";
$clauses[] = "event_type_id IN (%2)";
$params[2] = [$type, 'String'];
}
$eventsByDates = $this->get('eventsByDates');
......
......@@ -102,4 +102,24 @@ class CRM_Utils_AutoClean {
\Civi\Core\Resolver::singleton()->call($this->callback, $this->args);
}
/**
* Prohibit (de)serialization of CRM_Utils_AutoClean.
*
* The generic nature of AutoClean makes it a potential target for escalating
* serialization vulnerabilities, and there's no good reason for serializing it.
*/
public function __sleep() {
throw new \RuntimeException("CRM_Utils_AutoClean is a runtime helper. It is not intended for serialization.");
}
/**
* Prohibit (de)serialization of CRM_Utils_AutoClean.
*
* The generic nature of AutoClean makes it a potential target for escalating
* serialization vulnerabilities, and there's no good reason for deserializing it.
*/
public function __wakeup() {
throw new \RuntimeException("CRM_Utils_AutoClean is a runtime helper. It is not intended for deserialization.");
}
}
......@@ -1066,4 +1066,29 @@ HTACCESS;
return FALSE;
}
/**
* Get the extensions that this MimeTpe is for
* @param string $mimeType the mime-type we want extensions for
* @return array
*/
public static function getAcceptableExtensionsForMimeType($mimeType = NULL) {
$mapping = \MimeType\Mapping::$types;
$extensions = [];
foreach ($mapping as $extension => $type) {
if ($mimeType == $type) {
$extensions[] = $extension;
}
}
return $extensions;
}
/**
* Get the extension of a file based on its path
* @param string $path path of the file to query
* @return string
*/
public static function getExtensionFromPath($path) {
return pathinfo($path, PATHINFO_EXTENSION);
}
}
......@@ -488,6 +488,8 @@ class CRM_Utils_Rule {
*/
public static function commaSeparatedIntegers($value) {
foreach (explode(',', $value) as $val) {
// Remove any Whitespace around the key.
$val = trim($val);
if (!self::positiveInteger($val)) {
return FALSE;
}
......
......@@ -432,7 +432,7 @@ function civicrm_api3_generic_getoptions($apiRequest) {
// Validate 'context' from params
$context = CRM_Utils_Array::value('context', $apiRequest['params']);
CRM_Core_DAO::buildOptionsContext($context);
unset($apiRequest['params']['context'], $apiRequest['params']['field']);
unset($apiRequest['params']['context'], $apiRequest['params']['field'], $apiRequest['params']['condition']);
$baoName = _civicrm_api3_get_BAO($apiRequest['entity']);
$options = $baoName::buildOptions($fieldName, $context, $apiRequest['params']);
......
......@@ -18,7 +18,7 @@
"d3-3.5.x": "d3#~3.5.17",
"dc-2.1.x": "dc.js#~2.1.8",
"crossfilter-1.3.x": "crossfilter2#~1.3.11",
"jquery": "~1.12",
"jquery": "civicrm/jquery#1.12.4-civicrm-1.1",
"jquery-ui": "~1.12",
"lodash-compat": "~3.0",
"google-code-prettify": "~1.0",
......@@ -35,6 +35,7 @@
"es6-promise": "^4.2.4"
},
"resolutions": {
"angular": "~1.5.11"
"angular": "~1.5.11",
"jquery": "1.12.4-civicrm-1.1"
}
}
......@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "93a9f686f7eb00fb9d766d262eedb09b",
"content-hash": "2a06373b9174ae3aa2bfb820e2e5a35e",
"packages": [
{
"name": "civicrm/civicrm-cxn-rpc",
......@@ -454,6 +454,50 @@
],
"time": "2017-03-20T17:10:46+00:00"
},
{
"name": "katzien/php-mime-type",
"version": "2.1.0",
"source": {
"type": "git",
"url": "https://github.com/katzien/PhpMimeType.git",
"reference": "159dfbdcd5906442f3dad89951127f0b9dfa3b78"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/katzien/PhpMimeType/zipball/159dfbdcd5906442f3dad89951127f0b9dfa3b78",
"reference": "159dfbdcd5906442f3dad89951127f0b9dfa3b78",
"shasum": ""
},
"require": {
"php": ">=5.6"
},
"require-dev": {
"phpunit/phpunit": "5.*",
"satooshi/php-coveralls": "1.*"
},
"type": "library",
"autoload": {
"psr-4": {
"MimeType\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Kat Zien"
}
],
"description": "A PHP library to detect the mime type of files.",
"homepage": "https://github.com/katzien/PhpMimeType",
"keywords": [
"mimetype",
"php"
],
"time": "2017-03-23T02:05:33+00:00"
},
{
"name": "marcj/topsort",
"version": "1.1.0",
......@@ -1980,16 +2024,16 @@
},
{
"name": "tecnickcom/tcpdf",
"version": "6.2.13",
"version": "6.2.26",
"source": {
"type": "git",
"url": "https://github.com/tecnickcom/TCPDF.git",
"reference": "95c5938aafe4b20df1454dbddb3e5005c0b26f64"
"reference": "367241059ca166e3a76490f4448c284e0a161f15"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/tecnickcom/TCPDF/zipball/95c5938aafe4b20df1454dbddb3e5005c0b26f64",
"reference": "95c5938aafe4b20df1454dbddb3e5005c0b26f64",
"url": "https://api.github.com/repos/tecnickcom/TCPDF/zipball/367241059ca166e3a76490f4448c284e0a161f15",
"reference": "367241059ca166e3a76490f4448c284e0a161f15",
"shasum": ""
},
"require": {
......@@ -2018,13 +2062,13 @@
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPLv3"
"LGPL-3.0"
],
"authors": [
{
"name": "Nicola Asuni",
"email": "info@tecnick.com",
"homepage": "http://nicolaasuni.tecnick.com"
"role": "lead"
}
],
"description": "TCPDF is a PHP class for generating PDF documents and barcodes.",
......@@ -2038,7 +2082,7 @@
"pdf417",
"qrcode"
],
"time": "2017-04-26T08:14:48+00:00"
"time": "2018-10-16T17:24:05+00:00"
},
{
"name": "totten/ca-config",
......
......@@ -63,14 +63,16 @@ global $installURLPath;
// Set the install type
// this is sent as a query string when the page is first loaded
// and subsequently posted to the page as a hidden field
if (isset($_POST['civicrm_install_type'])) {
// only permit acceptable installation types to prevent issues;
$acceptableInstallTypes = ['drupal', 'wordpress', 'backdrop'];
if (isset($_POST['civicrm_install_type']) && in_array($_POST['civicrm_install_type'], $acceptableInstallTypes)) {
$installType = $_POST['civicrm_install_type'];
}
elseif (isset($_GET['civicrm_install_type'])) {
elseif (isset($_GET['civicrm_install_type']) && in_array(strtolower($_GET['civicrm_install_type']), $acceptableInstallTypes)) {
$installType = strtolower($_GET['civicrm_install_type']);
}
else {
// default value if not set
// default value if not set and not an acceptable install type.
$installType = "drupal";
}
......
......@@ -14,9 +14,15 @@ Other resources for identifying changes are:
* https://github.com/civicrm/civicrm-joomla
* https://github.com/civicrm/civicrm-wordpress
## CiviCRM 5.13.4
Released May 15, 2019
- **[Security advisories](release-notes/5.3.4.md#security)**
## CiviCRM 5.13.3
Released May 13, 2019
Released May 14, 2019
- **[Synopsis](release-notes/5.13.3.md#synopsis)**
- **[Features](release-notes/5.13.3.md#features)**
......
# CiviCRM 5.13.3
Released May 13, 2019
Released May 14, 2019
- **[Synopsis](#synopsis)**
- **[Bugs resolved](#bugs)**
......
# CiviCRM 5.13.4
Released May 15, 2019
- **[Security advisories](#security)**
- **[Features](#features)**
- **[Bugs resolved](#bugs)**
- **[Miscellany](#misc)**
- **[Credits](#credits)**
## <a name="security"></a>Security advisories
- **[CIVI-SA-2019-09](https://civicrm.org/advisory/civi-sa-2019-09-xxe-in-phpword)**: XXE in PHPWord
- **[CIVI-SA-2019-10](https://civicrm.org/advisory/civi-sa-2019-10-tcpdf-xss-and-rce-vulerabilities)**: TCPDF XSS and RCE vulnerabilities
- **[CIVI-SA-2019-11](https://civicrm.org/advisory/civi-sa-2019-11-jquery-objectprototype-pollution)**: jQuery Object.prototype pollution
- **[CIVI-SA-2019-12](https://civicrm.org/advisory/civi-sa-2019-12-sqli-in-country-et-al)**: SQLI in "Country", et al
- **[CIVI-SA-2019-13](https://civicrm.org/advisory/civi-sa-2019-13-harden-against-unserialize-vulnerabilities)**: Harden against unserialize vulnerabilities
- **[CIVI-SA-2019-14](https://civicrm.org/advisory/civi-sa-2019-14-sqli-in-apiv3-getoptions)**: SQLI in APIv3 GetOptions
- **[CIVI-SA-2019-15](https://civicrm.org/advisory/civi-sa-2019-15-xss-via-forged-mime-type)**: XSS via forged MIME type
- **[CIVI-SA-2019-16](https://civicrm.org/advisory/civi-sa-2019-16-sqli-in-certain-checkboxes)**: SQLI in certain checkboxes
- **[CIVI-SA-2019-17](https://civicrm.org/advisory/civi-sa-2019-17-sqli-in-manage-events)**: SQLI in "Manage Events"
- **[CIVI-SA-2019-18](https://civicrm.org/advisory/civi-sa-2019-18-xss-in-civicrm-installer)**: XSS in CiviCRM installer
- **[CIVIEXT-SA-2019-01](https://civicrm.org/advisory/civiext-sa-2019-01-multiple-security-issues-in-apiv4)**: Multiple security issues in APIv4
......@@ -1052,4 +1052,18 @@ return [
'help_text' => NULL,
'validate_callback' => 'CRM_Utils_Rule::color',
],
'requestableMimeTypes' => [
'group_name' => 'CiviCRM Preferences',
'group' => 'core',
'name' => 'requestableMimeTypes',
'type' => 'String',
'html_type' => 'Text',
'default' => 'image/jpeg,image/pjpeg,image/gif,image/x-png,image/png,image/jpg,text/html,application/pdf',
'add' => '5.13',
'title' => ts('Mime Types that can be passed as URL params'),
'is_domain' => 1,
'is_contact' => 0,
'description' => ts('Acceptable Mime Types that can be used as part of file urls'),
'help_text' => NULL,
],
];
......@@ -95,4 +95,39 @@ class CRM_Utils_FileTest extends CiviUnitTestCase {
$this->assertEquals($expectedResult, CRM_Utils_File::isValidFileName($fileName));
}
public function pathToFileExtension() {
$cases = [];
$cases[] = ['/evil.pdf', 'pdf'];
$cases[] = ['/helloworld.jpg', 'jpg'];
$cases[] = ['/smartwatch_1736683_1280_9af3657015e8660cc234eb1601da871.jpg', 'jpg'];
return $cases;
}
/**
* Test returning appropriate file extension
* @dataProvider pathToFileExtension
* @param string $path
* @param string $expectedExtension
*/
public function testPathToExtension($path, $expectedExtension) {
$this->assertEquals($expectedExtension, CRM_Utils_File::getExtensionFromPath($path));
}
public function mimeTypeToExtension() {
$cases = [];
$cases[] = ['text/plain', ['txt', 'text', 'conf', 'def', 'list', 'log', 'in']];
$cases[] = ['image/jpeg', ['jpeg', 'jpg', 'jpe']];
$cases[] = ['image/png', ['png']];
return $cases;
}
/**
* @dataProvider mimeTypeToExtension
* @param stirng $mimeType
* @param array $expectedExtensions
*/
public function testMimeTypeToExtension($mimeType, $expectedExtensions) {
$this->assertEquals($expectedExtensions, CRM_Utils_File::getAcceptableExtensionsForMimeType($mimeType));
}
}
......@@ -479,4 +479,47 @@ class api_v3_AddressTest extends CiviUnitTestCase {
$this->assertEquals($expectState, $created['state_province_id']);
}
public function testBuildStateProvinceOptionsWithDodgyProvinceLimit() {
$provinceLimit = [1228, "abcd;ef"];
$this->callAPISuccess('setting', 'create', [
'provinceLimit' => $provinceLimit,
]);
$result = $this->callAPIFailure('address', 'getoptions', ['field' => 'state_province_id']);
// confirm that we hit our error not a SQLI.
$this->assertEquals('Province limit or default country setting is incorrect', $result['error_message']);
$this->callAPISuccess('setting', 'create', [
'provinceLimit' => [1228],
]);
// Now confirm with a correct province setting it works fine
$this->callAPISuccess('address', 'getoptions', ['field' => 'state_province_id']);
}
public function testBuildCountryWithDodgyCountryLimitSetting() {
$countryLimit = [1228, "abcd;ef"];
$this->callAPISuccess('setting', 'create', [
'countryLimit' => $countryLimit,
]);
$result = $this->callAPIFailure('address', 'getoptions', ['field' => 'country_id']);
// confirm that we hit our error not a SQLI.
$this->assertEquals('Available Country setting is incorrect', $result['error_message']);
$this->callAPISuccess('setting', 'create', [
'countryLimit' => [1228],
]);
// Now confirm with a correct province setting it works fine
$this->callAPISuccess('address', 'getoptions', ['field' => 'country_id']);
}
public function testBuildCountyWithDodgeStateProvinceFiltering() {
$result = $this->callAPIFailure('Address', 'getoptions', [
'field' => 'county_id',
'state_province_id' => "abcd;ef",
]);
$this->assertEquals('Can only accept Integers for state_province_id filtering', $result['error_message']);
$goodResult = $this->callAPISuccess('Address', 'getoptions', [
'field' => 'county_id',
'state_province_id' => 1004,
]);
$this->assertEquals('San Francisco', $goodResult['values'][4]);
}
}