diff --git a/build/.phan/plugins/SqlInjectionCheckerPlugin.php b/build/.phan/plugins/SqlInjectionCheckerPlugin.php index a9a0b817d5c01..83c12434363ef 100644 --- a/build/.phan/plugins/SqlInjectionCheckerPlugin.php +++ b/build/.phan/plugins/SqlInjectionCheckerPlugin.php @@ -82,10 +82,10 @@ private function checkQueryBuilderParameters(\ast\Node $node) { if(isset($child->kind) && $child->kind === 128) { if(isset($child->children)) { /** @var \ast\Node $subChild */ - foreach ($child->children as $subChild) { + foreach ($child->children as $subChildIndex => $subChild) { // For set actions if(isset($node->children['method']) && in_array($node->children['method'], $functionsToSearch, true) && !is_string($subChild)) { - if(!isset($subChild->children['method']) || !in_array($subChild->children['method'], $safeFunctions, true)) { + if($subChildIndex > 0 && !isset($subChild->children['method']) || !in_array($subChild->children['method'], $safeFunctions, true)) { $this->throwError('method: ' . ($subChild->children['method'] ?? 'no child method')); } } diff --git a/lib/private/Files/Cache/Cache.php b/lib/private/Files/Cache/Cache.php index 007bccf0a54bb..89729e34cc7ed 100644 --- a/lib/private/Files/Cache/Cache.php +++ b/lib/private/Files/Cache/Cache.php @@ -260,15 +260,10 @@ public function insert($file, array $data) { $data['parent'] = $this->getParentId($file); $data['name'] = basename($file); - list($queryParts, $params) = $this->buildParts($data); - $queryParts[] = '`storage`'; - $params[] = $this->getNumericStorageId(); - - $queryParts = array_map(function ($item) { - return trim($item, "`"); - }, $queryParts); - $values = array_combine($queryParts, $params); - if (\OC::$server->getDatabaseConnection()->insertIfNotExist('*PREFIX*filecache', $values, [ + $params = $this->buildParts($data); + $params['storage'] = $this->getNumericStorageId(); + + if (\OC::$server->getDatabaseConnection()->insertIfNotExist('*PREFIX*filecache', $params, [ 'storage', 'path_hash', ]) @@ -303,35 +298,39 @@ public function update($id, array $data) { $data['name'] = $this->normalize($data['name']); } - list($queryParts, $params) = $this->buildParts($data); - // duplicate $params because we need the parts twice in the SQL statement - // once for the SET part, once in the WHERE clause - $params = array_merge($params, $params); - $params[] = $id; - - // don't update if the data we try to set is the same as the one in the record - // some databases (Postgres) don't like superfluous updates - $sql = 'UPDATE `*PREFIX*filecache` SET ' . implode(' = ?, ', $queryParts) . '=? ' . - 'WHERE (' . - implode(' <> ? OR ', $queryParts) . ' <> ? OR ' . - implode(' IS NULL OR ', $queryParts) . ' IS NULL' . - ') AND `fileid` = ? '; - $this->connection->executeQuery($sql, $params); + $params = $this->buildParts($data); + $keys = array_keys($params); + $query = $this->connection->getQueryBuilder(); + $query->update('filecache'); + foreach ($keys as $key) { + $query->set($key, $query->createParameter($key)); + } + $query->where($query->expr()->eq('fileid', $query->createParameter('fileid'))) + ->andWhere($query->expr()->orX( + $query->expr()->orX(...array_map(function($key) use ($query) { + return $query->expr()->neq($key, $query->createParameter($key)); + }, $keys)), + $query->expr()->orX(...array_map(function($key) use ($query) { + return $query->expr()->isNull($key); + }, $keys)) + )); + $query->setParameters($params); + $query->setParameter('fileid', $id, IQueryBuilder::PARAM_INT); + $query->execute(); } /** * extract query parts and params array from data array * * @param array $data - * @return array [$queryParts, $params] - * $queryParts: string[], the (escaped) column names to be set in the query - * $params: mixed[], the new values for the columns, to be passed as params to the query + * @return array [$key => $value] */ protected function buildParts(array $data) { - $fields = array( + $fields = [ 'path', 'parent', 'name', 'mimetype', 'size', 'mtime', 'storage_mtime', 'encrypted', - 'etag', 'permissions', 'checksum', 'storage'); + 'etag', 'permissions', 'checksum', 'storage', 'content_md5' + ]; $doNotCopyStorageMTime = false; if (array_key_exists('mtime', $data) && $data['mtime'] === null) { @@ -340,21 +339,18 @@ protected function buildParts(array $data) { $doNotCopyStorageMTime = true; } - $params = array(); - $queryParts = array(); + $params = []; foreach ($data as $name => $value) { if (array_search($name, $fields) !== false) { if ($name === 'path') { - $params[] = md5($value); - $queryParts[] = '`path_hash`'; + $params['path_hash'] = md5($value); } elseif ($name === 'mimetype') { - $params[] = $this->mimetypeLoader->getId(substr($value, 0, strpos($value, '/'))); - $queryParts[] = '`mimepart`'; + $mimePart = $this->mimetypeLoader->getId(substr($value, 0, strpos($value, '/'))); + $params['mimepart'] = $mimePart; $value = $this->mimetypeLoader->getId($value); } elseif ($name === 'storage_mtime') { if (!$doNotCopyStorageMTime && !isset($data['mtime'])) { - $params[] = $value; - $queryParts[] = '`mtime`'; + $params['mtime'] =$value; } } elseif ($name === 'encrypted') { if (isset($data['encryptedVersion'])) { @@ -364,11 +360,10 @@ protected function buildParts(array $data) { $value = $value ? 1 : 0; } } - $params[] = $value; - $queryParts[] = '`' . $name . '`'; + $params[$name] = $value; } } - return array($queryParts, $params); + return $params; } /**