source: spip-zone/_plugins_/htmlpurifier/inc/texte.php @ 112271

Last change on this file since 112271 was 112271, checked in by gouz@…, 2 years ago

on surcharge inc/texte.php pour redéfinir propre() et appliquer le filtrage avec echapper_html_suspect() en fin de traitement mais avant echappe_retour_modeles() : on lance le débat et si certains veulent tester, ils ont un plugin fonctionnel qui ne casse pas tout. Par cohérence : les fonctions de filtrage inc_echapper_html_suspect_dist() et echappe_anti_xss() effectue le même traitement. Version++

File size: 12.0 KB
Line 
1<?php
2
3/***************************************************************************\
4 *  SPIP, Systeme de publication pour l'internet                           *
5 *                                                                         *
6 *  Copyright (c) 2001-2018                                                *
7 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
8 *                                                                         *
9 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
10 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
11\***************************************************************************/
12
13/**
14 * Gestion des textes et raccourcis SPIP
15 *
16 * @package SPIP\Core\Texte
17 **/
18
19if (!defined('_ECRIRE_INC_VERSION')) {
20        return;
21}
22
23include_spip('inc/texte_mini');
24include_spip('inc/lien');
25
26/*************************************************************************************************************************
27 * Fonctions inutilisees en dehors de inc/texte
28 *
29 */
30
31/**
32 * Raccourcis dépendant du sens de la langue
33 *
34 * @return array Tablea ('','')
35 */
36function definir_raccourcis_alineas() {
37        return array('', '');
38}
39
40
41/**
42 * Traitement des raccourcis de tableaux
43 *
44 * Ne fait rien ici. Voir plugin Textwheel.
45 *
46 * @param string $bloc
47 * @return string
48 */
49function traiter_tableau($bloc) {
50        return $bloc;
51}
52
53
54/**
55 * Traitement des listes
56 *
57 * Ne fais rien. Voir Plugin Textwheel.
58 * (merci a Michael Parienti)
59 *
60 * @param string $texte
61 * @return string
62 */
63function traiter_listes($texte) {
64        return $texte;
65}
66
67/**
68 * Nettoie un texte, traite les raccourcis autre qu'URL, la typo, etc.
69 *
70 * Ne fais rien ici. Voir plugin Textwheel.
71 *
72 * @pipeline_appel pre_propre
73 * @pipeline_appel post_propre
74 *
75 * @param string $letexte
76 * @return string
77 */
78function traiter_raccourcis($letexte) {
79
80        // Appeler les fonctions de pre_traitement
81        $letexte = pipeline('pre_propre', $letexte);
82
83        // APPELER ICI UN PIPELINE traiter_raccourcis ?
84        // $letexte = pipeline('traiter_raccourcis', $letexte);
85
86        // Appeler les fonctions de post-traitement
87        $letexte = pipeline('post_propre', $letexte);
88
89        return $letexte;
90}
91
92/*************************************************************************************************************************
93 * Fonctions utilisees en dehors de inc/texte
94 */
95
96
97/**
98 * Échapper et affichier joliement les `<script` et `<iframe`...
99 *
100 * @param string $t
101 * @param string $class Attributs HTML du conteneur à ajouter
102 * @return string
103 */
104function echappe_js($t, $class = ' class = "echappe-js"') {
105        foreach (array('script', 'iframe') as $tag) {
106                if (stripos($t, "<$tag") !== false
107                        and preg_match_all(',<' . $tag . '.*?($|</' . $tag . '.),isS', $t, $r, PREG_SET_ORDER)
108                ) {
109                        foreach ($r as $regs) {
110                                $t = str_replace($regs[0],
111                                        "<code$class>" . nl2br(spip_htmlspecialchars($regs[0])) . '</code>',
112                                        $t);
113                        }
114                }
115        }
116
117        return $t;
118}
119
120
121/**
122 * Empêcher l'exécution de code PHP et JS
123 *
124 * Sécurité : empêcher l'exécution de code PHP, en le transformant en joli code
125 * dans l'espace privé. Cette fonction est aussi appelée par propre et typo.
126 *
127 * De la même manière, la fonction empêche l'exécution de JS mais selon le mode
128 * de protection passe en argument
129 *
130 * Il ne faut pas désactiver globalement la fonction dans l'espace privé car elle protège
131 * aussi les balises des squelettes qui ne passent pas forcement par propre ou typo après
132 * si elles sont appelées en direct
133 *
134 * @param string $arg
135 *     Code à protéger
136 * @param int $mode_filtre
137 *     Mode de protection
138 *       -1 : protection dans l'espace privé et public
139 *       0  : protection dans l'espace public
140 *       1  : aucune protection
141 *     utilise la valeur de la globale filtrer_javascript si non fourni
142 * @return string
143 *     Code protégé
144 **/
145function interdire_scripts($arg, $mode_filtre=null) {
146        // on memorise le resultat sur les arguments non triviaux
147        static $dejavu = array();
148
149        // Attention, si ce n'est pas une chaine, laisser intact
150        if (!$arg or !is_string($arg) or !strstr($arg, '<')) {
151                return $arg;
152        }
153
154        if (is_null($mode_filtre) or !in_array($mode_filtre, array(-1, 0, 1))) {
155                $mode_filtre = $GLOBALS['filtrer_javascript'];
156        }
157
158        if (isset($dejavu[$mode_filtre][$arg])) {
159                return $dejavu[$mode_filtre][$arg];
160        }
161
162        // echapper les tags asp/php
163        $t = str_replace('<' . '%', '&lt;%', $arg);
164
165        // echapper le php
166        $t = str_replace('<' . '?', '&lt;?', $t);
167
168        // echapper le < script language=php >
169        $t = preg_replace(',<(script\b[^>]+\blanguage\b[^\w>]+php\b),UimsS', '&lt;\1', $t);
170
171        // Pour le js, trois modes : parano (-1), prive (0), ok (1)
172        switch ($mode_filtre) {
173                case 0:
174                        if (!_DIR_RESTREINT) {
175                                $t = echappe_js($t);
176                        }
177                        break;
178                case -1:
179                        $t = echappe_js($t);
180                        break;
181        }
182
183        // pas de <base href /> svp !
184        $t = preg_replace(',<(base\b),iS', '&lt;\1', $t);
185
186        // Reinserer les echappements des modeles
187        if (defined('_PROTEGE_JS_MODELES')) {
188                $t = echappe_retour($t, "javascript" . _PROTEGE_JS_MODELES);
189        }
190        if (defined('_PROTEGE_PHP_MODELES')) {
191                $t = echappe_retour($t, "php" . _PROTEGE_PHP_MODELES);
192        }
193
194        return $dejavu[$mode_filtre][$arg] = $t;
195}
196
197
198/**
199 * Applique la typographie générale
200 *
201 * Effectue un traitement pour que les textes affichés suivent les règles
202 * de typographie. Fait une protection préalable des balises HTML et SPIP.
203 * Transforme les balises `<multi>`
204 *
205 * @filtre
206 * @uses traiter_modeles()
207 * @uses corriger_typo()
208 * @uses echapper_faux_tags()
209 * @see  propre()
210 *
211 * @param string $letexte
212 *     Texte d'origine
213 * @param bool $echapper
214 *     Échapper ?
215 * @param string|null $connect
216 *     Nom du connecteur à la bdd
217 * @param array $env
218 *     Environnement (pour les calculs de modèles)
219 * @return string $t
220 *     Texte transformé
221 **/
222function typo($letexte, $echapper = true, $connect = null, $env = array()) {
223        // Plus vite !
224        if (!$letexte) {
225                return $letexte;
226        }
227
228        // les appels directs a cette fonction depuis le php de l'espace
229        // prive etant historiquement ecrit sans argment $connect
230        // on utilise la presence de celui-ci pour distinguer les cas
231        // ou il faut passer interdire_script explicitement
232        // les appels dans les squelettes (de l'espace prive) fournissant un $connect
233        // ne seront pas perturbes
234        $interdire_script = false;
235        if (is_null($connect)) {
236                $connect = '';
237                $interdire_script = true;
238                $env['espace_prive'] = test_espace_prive();
239        }
240
241        // Echapper les codes <html> etc
242        if ($echapper) {
243                $letexte = echappe_html($letexte, 'TYPO');
244        }
245
246        //
247        // Installer les modeles, notamment images et documents ;
248        //
249        // NOTE : propre() ne passe pas par ici mais directement par corriger_typo
250        // cf. inc/lien
251
252        $letexte = traiter_modeles($mem = $letexte, false, $echapper ? 'TYPO' : '', $connect, null, $env);
253        if ($letexte != $mem) {
254                $echapper = true;
255        }
256        unset($mem);
257
258        $letexte = corriger_typo($letexte);
259        $letexte = echapper_faux_tags($letexte);
260
261        // reintegrer les echappements
262        if ($echapper) {
263                $letexte = echappe_retour($letexte, 'TYPO');
264        }
265
266        // Dans les appels directs hors squelette, securiser ici aussi
267        if ($interdire_script) {
268                $letexte = interdire_scripts($letexte);
269        }
270
271        // Dans l'espace prive on se mefie de tout contenu dangereux
272        // https://core.spip.net/issues/3371
273        // et aussi dans l'espace public si la globale filtrer_javascript = -1
274        // https://core.spip.net/issues/4166
275        if ($GLOBALS['filtrer_javascript'] == -1
276          or (isset($env['espace_prive']) and $env['espace_prive'] and $GLOBALS['filtrer_javascript']<=0)) {
277                $letexte = echapper_html_suspect($letexte);
278        }
279
280        return $letexte;
281}
282
283// Correcteur typographique
284define('_TYPO_PROTEGER', "!':;?~%-");
285define('_TYPO_PROTECTEUR', "\x1\x2\x3\x4\x5\x6\x7\x8");
286
287define('_TYPO_BALISE', ",</?[a-z!][^<>]*[" . preg_quote(_TYPO_PROTEGER) . "][^<>]*>,imsS");
288
289/**
290 * Corrige la typographie
291 *
292 * Applique les corrections typographiques adaptées à la langue indiquée.
293 *
294 * @pipeline_appel pre_typo
295 * @pipeline_appel post_typo
296 * @uses corriger_caracteres()
297 * @uses corriger_caracteres()
298 *
299 * @param string $letexte Texte
300 * @param string $lang Langue
301 * @return string Texte
302 */
303function corriger_typo($letexte, $lang = '') {
304
305        // Plus vite !
306        if (!$letexte) {
307                return $letexte;
308        }
309
310        $letexte = pipeline('pre_typo', $letexte);
311
312        // Caracteres de controle "illegaux"
313        $letexte = corriger_caracteres($letexte);
314
315        // Proteger les caracteres typographiques a l'interieur des tags html
316        if (preg_match_all(_TYPO_BALISE, $letexte, $regs, PREG_SET_ORDER)) {
317                foreach ($regs as $reg) {
318                        $insert = $reg[0];
319                        // hack: on transforme les caracteres a proteger en les remplacant
320                        // par des caracteres "illegaux". (cf corriger_caracteres())
321                        $insert = strtr($insert, _TYPO_PROTEGER, _TYPO_PROTECTEUR);
322                        $letexte = str_replace($reg[0], $insert, $letexte);
323                }
324        }
325
326        // trouver les blocs idiomes et les traiter à part
327        $letexte = extraire_idiome($ei = $letexte, $lang, true);
328        $ei = ($ei !== $letexte);
329
330        // trouver les blocs multi et les traiter a part
331        $letexte = extraire_multi($em = $letexte, $lang, true);
332        $em = ($em !== $letexte);
333
334        // Charger & appliquer les fonctions de typographie
335        $typographie = charger_fonction(lang_typo($lang), 'typographie');
336        $letexte = $typographie($letexte);
337
338        // Les citations en une autre langue, s'il y a lieu
339        if ($em) {
340                $letexte = echappe_retour($letexte, 'multi');
341        }
342        if ($ei) {
343                $letexte = echappe_retour($letexte, 'idiome');
344        }
345
346        // Retablir les caracteres proteges
347        $letexte = strtr($letexte, _TYPO_PROTECTEUR, _TYPO_PROTEGER);
348
349        // pipeline
350        $letexte = pipeline('post_typo', $letexte);
351
352        # un message pour abs_url - on est passe en mode texte
353        $GLOBALS['mode_abs_url'] = 'texte';
354
355        return $letexte;
356}
357
358
359/**
360 * Paragrapher seulement
361 *
362 * /!\ appelée dans inc/filtres et public/composer
363 *
364 * Ne fait rien ici. Voir plugin Textwheel
365 *
366 * @param string $letexte
367 * @param null $forcer
368 * @return string
369 */
370function paragrapher($letexte, $forcer = true) {
371        return $letexte;
372}
373
374/**
375 * Harmonise les retours chariots et mange les paragraphes HTML
376 *
377 * Ne sert plus
378 *
379 * @param string $letexte Texte
380 * @return string Texte
381 **/
382function traiter_retours_chariots($letexte) {
383        $letexte = preg_replace(",\r\n?,S", "\n", $letexte);
384        $letexte = preg_replace(",<p[>[:space:]],iS", "\n\n\\0", $letexte);
385        $letexte = preg_replace(",</p[>[:space:]],iS", "\\0\n\n", $letexte);
386
387        return $letexte;
388}
389
390
391/**
392 * Transforme les raccourcis SPIP, liens et modèles d'un texte en code HTML
393 *
394 * Filtre à appliquer aux champs du type `#TEXTE*`
395 *
396 * @filtre
397 * @uses echappe_html()
398 * @uses expanser_liens()
399 * @uses traiter_raccourcis()
400 * @uses echappe_retour_modeles()
401 * @see  typo()
402 *
403 * @param string $t
404 *     Texte avec des raccourcis SPIP
405 * @param string|null $connect
406 *     Nom du connecteur à la bdd
407 * @param array $env
408 *     Environnement (pour les calculs de modèles)
409 * @return string $t
410 *     Texte transformé
411 **/
412function propre($t, $connect = null, $env = array()) {
413        // les appels directs a cette fonction depuis le php de l'espace
414        // prive etant historiquement ecrits sans argment $connect
415        // on utilise la presence de celui-ci pour distinguer les cas
416        // ou il faut passer interdire_script explicitement
417        // les appels dans les squelettes (de l'espace prive) fournissant un $connect
418        // ne seront pas perturbes
419        $interdire_script = false;
420        if (is_null($connect)) {
421                $connect = '';
422                $interdire_script = true;
423        }
424
425        if (!$t) {
426                return strval($t);
427        }
428
429        // Dans l'espace prive on se mefie de tout contenu dangereux
430        // avant echappement des balises <html>
431        // https://core.spip.net/issues/3371
432        // et aussi dans l'espace public si la globale filtrer_javascript = -1
433        // https://core.spip.net/issues/4166
434        if ($interdire_script
435                or $GLOBALS['filtrer_javascript'] == -1
436                or (isset($env['espace_prive']) and $env['espace_prive'] and $GLOBALS['filtrer_javascript']<=0)
437                or (isset($env['wysiwyg']) and $env['wysiwyg'] and $GLOBALS['filtrer_javascript']<=0)) {
438                $t = echapper_html_suspect($t, false);
439        }
440        $t = echappe_html($t);
441        $t = expanser_liens($t, $connect, $env);
442        $t = traiter_raccourcis($t);
443        $t = echappe_retour_modeles($t, $interdire_script);
444
445        return $t;
446}
Note: See TracBrowser for help on using the repository browser.