From: Tim Otten Date: Fri, 25 Oct 2019 03:59:08 +0000 (-0700) Subject: (security/core#67) Port APIv3's escaping scheme to APIv4 X-Git-Url: https://vcs.fsf.org/?a=commitdiff_plain;h=c9b7a5528406ffd85a5f71087f7800370f558b05;p=civicrm-core.git (security/core#67) Port APIv3's escaping scheme to APIv4 --- diff --git a/Civi/Api4/Generic/Traits/DAOActionTrait.php b/Civi/Api4/Generic/Traits/DAOActionTrait.php index 29e30f86ea..638c8fe83d 100644 --- a/Civi/Api4/Generic/Traits/DAOActionTrait.php +++ b/Civi/Api4/Generic/Traits/DAOActionTrait.php @@ -92,7 +92,11 @@ trait DAOActionTrait { $query->orderBy = $this->getOrderBy(); $query->limit = $this->getLimit(); $query->offset = $this->getOffset(); - return $query->run(); + $result = $query->run(); + if (is_array($result)) { + \CRM_Utils_API_HTMLInputCoder::singleton()->decodeRows($result); + } + return $result; } /** diff --git a/Civi/Api4/Utils/FormattingUtil.php b/Civi/Api4/Utils/FormattingUtil.php index b845ea6608..f848f5ba26 100644 --- a/Civi/Api4/Utils/FormattingUtil.php +++ b/Civi/Api4/Utils/FormattingUtil.php @@ -81,6 +81,8 @@ class FormattingUtil { $params[$name] = 'null'; } } + + \CRM_Utils_API_HTMLInputCoder::singleton()->encodeRow($params); } /** @@ -127,6 +129,11 @@ class FormattingUtil { $value = date('Ymd', strtotime($value)); break; } + + $hic = \CRM_Utils_API_HTMLInputCoder::singleton(); + if (!$hic->isSkippedField($fieldSpec['name'])) { + $value = $hic->encodeValue($value); + } } } diff --git a/tests/phpunit/api/v4/Entity/ContactInterchangeTest.php b/tests/phpunit/api/v4/Entity/ContactInterchangeTest.php new file mode 100644 index 0000000000..2c0b4cad6b --- /dev/null +++ b/tests/phpunit/api/v4/Entity/ContactInterchangeTest.php @@ -0,0 +1,158 @@ + 'Little Bobby :> & :<', 'db' => 'Little Bobby :> & :<'], + ['api' => 'Little Bobby & Bob', 'db' => 'Little Bobby & Bob'], + ['api' => 'It\'s', 'db' => 'It\'s'], + ]; + + $cases = \CRM_Utils_Array::product([ + 'w' => $apiWriters, + 'r' => $apiReaderSets, + 'strs' => $strings, + ]); + foreach ($cases as $case) { + yield [$case['w'], $case['r'], $case['strs']]; + } + } + + /** + * Use the given $writer to create a row with some funny data. + * Then use the given $reader to load the row. + * Assert that the funny data looks the same. + * + * @dataProvider getExamples + */ + public function testReadWriteAPI($writer, $readers, $strs) { + $caseName = sprintf("writer=%s, readers=%s, strs.api=%s", $writer, implode(',', $readers), $strs['api']); + + $cid = call_user_func([$this, $writer], $strs); + + $dbGet = \CRM_Core_DAO::singleValueQuery('SELECT first_name FROM civicrm_contact WHERE id = %1', [ + 1 => [$cid, 'Positive'], + ]); + $this->assertEquals($strs['db'], $dbGet, "Check DB content ($caseName)"); + + $this->assertNotEmpty($readers); + foreach ($readers as $reader) { + $get = call_user_func([$this, $reader], $cid, $strs); + $this->assertEquals($strs['api'], $get, "Check API content ($caseName)"); + } + } + + public function create_3($strs) { + $contact = civicrm_api3('Contact', 'create', [ + 'contact_type' => 'Individual', + 'first_name' => $strs['api'], + ]); + return $contact['id']; + } + + public function create_4($strs) { + $contact = civicrm_api4('Contact', 'create', [ + 'values' => [ + 'contact_type' => 'Individual', + 'first_name' => $strs['api'], + ], + ]); + return $contact->first()['id']; + } + + public function readNameById_3($cid, $strs) { + $get = civicrm_api3('Contact', 'getsingle', [ + 'id' => $cid, + 'return' => 'first_name', + ]); + return $get['first_name']; + } + + public function readNameByValueEq_3($cid, $strs) { + $get = civicrm_api3('Contact', 'getsingle', [ + 'first_name' => $strs['api'], + ]); + return $get['first_name']; + } + + public function readNameById_4($cid, $strs) { + $get = Contact::get() + ->addWhere('id', '=', $cid) + ->addSelect('first_name') + ->execute(); + return $get->first()['first_name']; + } + + public function readNameByValueEq_4($cid, $strs) { + $get = Contact::get() + ->addWhere('first_name', '=', $strs['api']) + ->addSelect('first_name') + ->execute(); + return $get->first()['first_name']; + } + + public function readNameByValueIn_4($cid, $strs) { + $get = Contact::get() + ->addWhere('first_name', 'IN', [$strs['api']]) + ->addSelect('first_name') + ->execute(); + return $get->first()['first_name']; + } + +}