From 3346d8cd90c77e284c9c5abe3b0327695c7006da Mon Sep 17 00:00:00 2001 From: Patrick Barroca <pbarroca@afi-sa.fr> Date: Tue, 19 Feb 2019 17:37:32 +0100 Subject: [PATCH] dev #87205 : fix XSS vulnerabilities in search screen --- VERSIONS_WIP/87205 | 1 + .../opac/controllers/RechercheController.php | 8 +- library/Class/CodifSection.php | 7 +- library/Class/CriteresRecherche.php | 32 ++--- library/Class/CriteresRecherche/Validator.php | 119 ++++++++++++++++++ library/Class/SearchHistory.php | 58 +++++++++ library/Class/Systeme/Sql.php | 27 +++- .../Plugin/Manager/BookmarkedSearches.php | 5 +- .../View/Helper/HistoriqueRecherche.php | 24 ++-- .../View/Helper/TagCriteresRecherche.php | 4 +- .../View/Helper/TagHistoriqueRecherche.php | 73 ++++++----- .../ZendAfi/View/Helper/TagNombreDePages.php | 20 +-- ...ercheControllerHistoriqueRechercheTest.php | 7 +- .../RechercheControllerSimpleSerieTest.php | 3 +- tests/library/Class/MoteurRechercheTest.php | 4 + .../Accueil/HistoriqueRecherchesTest.php | 22 ++-- .../View/Helper/HistoriqueRechercheTest.php | 35 +++--- tests/scenarios/Security/SearchTest.php | 118 +++++++++++++++++ tests/scenarios/bookmarks/SearchTest.php | 9 +- 19 files changed, 450 insertions(+), 126 deletions(-) create mode 100644 VERSIONS_WIP/87205 create mode 100644 library/Class/CriteresRecherche/Validator.php create mode 100644 library/Class/SearchHistory.php create mode 100644 tests/scenarios/Security/SearchTest.php diff --git a/VERSIONS_WIP/87205 b/VERSIONS_WIP/87205 new file mode 100644 index 00000000000..d802abe8235 --- /dev/null +++ b/VERSIONS_WIP/87205 @@ -0,0 +1 @@ + - ticket #87205 : Moteur de recherche : corrections de vulnérabilités \ No newline at end of file diff --git a/application/modules/opac/controllers/RechercheController.php b/application/modules/opac/controllers/RechercheController.php index a3f3eebdf50..5732f13aa00 100644 --- a/application/modules/opac/controllers/RechercheController.php +++ b/application/modules/opac/controllers/RechercheController.php @@ -492,13 +492,7 @@ class RechercheController extends ZendAfi_Controller_Action { protected function addHistoRecherche($criteres_recherche) { $criteres_recherche->setTime(microtime(true)); $criteres_histo = clone $criteres_recherche; - $histo_session = new Zend_Session_Namespace('historiqueRecherche'); - - (null == ($criteres = $histo_session->criteres)) - ? ($criteres = [serialize($criteres_histo)]) - : ($criteres[] = serialize($criteres_histo)); - - return $histo_session->criteres = array_unique($criteres); + return (new Class_SearchHistory())->add($criteres_histo); } diff --git a/library/Class/CodifSection.php b/library/Class/CodifSection.php index 455b21229d8..03cbc5c21dc 100644 --- a/library/Class/CodifSection.php +++ b/library/Class/CodifSection.php @@ -29,7 +29,7 @@ class Class_CodifSectionLoader extends Storm_Model_Loader { public function getMultiOptions() { - $datas = Class_CodifSection::findAllBy(['invisible' => 0, 'order' => 'libelle']); + $datas = Class_CodifSection::findAllVisibleOrderedByLabel(); $items = ['' => $this->_('toutes')]; $controle = ''; @@ -42,6 +42,11 @@ class Class_CodifSectionLoader extends Storm_Model_Loader { return $items; } + + + public function findAllVisibleOrderedByLabel() { + return Class_CodifSection::findAllBy(['invisible' => 0, 'order' => 'libelle']); + } } diff --git a/library/Class/CriteresRecherche.php b/library/Class/CriteresRecherche.php index 5a23c81901d..db21eb84a07 100644 --- a/library/Class/CriteresRecherche.php +++ b/library/Class/CriteresRecherche.php @@ -39,7 +39,8 @@ class Class_CriteresRecherche { $_validate_facette, $_default_page_size = 20, $_profil, - $_time; + $_time, + $_validator; public static $criteres = ['expressionRecherche' => 'Expression', 'rech_titres' => 'Titre', @@ -250,8 +251,8 @@ class Class_CriteresRecherche { } - public function getGeoZone(){ - return $this->getParam('geo_zone',false); + public function getGeoZone() { + return $this->getParam('geo_zone', false); } @@ -290,8 +291,9 @@ class Class_CriteresRecherche { }, $bibs); - if ($geo_zone = $this->getGeoZone()) { - $all_bibs = Class_Zone::find($geo_zone)->getBibs(); + if (($geo_zone = $this->getGeoZone()) + && ($geo_zone = Class_Zone::find($geo_zone))) { + $all_bibs = $geo_zone->getBibs(); $filtres[Class_Bib::CODE_FACETTE] = array_map(function($bib) { return Class_Bib::CODE_FACETTE . $bib->getId(); @@ -685,17 +687,10 @@ class Class_CriteresRecherche { $params = $this->replaceEmptyRechercheByStar($params); - foreach($valid_parameters as $key) { - if (array_isset($key, $params)) { - if (preg_match('/^operateur_/', $key) - && (!array_key_exists(str_replace('operateur_','rech_',$key),$params) - || $params[str_replace('operateur_','rech_',$key)]=='')) { - continue; - } - + foreach($valid_parameters as $key) + if (array_isset($key, $params) + && $this->_getValidator()->isValid($key, $params)) $filtered_params[$key] = $params[$key]; - } - } if (isset($params['page_size']) && (static::MAX_PAGE_SIZE < (int)$params['page_size'])) $filtered_params['page_size'] = static::MAX_PAGE_SIZE; @@ -704,6 +699,13 @@ class Class_CriteresRecherche { } + protected function _getValidator() { + return $this->_validator + ? $this->_validator + : $this->_validator = new Class_CriteresRecherche_Validator(); + } + + public function getCriteres() { return $this->_params; } diff --git a/library/Class/CriteresRecherche/Validator.php b/library/Class/CriteresRecherche/Validator.php new file mode 100644 index 00000000000..086cec4f500 --- /dev/null +++ b/library/Class/CriteresRecherche/Validator.php @@ -0,0 +1,119 @@ +<?php +/** + * Copyright (c) 2012-2019, Agence Française Informatique (AFI). All rights reserved. + * + * BOKEH is free software; you can redistribute it and/or modify + * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by + * the Free Software Foundation. + * + * There are special exceptions to the terms and conditions of the AGPL as it + * is applied to this software (see README file). + * + * BOKEH 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 AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE + * along with BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +class Class_CriteresRecherche_Validator { + const + PATTERN_YEAR = '/^[0-9]{4}$/', + PATTERN_FACET = '/^[a-zA-Z0-9]+$/', + PATTERN_MULTIFACET = '/^[a-zA-Z0-9-]+$/', + PATTERN_MULTI_IDS = '/^[0-9,]+$/', + PATTERN_ALPHAMAJ = '/^[A-Z0-9-]+$/'; + + public function isValid($key, $params) { + return $this->_validatorFor($key)->isValid($key, $params); + } + + + protected function _validatorFor($name) { + if ($validator = $this->_regexValidatorFor($name)) + return $validator; + + if ('operateur_' == substr($name, 0, 10)) + return new Class_CriteresRecherche_ValidatorOperator(); + + if ('section' == $name) { + $ids = (new Storm_Model_Collection(Class_CodifSection::findAllVisibleOrderedByLabel())) + ->collect('id') + ->getArrayCopy(); + + return new Class_CriteresRecherche_ValidatorInArray($ids); + } + + return new Class_CriteresRecherche_ValidatorValid(); + } + + + protected function _regexValidatorFor($name) { + $map = [static::PATTERN_YEAR => ['annee_debut', 'annee_fin'], + static::PATTERN_FACET => ['rubrique', 'code_rebond'], + static::PATTERN_MULTIFACET => ['multifacets'], + static::PATTERN_MULTI_IDS => ['bib_select'], + static::PATTERN_ALPHAMAJ => ['serie'] + ]; + + foreach($map as $pattern => $names) + if (in_array($name, $names)) + return new Class_CriteresRecherche_ValidatorRegex($pattern); + } +} + + + + +class Class_CriteresRecherche_ValidatorValid { + public function isValid($key, $params) { + return true; + } +} + + + +class Class_CriteresRecherche_ValidatorRegex { + protected $_pattern; + + public function __construct($pattern) { + $this->_pattern = $pattern; + } + + + public function isValid($key, $params) { + return preg_match($this->_pattern, $params[$key]); + } +} + + + +class Class_CriteresRecherche_ValidatorInArray { + protected $_possibles = []; + + + public function __construct($possibles) { + $this->_possibles = $possibles; + } + + + public function isValid($key, $params) { + return in_array($params[$key], $this->_possibles); + } +} + + + +class Class_CriteresRecherche_ValidatorOperator { + public function isValid($key, $params) { + $matching_rech = str_replace('operateur_', 'rech_', $key); + + return array_key_exists($matching_rech, $params) + && $params[$matching_rech] + && in_array($params[$key], ['or', 'and', 'and not']); + } +} diff --git a/library/Class/SearchHistory.php b/library/Class/SearchHistory.php new file mode 100644 index 00000000000..73206259022 --- /dev/null +++ b/library/Class/SearchHistory.php @@ -0,0 +1,58 @@ +<?php +/** + * Copyright (c) 2012-2019, Agence Française Informatique (AFI). All rights reserved. + * + * BOKEH is free software; you can redistribute it and/or modify + * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by + * the Free Software Foundation. + * + * There are special exceptions to the terms and conditions of the AGPL as it + * is applied to this software (see README file). + * + * BOKEH 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 AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE + * along with BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +class Class_SearchHistory { + const SESSION_NAMESPACE = 'historiqueRecherche'; + + protected $_session; + + + /** @param $new Class_CriteresRecherche */ + public function add($new) { + $session = $this->_getSession(); + (null == ($history = $session->criteres)) + ? ($history = [serialize($new)]) + : ($history[] = serialize($new)); + + return $session->criteres = array_unique($history); + } + + + public function getHistory() { + return (null == ($history = $this->_getSession()->criteres)) + ? [] + : $history; + } + + + public function clear() { + $session = $this->_getSession(); + unset($session->criteres); + } + + + protected function _getSession() { + return $this->_session + ? $this->_session + : $this->_session = new Zend_Session_Namespace(static::SESSION_NAMESPACE); + } +} diff --git a/library/Class/Systeme/Sql.php b/library/Class/Systeme/Sql.php index 154fbd64e18..90a49f4c21b 100644 --- a/library/Class/Systeme/Sql.php +++ b/library/Class/Systeme/Sql.php @@ -19,10 +19,12 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ class Class_Systeme_Sql { - private $adapter; // Handle de connexion - private $ignore_erreurs=false; // Pour l'intégration les erreurs sont ignorées - private $log; // Instance classe de log - private $filtre_write; // Filtre pour les ecritures + protected static $_throw_errors = false; + + private $adapter; + private $ignore_erreurs = false; + private $log; + private $filtre_write; public function __construct() { @@ -44,10 +46,25 @@ class Class_Systeme_Sql { } + /** @category testing */ + public static function shouldThrowErrors() { + static::$_throw_errors = true; + } + + + /** @category testing */ + public static function shouldNotThrowErrors() { + static::$_throw_errors = false; + } + + protected function run($closure, $req='') { try { return $closure(); } catch(Exception $e) { + if (static::$_throw_errors) + throw $e; + $this->traiteErreur($req, $e); return false; } @@ -182,5 +199,3 @@ class Class_Systeme_Sql { exit; } } - -?> \ No newline at end of file diff --git a/library/ZendAfi/Controller/Plugin/Manager/BookmarkedSearches.php b/library/ZendAfi/Controller/Plugin/Manager/BookmarkedSearches.php index bca75c3c449..2da94758d30 100644 --- a/library/ZendAfi/Controller/Plugin/Manager/BookmarkedSearches.php +++ b/library/ZendAfi/Controller/Plugin/Manager/BookmarkedSearches.php @@ -39,10 +39,7 @@ class ZendAfi_Controller_Plugin_Manager_BookmarkedSearches extends ZendAfi_Contr protected function _getCriterias() { - if (!$namespace = new Zend_Session_Namespace('historiqueRecherche')) - return null; - - if (!$searches = $namespace->criteres) + if (!$searches = (new Class_SearchHistory())->getHistory()) return null; return array_pop($searches); diff --git a/library/ZendAfi/View/Helper/HistoriqueRecherche.php b/library/ZendAfi/View/Helper/HistoriqueRecherche.php index da700948269..6d1777e1c96 100644 --- a/library/ZendAfi/View/Helper/HistoriqueRecherche.php +++ b/library/ZendAfi/View/Helper/HistoriqueRecherche.php @@ -16,23 +16,21 @@ * * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE * along with BOKEH; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + class ZendAfi_View_Helper_HistoriqueRecherche extends ZendAfi_View_Helper_BaseHelper { public function historiqueRecherche() { - $html=''; + if (!$history = (new Class_SearchHistory())->getHistory()) + return ''; - $this->_session = new Zend_Session_Namespace('historiqueRecherche'); - if (!$this->_session->criteres) - return $html; - - $criteres_recherche = array_reverse($this->_session->criteres); - - foreach($criteres_recherche as $critere) - $html.=$this->view->tagHistoriqueRecherche(unserialize($critere)).""; + $history = array_reverse($history); + $html = ''; + foreach($history as $critere) + $html .= $this->view->tagHistoriqueRecherche(unserialize($critere)); - return "<div class='criteres_recherche'>" - ."<ul>".$html."</ul>" - ."</div>"; + return $this->_tag('div', + $this->_tag('ul', $html), + ['class' => 'criteres_recherche']); } } \ No newline at end of file diff --git a/library/ZendAfi/View/Helper/TagCriteresRecherche.php b/library/ZendAfi/View/Helper/TagCriteresRecherche.php index bad61c909e0..9b30ff62f34 100644 --- a/library/ZendAfi/View/Helper/TagCriteresRecherche.php +++ b/library/ZendAfi/View/Helper/TagCriteresRecherche.php @@ -133,11 +133,11 @@ class ZendAfi_View_Helper_TagCriteresRecherche extends ZendAfi_View_Helper_BaseH } - public function getSuppressionImgUrlForLibelle($libelle,$url) { + public function getSuppressionImgUrlForLibelle($libelle, $url) { unset($url['page']); $title = $this->view->_('Retirer le critère: %s', $libelle); return $this->view->tagAnchor($this->view->url($url, null, true), - $libelle . + $this->view->escape($libelle) . $this->view->tagImg(URL_IMG . 'suppression.gif'), ['title' => $title]); } diff --git a/library/ZendAfi/View/Helper/TagHistoriqueRecherche.php b/library/ZendAfi/View/Helper/TagHistoriqueRecherche.php index c65aff8c66c..b7ae528371c 100644 --- a/library/ZendAfi/View/Helper/TagHistoriqueRecherche.php +++ b/library/ZendAfi/View/Helper/TagHistoriqueRecherche.php @@ -18,6 +18,7 @@ * along with BOKEH; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + class ZendAfi_View_Helper_TagHistoriqueRecherche extends ZendAfi_View_Helper_TagCriteresRecherche { protected $_codification, @@ -25,46 +26,49 @@ class ZendAfi_View_Helper_TagHistoriqueRecherche extends ZendAfi_View_Helper_Tag $_expression; public function tagHistoriqueRecherche($criteres_recherche) { + if (!$criteres_recherche) + return ''; + $this->_html=''; $this->_affichage_criteres = ''; - $this->_expression = $this->view->_('Tous les documents'); + $this->_expression = $this->_('Tous les documents'); $this->_codification = Class_Codification::getInstance(); parent::visitCriteresRecherche($criteres_recherche); parent::_injectMultiFacets(); $this->_insertTime($criteres_recherche); + $url = $this->view->url($this->_criteres_recherche->getUrlCriteresWithFacettes(),null,true); + $icon = Class_Admin_Skin::current() + ->renderActionIconOn('loupe', $this->view, + ['alt' => $this->_('Relancer cette Recherche')]); - $this->_html = - '<a href="'.$url.'" title="'.$this->view->_('Relancer cette recherche').'">' - . Class_Admin_Skin::current()->renderActionIconOn('loupe', $this->view, - ['alt' => $this->view->_('Relancer cette Recherche')]) - . $this->_expression . '</a>'; + $this->_html = $this->view + ->tagAnchor($url, $icon . $this->view->escape($this->_expression), + ['title' => $this->_('Relancer cette recherche')]); if ($this->_affichage_criteres) - $this->_html .='<ul>'.$this->_affichage_criteres.'</ul>'; + $this->_html .= $this->_tag('ul', $this->_affichage_criteres); - return '<li>'.$this->_html.'</li>'; + return $this->_tag('li', $this->_html); } - public function visitRubrique($rubrique,$fil) { + public function visitRubrique($rubrique, $fil) { $libelle = $this->_codification->getLibelleFacette($rubrique); - $this->htmlAppend($this->view->_('Rubrique: ').$libelle); + $this->htmlAppend($this->_('Rubrique: ') . $libelle); } public function visitTextInput($name, $operateur, $type_recherche, $value) { - if (!$operateur) - $libelle_operateur = " et "; - else - $libelle_operateur = $this->_libelles_operateur[$operateur]; - - $this->htmlAppend( - $libelle_operateur. - $this->_libelles_criteres[$name]. - ': '. - $value); + $libelle_operateur = ($operateur && array_key_exists($operateur, $this->_libelles_operateur)) + ? $this->_libelles_operateur[$operateur] + : $this->_(' et '); + + $this->htmlAppend($libelle_operateur + . $this->_libelles_criteres[$name] + . ': ' + . $value); } @@ -73,28 +77,29 @@ class ZendAfi_View_Helper_TagHistoriqueRecherche extends ZendAfi_View_Helper_Tag return; $libelle = $libelle_facette . ': ' . $this->_codification->getLibelleFacette($facette); - $this->htmlAppend('Facette: '.$libelle); + $this->htmlAppend($this->_('Facette: ') . $libelle); } public function visitPage($page) { - $this->htmlAppend($this->view->_('Page: ').$page); + $this->htmlAppend($this->_('Page: ') . $page); } public function visitTri($tri, $libelle) { - $this->htmlAppend($this->view->_('Trié par: ').$libelle); + $this->htmlAppend($this->_('Trié par: ') . $libelle); } - public function getSuppressionImgUrlForLibelle($libelle,$url) { + public function getSuppressionImgUrlForLibelle($libelle, $url) { return $libelle; } public function visitTypeDoc($type_docs) { $type_doc = array_shift($type_docs); - $this->htmlAppend('Type de document: ' . $this->_codification->getLibelleFacette('T' . $type_doc)); + $this->htmlAppend($this->_('Type de document: ') + . $this->_codification->getLibelleFacette('T' . $type_doc)); } @@ -105,25 +110,27 @@ class ZendAfi_View_Helper_TagHistoriqueRecherche extends ZendAfi_View_Helper_Tag public function visitNouveaute($nouveaute) { - $url=$this->_criteres_recherche->getUrlCriteresWithoutElement('nouveaute'); + $url = $this->_criteres_recherche->getUrlCriteresWithoutElement('nouveaute'); $libelle = ($nouveaute == 0) - ? $this->view->_('Nouveautés') - : $this->view->_('Nouveautés de moins de: ').$nouveaute.' mois'; + ? $this->_('Nouveautés') + : $this->_('Nouveautés de moins de: %s mois', $nouveaute); + $this->htmlAppend($libelle); } public function visitAnneeDebutFin($annee_debut, $annee_fin) { - $texte = $this->view->_("Documents parus "); + $texte = $this->_('Documents parus '); $texte .= ($annee_debut == $annee_fin) - ? "en " . $annee_debut - : $this->view->_("entre %s et %s", $annee_debut, $annee_fin); + ? $this->_('en %s', $annee_debut) + : $this->_('entre %s et %s', $annee_debut, $annee_fin); + $this->htmlAppend($texte); } public function htmlAppend($text, $attribs=[]) { - $this->_affichage_criteres .= $this->_tag('li', $text); + $this->_affichage_criteres .= $this->_tag('li', $this->view->escape($text)); return $this; } @@ -132,7 +139,7 @@ class ZendAfi_View_Helper_TagHistoriqueRecherche extends ZendAfi_View_Helper_Tag if(!$criteres_recherche->getTime()) return; - $this->htmlAppend($this->_('Date : %s', date('d/m/Y H:i:s',$criteres_recherche->getTime()))); + $this->htmlAppend($this->_('Date : %s', date('d/m/Y H:i:s', $criteres_recherche->getTime()))); return $this; } } \ No newline at end of file diff --git a/library/ZendAfi/View/Helper/TagNombreDePages.php b/library/ZendAfi/View/Helper/TagNombreDePages.php index ca07f98ef8b..faca0599ae4 100644 --- a/library/ZendAfi/View/Helper/TagNombreDePages.php +++ b/library/ZendAfi/View/Helper/TagNombreDePages.php @@ -1,6 +1,6 @@ <?php /** - * Copyright (c) 2012, Agence Française Informatique (AFI). All rights reserved. + * Copyright (c) 2012-2019, Agence Française Informatique (AFI). All rights reserved. * * BOKEH is free software; you can redistribute it and/or modify * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by @@ -16,13 +16,17 @@ * * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE * along with BOKEH; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -class ZendAfi_View_Helper_TagNombreDePages extends Zend_View_Helper_HtmlElement { - public function tagNombreDePages($page){ - if(!intval($page)) $page=1; - return $html='<div class="nb-pages-recherche"><span>'.$this->view->_('page ').$page.'</span></div>'; +class ZendAfi_View_Helper_TagNombreDePages extends ZendAfi_View_Helper_BaseHelper { + public function tagNombreDePages($page) { + $page = (int)$page; + + return $this + ->_tag('div', + $this->_tag('span', + $this->_('page ') . ($page ? $page : 1)), + ['class' => 'nb-pages-recherche']); } -} -?> \ No newline at end of file +} \ No newline at end of file diff --git a/tests/application/modules/opac/controllers/RechercheControllerHistoriqueRechercheTest.php b/tests/application/modules/opac/controllers/RechercheControllerHistoriqueRechercheTest.php index 1d4a33c43ec..082309b46c5 100644 --- a/tests/application/modules/opac/controllers/RechercheControllerHistoriqueRechercheTest.php +++ b/tests/application/modules/opac/controllers/RechercheControllerHistoriqueRechercheTest.php @@ -34,8 +34,9 @@ class RechercheControllerHistoriqueRechercheTest extends AbstractControllerTestC ->setParams(['rech_auteurs' => 'Miles Davis', 'annee_fin' => '1970']); - $session = new Zend_Session_Namespace('historiqueRecherche'); - $session->criteres = [serialize($criteres_potter), serialize($criteres_davis)]; + $history = new Class_SearchHistory(); + $history->add($criteres_potter); + $history->add($criteres_davis); $this->dispatch('/opac/recherche/saisie', true); } @@ -58,5 +59,3 @@ class RechercheControllerHistoriqueRechercheTest extends AbstractControllerTestC $this->assertXPath('//div[@class="configuration_module"]//a[contains(@href, "action1/saisie")]'); } } - -?> diff --git a/tests/application/modules/opac/controllers/RechercheControllerSimpleSerieTest.php b/tests/application/modules/opac/controllers/RechercheControllerSimpleSerieTest.php index ed91d803eed..479081fb184 100644 --- a/tests/application/modules/opac/controllers/RechercheControllerSimpleSerieTest.php +++ b/tests/application/modules/opac/controllers/RechercheControllerSimpleSerieTest.php @@ -20,8 +20,7 @@ */ -class RechercheControllerRechercheSimpleSerieTest - extends AbstractControllerTestCase { +class RechercheControllerRechercheSimpleSerieTest extends AbstractControllerTestCase { protected $_storm_default_to_volatile = true, $_search; diff --git a/tests/library/Class/MoteurRechercheTest.php b/tests/library/Class/MoteurRechercheTest.php index c210f1fec93..243f2e09136 100644 --- a/tests/library/Class/MoteurRechercheTest.php +++ b/tests/library/Class/MoteurRechercheTest.php @@ -39,6 +39,10 @@ abstract class MoteurRechercheTestCase extends ModelTestCase { $this->fixture('Class_Profil', ['id' => 1, 'libelle' => 'Portail']) ->beCurrentProfil(); + + $this->fixture('Class_CodifSection', + ['id' => 1, + 'libelle' => 'Super section']); } diff --git a/tests/library/ZendAfi/View/Helper/Accueil/HistoriqueRecherchesTest.php b/tests/library/ZendAfi/View/Helper/Accueil/HistoriqueRecherchesTest.php index 9a1f9488378..fd8c69c90e3 100644 --- a/tests/library/ZendAfi/View/Helper/Accueil/HistoriqueRecherchesTest.php +++ b/tests/library/ZendAfi/View/Helper/Accueil/HistoriqueRecherchesTest.php @@ -26,11 +26,13 @@ class ZendAfi_View_Helper_Accueil_HistoriqueRecherchesTest extends ViewHelperTes public function setUp() { parent::setUp(); Class_AdminVar::newInstanceWithId('MULTIMEDIA_KEY',['valeur'=>'81b3ab7b0b9a621afb6044a9c2f48ed2']); + $this->_helper = new ZendAfi_View_Helper_Accueil_HistoriqueRecherches(2, [ 'type_module'=>'HISTORIQUE_RECHERCHES', 'division' => '1', 'preferences' => ['titre' => 'Historique Recherches']]); - $this->_helper->setView(new ZendAfi_Controller_Action_Helper_View()); + + $this->_helper->setView($this->view); $criteres_potter = (new Class_CriteresRecherche) ->setParams(['expressionRecherche' => 'Harry Potter', @@ -40,9 +42,10 @@ class ZendAfi_View_Helper_Accueil_HistoriqueRecherchesTest extends ViewHelperTes ->setParams(['rech_auteurs' => 'Miles Davis', 'annee_fin' => '1970']); - $session = new Zend_Session_Namespace('historiqueRecherche'); - $session->criteres = [serialize($criteres_potter), serialize($criteres_davis)]; - + $history = new Class_SearchHistory(); + $history->add($criteres_potter); + $history->add($criteres_davis); + $this->html = $this->_helper->getBoite(); } @@ -56,18 +59,17 @@ class ZendAfi_View_Helper_Accueil_HistoriqueRecherchesTest extends ViewHelperTes /** @test */ - public function URLRechercheHarryPotterShouldBeDisplay(){ - $this->assertXPath($this->html, + public function urlRechercheHarryPotterShouldBeDisplayWithoutPageNumber(){ + $this->assertXPath($this->html, '//a[contains(@href, "/recherche/simple/expressionRecherche/Harry+Potter")]', $this->html); } + /** @test */ - public function URLRechercheMilesDavisShouldBeDisplay(){ - $this->assertXPath($this->html, + public function urlRechercheMilesDavisShouldBeDisplayWithEndYear(){ + $this->assertXPath($this->html, '//a[contains(@href, "/recherche/simple/rech_auteurs/Miles+Davis/annee_fin/1970")]', $this->html); } } - -?> \ No newline at end of file diff --git a/tests/library/ZendAfi/View/Helper/HistoriqueRechercheTest.php b/tests/library/ZendAfi/View/Helper/HistoriqueRechercheTest.php index fbd00a384f5..ac7d74ab51c 100644 --- a/tests/library/ZendAfi/View/Helper/HistoriqueRechercheTest.php +++ b/tests/library/ZendAfi/View/Helper/HistoriqueRechercheTest.php @@ -16,56 +16,57 @@ * * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE * along with BOKEH; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ class ZendAfi_View_Helper_HistoriqueRechercheWith2CriteresTest extends ViewHelperTestCase { public function setUp() { parent::setUp(); - $this->_session = new Zend_Session_Namespace('historiqueRecherche'); + $criteres = new Class_CriteresRecherche(); $criteres->setParams(['expressionRecherche' => 'Millenium']); + $criteres_auteur = new Class_CriteresRecherche(); $criteres_auteur->setParams(['rech_auteurs' => 'Larsson', - 'operateur_auteurs'=>'and', - 'rech_titres' => 'Les hommes qui n\'aimaient pas les femmes']); - - $this->_session->criteres=[serialize($criteres),serialize($criteres_auteur)]; - $view = new ZendAfi_Controller_Action_Helper_View(); + 'operateur_auteurs'=>'and', + 'rech_titres' => 'Les hommes qui n\'aimaient pas les femmes']); + + $history = new Class_SearchHistory(); + $history->add($criteres); + $history->add($criteres_auteur); + $this->_helper = new ZendAfi_View_Helper_HistoriqueRecherche(); - $this->_helper->setView($view); - + $this->_helper->setView($this->view); } - /** @test */ public function milleniumShouldBeDisplayed() { - $this->assertXPathContentContains($this->_helper->historiqueRecherche(), + $this->assertXPathContentContains($this->_helper->historiqueRecherche(), '//div', 'Millenium'); } + /** @test */ public function milleniumAndLarssonShouldBeDisplayed() { - $this->assertXPathContentContains($this->_helper->historiqueRecherche(), + $this->assertXPathContentContains($this->_helper->historiqueRecherche(), '//div', 'Larsson'); } + /** @test */ public function URLRechercheSimpleShouldBeDisplay(){ - $this->assertXPath($this->_helper->historiqueRecherche(), + $this->assertXPath($this->_helper->historiqueRecherche(), '//a[contains(@href, "/recherche/simple/expressionRecherche/Millenium")]'); } + /** @test */ public function URLRechercheAvanceShouldBeDisplay(){ - $this->assertXPath($this->_helper->historiqueRecherche(), + $this->assertXPath($this->_helper->historiqueRecherche(), '//a[contains(@href, "/recherche/simple/rech_titres/Les+hommes+qui+n%27aimaient+pas+les+femmes/rech_auteurs/Larsson")]',$this->_helper->historiqueRecherche()); } - } - - diff --git a/tests/scenarios/Security/SearchTest.php b/tests/scenarios/Security/SearchTest.php new file mode 100644 index 00000000000..4bf7cdcbbc0 --- /dev/null +++ b/tests/scenarios/Security/SearchTest.php @@ -0,0 +1,118 @@ +<?php +/** + * Copyright (c) 2012-2019, Agence Française Informatique (AFI). All rights reserved. + * + * BOKEH is free software; you can redistribute it and/or modify + * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by + * the Free Software Foundation. + * + * There are special exceptions to the terms and conditions of the AGPL as it + * is applied to this software (see README file). + * + * BOKEH 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 AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE + * along with BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +class Security_SearchTest extends AbstractControllerTestCase { + protected + $_storm_default_to_volatile = true, + $_default_params; + + + public function setUp() { + parent::setUp(); + $this->_default_params = ['operateur_titres' => 'and', + 'rech_titres' => '1', + 'operateur_auteurs' => 'and', + 'rech_auteurs' => '1', + 'operateur_matieres' => 'and', + 'rech_matieres' => '1', + 'operateur_dewey' => 'and', + 'type_recherche' => 'fulltext', + 'rech_dewey' => '1', + 'operateur_editeur' => 'and', + 'tri' => '*', + 'rech_editeur' => '1', + 'operateur_collection' => 'and', + 'annee_debut' => '1', + 'rech_collection' => '1', + 'annee_fin' => '1', + 'nouveaute' => '1', + 'type_doc ' => '1', + 'annexe' => '1', + 'section' => '1', + 'genre' => '']; + + $this->onLoaderOfModel('Class_TypeDoc') + ->whenCalled('findUsedTypeDocIds') + ->answers([]); + + Class_Systeme_Sql::shouldThrowErrors(); + } + + + public function tearDown() { + Class_Systeme_Sql::shouldNotThrowErrors(); + (new Class_SearchHistory())->clear(); + + parent::tearDown(); + } + + + public function validParameters() { + return array_map(function($item) { return [$item]; }, + (new Class_CriteresRecherche())->getValidParameters()); + } + + + /** + * @test + * @dataProvider validParameters + */ + public function paramShouldNotMakeSqlCrash($param) { + $this->_default_params[$param] = '%22\'%3E%3Cqss%20%60%3b!--%3%26%7b()%7d%3E'; + $this->dispatch('/recherche/simple?' . http_build_query($this->_default_params)); + } + + + /** + * @test + * @dataProvider validParameters + */ + public function paramShouldNotBeInjectedInHtml($param) { + $this->_default_params[$param] = '1"\'><qss>'; + $this->dispatch('/recherche/simple?' . http_build_query($this->_default_params)); + $this->assertNotContains('1"\'><qss>', $this->_response->getBody()); + } + + + + /** + * @test + * @dataProvider validParameters + */ + public function paramShouldNotBeInjectedInHistory($param) { + $history = new Class_SearchHistory(); + $history->clear(); + $history->add((new Class_CriteresRecherche) + ->setParams([$param => '1 <script>_q_q=random()</script>'])); + + $this->dispatch('/recherche/avancee'); + + $this->assertNotContains('1 <script>_q_q=random()</script>', $this->_response->getBody()); + } + + + /** @test */ + public function expressionRechercheShouldNotBeXssable() { + $this->dispatch('/recherche/simple?' . http_build_query(['expressionRecherche' => '1 <script>_q_q=random()</script>'])); + $this->assertNotContains('1 <script>_q_q=random()</script>', $this->_response->getBody()); + } +} diff --git a/tests/scenarios/bookmarks/SearchTest.php b/tests/scenarios/bookmarks/SearchTest.php index f35445656f4..edcb3f03cc1 100644 --- a/tests/scenarios/bookmarks/SearchTest.php +++ b/tests/scenarios/bookmarks/SearchTest.php @@ -63,14 +63,15 @@ abstract class Bookmarks_SearchWithSessionAbstract extends Admin_AbstractControl 'annee_fin' => '1970', 'page' => 2]); - $session = new Zend_Session_Namespace('historiqueRecherche'); - $session->criteres = [serialize($criteres_potter), serialize($criteres_davis)]; + $history = new Class_SearchHistory(); + $history->add($criteres_potter); + $history->add($criteres_davis); } } -class Bookmarks_SearchAbonneBookmarkSearchDispatchTest extends Bookmarks_SearchWithSessionAbstract { +class Bookmarks_SearchAbonneBookmarkSearchDispatchTest extends Bookmarks_SearchWithSessionAbstract { public function setUp() { parent::setUp(); Class_AdminVar::set('ENABLE_BOOKMARKABLE_SEARCHES_NOTIFY', 1); @@ -98,7 +99,7 @@ class Bookmarks_SearchAbonneBookmarkSearchDispatchTest extends Bookmarks_Search -class Bookmarks_SearchAbonneBookmarkSearchPostTest extends Bookmarks_SearchWithSessionAbstract { +class Bookmarks_SearchAbonneBookmarkSearchPostTest extends Bookmarks_SearchWithSessionAbstract { public function setUp() { parent::setUp(); Class_User_BookmarkedSearch::setTimeSource(new TimeSourceForTest('2018-01-17 01:01:01')); -- GitLab