223 lines
6.1 KiB
PHP
223 lines
6.1 KiB
PHP
<?php
|
|
|
|
/***************************************************************************\
|
|
* SPIP, Systeme de publication pour l'internet *
|
|
* *
|
|
* Copyright (c) 2001-2019 *
|
|
* Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
|
|
* *
|
|
* Ce programme est un logiciel libre distribue sous licence GNU/GPL. *
|
|
* Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. *
|
|
\***************************************************************************/
|
|
|
|
/**
|
|
* Gestion d'une sécurisation des squelettes
|
|
*
|
|
* Une surcharge de ce fichier pourrait permettre :
|
|
*
|
|
* - de limiter l'utilisation des filtres à l'aide d'une liste blanche ou liste noire,
|
|
* - de rendre inactif le PHP écrit dans les squelettes
|
|
* - de refuser l'inclusion de fichier PHP dans les squelettes
|
|
*
|
|
* @package SPIP\Core\Compilateur\Sandbox
|
|
**/
|
|
|
|
if (!defined('_ECRIRE_INC_VERSION')) {
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* Composer le code d'exécution d'un texte
|
|
*
|
|
* En principe juste un echappement de guillemets
|
|
* sauf si on veut aussi echapper et interdire les scripts serveurs
|
|
* dans les squelettes
|
|
*
|
|
* @param string $texte
|
|
* Texte à composer
|
|
* @param Champ $p
|
|
* Balise qui appelle ce texte
|
|
* @return string
|
|
* Texte
|
|
*/
|
|
function sandbox_composer_texte($texte, &$p) {
|
|
$code = "'" . str_replace(array("\\", "'"), array("\\\\", "\\'"), $texte) . "'";
|
|
|
|
return $code;
|
|
}
|
|
|
|
|
|
/**
|
|
* Composer le code d'exécution d'un filtre
|
|
*
|
|
* @param string $fonc
|
|
* @param string $code
|
|
* @param string $arglist
|
|
* @param Champ $p
|
|
* Balise qui appelle ce filtre
|
|
* @return string
|
|
*/
|
|
function sandbox_composer_filtre($fonc, $code, $arglist, &$p) {
|
|
if (isset($GLOBALS['spip_matrice'][$fonc])) {
|
|
$code = "filtrer('$fonc',$code$arglist)";
|
|
}
|
|
|
|
// le filtre est defini sous forme de fonction ou de methode
|
|
// par ex. dans inc_texte, inc_filtres ou mes_fonctions
|
|
elseif ($f = chercher_filtre($fonc)) {
|
|
|
|
// cas particulier : le filtre |set doit acceder a la $Pile
|
|
// proto: filtre_set(&$Pile, $val, $args...)
|
|
if (strpbrk($f, ':')) { // Class::method
|
|
$refl = new ReflectionMethod($f);
|
|
} else {
|
|
$refl = new ReflectionFunction($f);
|
|
}
|
|
$refs = $refl->getParameters();
|
|
if (isset($refs[0]) and $refs[0]->name == 'Pile') {
|
|
$code = "$f(\$Pile,$code$arglist)";
|
|
} else {
|
|
$code = "$f($code$arglist)";
|
|
}
|
|
}
|
|
// le filtre n'existe pas,
|
|
// on le notifie
|
|
else {
|
|
erreur_squelette(array('zbug_erreur_filtre', array('filtre' => texte_script($fonc))), $p);
|
|
}
|
|
|
|
return $code;
|
|
}
|
|
|
|
// Calculer un <INCLURE(xx.php)>
|
|
// La constante ci-dessous donne le code general quand il s'agit d'un script.
|
|
define('CODE_INCLURE_SCRIPT', 'if (!($path = %s) OR !is_readable($path))
|
|
erreur_squelette(array("fichier_introuvable", array("fichier" => "%s")), array(%s));
|
|
else {
|
|
$contexte_inclus = %s;
|
|
include $path;
|
|
}
|
|
'
|
|
);
|
|
|
|
/**
|
|
* Composer le code d'inclusion PHP
|
|
*
|
|
* @param string $fichier
|
|
* @param Champ $p
|
|
* Balise créant l'inclusion
|
|
* @param array $_contexte
|
|
* @return string
|
|
*/
|
|
function sandbox_composer_inclure_php($fichier, &$p, $_contexte) {
|
|
$compil = texte_script(memoriser_contexte_compil($p));
|
|
// si inexistant, on essaiera a l'execution
|
|
if ($path = find_in_path($fichier)) {
|
|
$path = "\"$path\"";
|
|
} else {
|
|
$path = "find_in_path(\"$fichier\")";
|
|
}
|
|
|
|
return sprintf(CODE_INCLURE_SCRIPT, $path, $fichier, $compil, $_contexte);
|
|
}
|
|
|
|
/**
|
|
* Composer le code de sécurisation anti script
|
|
*
|
|
* @param string $code
|
|
* @param Champ $p
|
|
* Balise sur laquelle s'applique le filtre
|
|
* @return string
|
|
*/
|
|
function sandbox_composer_interdire_scripts($code, &$p) {
|
|
// Securite
|
|
if ($p->interdire_scripts
|
|
and $p->etoile != '**'
|
|
) {
|
|
if (!preg_match("/^sinon[(](.*),'([^']*)'[)]$/", $code, $r)) {
|
|
$code = "interdire_scripts($code)";
|
|
} else {
|
|
$code = interdire_scripts($r[2]);
|
|
$code = "sinon(interdire_scripts($r[1]),'$code')";
|
|
}
|
|
}
|
|
|
|
return $code;
|
|
}
|
|
|
|
|
|
/**
|
|
* Appliquer des filtres sur un squelette complet
|
|
*
|
|
* La fonction accèpte plusieurs tableaux de filtres à partir du 3ème argument
|
|
* qui seront appliqués dans l'ordre
|
|
*
|
|
* @uses echapper_php_callback()
|
|
*
|
|
* @param array $skel
|
|
* @param string $corps
|
|
* @param array $filtres
|
|
* Tableau de filtres à appliquer.
|
|
* @return mixed|string
|
|
*/
|
|
function sandbox_filtrer_squelette($skel, $corps, $filtres) {
|
|
$series_filtres = func_get_args();
|
|
array_shift($series_filtres);// skel
|
|
array_shift($series_filtres);// corps
|
|
|
|
// proteger les <INCLUDE> et tous les morceaux de php licites
|
|
if ($skel['process_ins'] == 'php') {
|
|
$corps = preg_replace_callback(',<[?](\s|php|=).*[?]>,UimsS', 'echapper_php_callback', $corps);
|
|
}
|
|
|
|
// recuperer les couples de remplacement
|
|
$replace = echapper_php_callback();
|
|
|
|
foreach ($series_filtres as $filtres) {
|
|
if (count($filtres)) {
|
|
foreach ($filtres as $filtre) {
|
|
if ($filtre and $f = chercher_filtre($filtre)) {
|
|
$corps = $f($corps);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// restaurer les echappements
|
|
return str_replace($replace[0], $replace[1], $corps);
|
|
}
|
|
|
|
|
|
/**
|
|
* Callback pour échapper du code PHP (les séquences `<?php ... ?>`)
|
|
*
|
|
* Rappeler la fonction sans paramètre pour obtenir les substitutions réalisées.
|
|
*
|
|
* @see sandbox_filtrer_squelette()
|
|
*
|
|
* @param array|null $r
|
|
* - array : ce sont les captures de la regex à échapper
|
|
* - NULL : demande à dépiler tous les échappements réalisés
|
|
* @return string|array
|
|
* - string : hash de substitution du code php lorsque `$r` est un array
|
|
* - array : Liste( liste des codes PHP, liste des substitutions )
|
|
**/
|
|
function echapper_php_callback($r = null) {
|
|
static $src = array();
|
|
static $dst = array();
|
|
|
|
// si on recoit un tableau, on est en mode echappement
|
|
// on enregistre le code a echapper dans dst, et le code echappe dans src
|
|
if (is_array($r)) {
|
|
$dst[] = $r[0];
|
|
|
|
return $src[] = '___' . md5($r[0]) . '___';
|
|
}
|
|
|
|
// si on recoit pas un tableau, on renvoit les couples de substitution
|
|
// et on RAZ les remplacements
|
|
$r = array($src, $dst);
|
|
$src = $dst = array();
|
|
|
|
return $r;
|
|
}
|