From 6c1650713fc948318852ace759aab8f1a84791cf Mon Sep 17 00:00:00 2001
From: Cerdic <cedric@yterium.com>
Date: Tue, 19 May 2020 17:48:56 +0200
Subject: [PATCH] Divers petites sanitization et une balise manquante #4494

---
 ecrire/inc/filtres.php            | 11 +++++++++
 ecrire/inc/precharger_objet.php   |  8 +++----
 ecrire/inc/utils.php              | 37 +++++++++++++++++++++++++++++++
 ecrire/public/aiguiller.php       |  7 ++++++
 ecrire/public/balises.php         | 13 +++++++++++
 ecrire/public/criteres.php        |  4 ++--
 prive/formulaires/editer_logo.php |  1 +
 prive/squelettes/structure.html   |  1 +
 8 files changed, 76 insertions(+), 6 deletions(-)

diff --git a/ecrire/inc/filtres.php b/ecrire/inc/filtres.php
index 8a7b0de59e..4957ec7e40 100644
--- a/ecrire/inc/filtres.php
+++ b/ecrire/inc/filtres.php
@@ -403,6 +403,17 @@ function filtre_setenv(&$Pile, $val, $key, $continue = null) {
 	return $continue ? $val : '';
 }
 
+/**
+ * @param array $Pile
+ * @param array|string $keys
+ * @return string
+ */
+function filtre_sanitize_env(&$Pile, $keys) {
+	$Pile[0] = spip_sanitize_from_request($Pile[0], $keys);
+	return '';
+}
+
+
 /**
  * Filtre `debug` qui affiche un debug de la valeur en entrée
  *
diff --git a/ecrire/inc/precharger_objet.php b/ecrire/inc/precharger_objet.php
index d11a381ebf..f607d38fd6 100644
--- a/ecrire/inc/precharger_objet.php
+++ b/ecrire/inc/precharger_objet.php
@@ -50,7 +50,7 @@ function precharger_objet($type, $id_objet, $id_rubrique = 0, $lier_trad = 0, $c
 
 	// si l'objet existe deja, on retourne simplement ses valeurs
 	if (is_numeric($id_objet)) {
-		return sql_fetsel("*", $table, "$_id_objet=$id_objet");
+		return sql_fetsel("*", $table, "$_id_objet=".intval($id_objet));
 	}
 
 	// ici, on demande une creation.
@@ -133,7 +133,7 @@ function precharger_traduction_objet($type, $id_objet, $id_rubrique = 0, $lier_t
 	$_id_objet = id_table_objet($table);
 
 	// Recuperer les donnees de l'objet original
-	$row = sql_fetsel("*", $table, "$_id_objet=$lier_trad");
+	$row = sql_fetsel("*", $table, "$_id_objet=".intval($lier_trad));
 	if ($row) {
 		include_spip('inc/filtres');
 		$row[$champ_titre] = filtrer_entites(objet_T($type, 'info_nouvelle_traduction')) . ' ' . $row[$champ_titre];
@@ -172,12 +172,12 @@ function precharger_traduction_objet($type, $id_objet, $id_rubrique = 0, $lier_t
 					$id_parent = 0;
 				} else {
 					// on cherche une rubrique soeur dans la bonne langue
-					$row_rub = sql_fetsel("id_parent", "spip_rubriques", "id_rubrique=$id_rubrique");
+					$row_rub = sql_fetsel("id_parent", "spip_rubriques", "id_rubrique=".intval($id_rubrique));
 					$id_parent = $row_rub['id_parent'];
 				}
 
 				$row_rub = sql_fetsel("id_rubrique", "spip_rubriques",
-					"lang='" . $GLOBALS['spip_lang'] . "' AND id_parent=$id_parent");
+					"lang='" . $GLOBALS['spip_lang'] . "' AND id_parent=".intval($id_parent));
 				if ($row_rub) {
 					$row['id_rubrique'] = $row_rub['id_rubrique'];
 				}
diff --git a/ecrire/inc/utils.php b/ecrire/inc/utils.php
index a61217fb4b..74e155d012 100644
--- a/ecrire/inc/utils.php
+++ b/ecrire/inc/utils.php
@@ -446,6 +446,43 @@ function set_request($var, $val = null, $c = false) {
 	return false; # n'affecte pas $c
 }
 
+/**
+ * Sanitizer une valeur *SI* elle provient du GET ou POST
+ * Utile dans les squelettes pour les valeurs qu'on attrape dans le env,
+ * dont on veut permettre à un squelette de confiance appelant de fournir une valeur complexe
+ * mais qui doit etre nettoyee si elle provient de l'URL
+ *
+ * On peut sanitizer
+ * - une valeur simple : `$where = spip_sanitize_from_request($value, 'where')`
+ * - un tableau en partie : `$env = spip_sanitize_from_request($env, ['key1','key2'])`
+ * - un tableau complet : `$env = spip_sanitize_from_request($env, '*')`
+ *
+ * @param string|array $value
+ * @param string|array $key
+ * @param string $sanitize_function
+ * @return array|mixed|string
+ */
+function spip_sanitize_from_request($value, $key, $sanitize_function='entites_html') {
+	if (is_array($value)) {
+		if ($key=='*') {
+			$key = array_keys($value);
+		}
+		if (!is_array($key)) {
+			$key = [$key];
+		}
+		foreach ($key as $k) {
+			if (!empty($value[$k])) {
+				$value[$k] = spip_sanitize_from_request($value[$k], $k, $sanitize_function);
+			}
+		}
+		return $value;
+	}
+	// si la valeur vient des GET ou POST on la sanitize
+	if (!empty($value) and $value == _request($key)) {
+		$value = $sanitize_function($value);
+	}
+	return $value;
+}
 
 /**
  * Tester si une URL est absolue
diff --git a/ecrire/public/aiguiller.php b/ecrire/public/aiguiller.php
index 0500e2d67d..44629a19ee 100644
--- a/ecrire/public/aiguiller.php
+++ b/ecrire/public/aiguiller.php
@@ -15,6 +15,13 @@ if (!defined('_ECRIRE_INC_VERSION')) {
 }
 
 function securiser_redirect_action($redirect) {
+	// cas d'un double urlencode : si un urldecode de l'url n'est pas secure, on retient ca comme redirect
+	if (strpos($redirect, '%') !== false) {
+		$r2 = urldecode($redirect);
+		if (($r3 = securiser_redirect_action($r2)) !== $r2) {
+			return $r3;
+		}
+	}
 	if ((tester_url_absolue($redirect) or preg_match(',^\w+:,',trim($redirect)))
 		and !defined('_AUTORISER_ACTION_ABS_REDIRECT')) {
 		// si l'url est une url du site, on la laisse passer sans rien faire
diff --git a/ecrire/public/balises.php b/ecrire/public/balises.php
index 699bd50e11..08f18935b8 100644
--- a/ecrire/public/balises.php
+++ b/ecrire/public/balises.php
@@ -1691,6 +1691,19 @@ function balise_REM_dist($p) {
 	return $p;
 }
 
+/**
+ * Une balise #NULL quand on a besoin de passer un argument null sur l'appel d'un filtre ou formulaire
+ * (evite un #EVAL{null})
+ * @param $p
+ * @return mixed
+ */
+function balise_NULL_dist($p) {
+	$p->code = "null";
+	$p->interdire_scripts = false;
+
+	return $p;
+}
+
 
 /**
  * Compile la balise `#HTTP_HEADER` envoyant des entêtes de retour HTTP
diff --git a/ecrire/public/criteres.php b/ecrire/public/criteres.php
index e948ab4efb..bed609c0dc 100644
--- a/ecrire/public/criteres.php
+++ b/ecrire/public/criteres.php
@@ -1557,11 +1557,11 @@ function critere_where_dist($idb, &$boucles, $crit) {
 	if (isset($crit->param[0])) {
 		$_where = calculer_liste($crit->param[0], $idb, $boucles, $boucle->id_parent);
 	} else {
-		$_where = '@$Pile[0]["where"]';
+		$_where = 'spip_sanitize_from_request(@$Pile[0]["where"],"where","vide")';
 	}
 
 	if ($crit->cond) {
-		$_where = "(($_where) ? ($_where) : '')";
+		$_where = "((\$zzw = $_where) ? \$zzw : '')";
 	}
 
 	if ($crit->not) {
diff --git a/prive/formulaires/editer_logo.php b/prive/formulaires/editer_logo.php
index 21e8577f3a..5e5267cfe6 100644
--- a/prive/formulaires/editer_logo.php
+++ b/prive/formulaires/editer_logo.php
@@ -61,6 +61,7 @@ function formulaires_editer_logo_charger_dist($objet, $id_objet, $retour = '', $
 	if (!is_array($options)) {
 		$options = unserialize($options);
 	}
+	$options = spip_sanitize_from_request($options, '*');
 
 	if (!isset($options['titre'])) {
 		$balise_img = chercher_filtre('balise_img');
diff --git a/prive/squelettes/structure.html b/prive/squelettes/structure.html
index 5d721cac80..8fafccc0e9 100644
--- a/prive/squelettes/structure.html
+++ b/prive/squelettes/structure.html
@@ -5,5 +5,6 @@
 <head>
 <INCLURE{fond=prive/squelettes/head/#ENV{type-page},env,espace_prive=1}>
 </head>
+[(#LISTE{titre,sinon}|sanitize_env)]
 <INCLURE{fond=prive/squelettes/body,env,espace_prive=1}>
 </html>
-- 
GitLab