2012-08-07 26 views
9

PHP'de statik kod analizi yapmak ve register_globals girişimi üzerinde güven duymak için herhangi bir yol var mı? Bir dosyayı manuel olarak incelemek ve başlatılmamış değişkenleri aramaya ve bunların bunlara güvenebileceğinden çıkarılamayacağını göreceli olarak kolaydır, ancak bunu yüzlerce komut dosyası için kullanmam gerekiyor, bu yüzden otomatik bir çözüm arıyorum. .Kayıt globals kullanımının tespiti

Son başvurum, yönerge kapalı ve katı hata bildirimi ve QA'nın uzun süre oynatılmasını sağlayarak dev bir ortam oluşturuyor, ardından hata günlüğünün yakaladığı örnekleri düzeltiyor ancak bu, 100 Otomatik çözüm mevcutsa vakaların% 'sini ve kesinlikle kaynakları iyi kullanamazsınız.

+0

Bunu yapmak zor olurdu çünkü değişken değişkenleri göz önünde bulundurmanız gerekir ve 'extract() ' –

+1

gibi" Hayır "diye düşünüyorum. @MikeB'ın doğru bir şekilde belirttiği gibi, 'extract()' ve değişken değişkenler (ve ek olarak '$ GLOBALS') gibi şeyler, aslında kodu yürütmeyen herhangi bir kod analizi için büyük bir anahtar atar. Evet, komut dosyalarını bir grupta çalıştırmayı deneyebilir ve ayarlanmamış değişkenlerin kullanımıyla ilgili şikayetlerinizi araştırabilirsiniz, ancak her zaman kodun (her ne kadar bu durumda hala sabitlenmesi gerekiyorsa da) yine de bozuk olduğu ihtimali vardır. Maalesef manuel yaklaşım muhtemelen burada tek seçeneğinizdir. Enjoy ... – DaveRandom

+0

Dikkat et, "register_globals" PHP 5.4.0'dan beri kaldırıldı. – Florent

cevap

13

Sadece basit tanımsız değişkenleri tespit etmek için bir araya kesmek küçük komut dosyası.

<?php 

error_reporting(E_ALL); 

$dir = './foo'; 

require_once './lib/bootstrap.php'; 

class Scope { 
    protected $stack; 
    protected $pos; 

    public function __construct() { 
     $this->stack = array(); 
     $this->pos = -1; 
    } 

    public function addVar($name) { 
     $this->stack[$this->pos][$name] = true; 
    } 

    public function hasVar($name) { 
     return isset($this->stack[$this->pos][$name]); 
    } 

    public function pushScope() { 
     $this->stack[++$this->pos] = array(); 
    } 

    public function popScope() { 
     --$this->pos; 
    } 
} 

class UndefinedVariableVisitor extends PHPParser_NodeVisitorAbstract { 
    protected $scope; 
    protected $parser; 
    protected $traverser; 

    public function __construct(Scope $scope, PHPParser_Parser $parser, PHPParser_NodeTraverser $traverser) { 
     $this->scope = $scope; 
     $this->parser = $parser; 
     $this->traverser = $traverser; 
    } 

    public function enterNode(PHPParser_Node $node) { 
     if (($node instanceof PHPParser_Node_Expr_Assign || $node instanceof PHPParser_Node_Expr_AssignRef) 
      && $node->var instanceof PHPParser_Node_Expr_Variable 
      && is_string($node->var->name) 
     ) { 
      $this->scope->addVar($node->var->name); 
     } elseif ($node instanceof PHPParser_Node_Stmt_Global || $node instanceof PHPParser_Node_Stmt_Static) { 
      foreach ($node->vars as $var) { 
       if (is_string($var->name)) { 
        $this->scope->addVar($var->name); 
       } 
      } 
     } elseif ($node instanceof PHPParser_Node_Expr_Variable && is_string($node->name)) { 
      if (!$this->scope->hasVar($node->name)) { 
       echo 'Undefined variable $' . $node->name . ' on line ' . $node->getLine() . "\n"; 
      } 
     } elseif ($node instanceof PHPParser_Node_Stmt_Function || $node instanceof PHPParser_Node_Stmt_ClassMethod) { 
      $this->scope->pushScope(); 

      // params are always available 
      foreach ($node->params as $param) { 
       $this->scope->addVar($param->name); 
      } 

      // methods always have $this 
      if ($node instanceof PHPParser_Node_Stmt_ClassMethod) { 
       $this->scope->addVar('this'); 
      } 
     } elseif ($node instanceof PHPParser_Node_Expr_Include && $node->expr instanceof PHPParser_Node_Scalar_String) { 
      $file = $node->expr->value; 
      $code = file_get_contents($file); 
      $stmts = $this->parser->parse($code); 

      // for includes within the file 
      $cwd = getcwd(); 
      chdir(dirname($file)); 

      $this->traverser->traverse($stmts); 

      chdir($cwd); 
     } 
    } 

    public function leaveNode(PHPParser_Node $node) { 
     if ($node instanceof PHPParser_Node_Stmt_Function || $node instanceof PHPParser_Node_Stmt_ClassMethod) { 
      $this->scope->popScope(); 
     } 
    } 
} 

$parser = new PHPParser_Parser(new PHPParser_Lexer()); 

$scope = new Scope; 

$traverser = new PHPParser_NodeTraverser; 
$traverser->addVisitor(new UndefinedVariableVisitor($scope, $parser, $traverser)); 

foreach (new RecursiveIteratorIterator(
      new RecursiveDirectoryIterator($dir), 
      RecursiveIteratorIterator::LEAVES_ONLY) 
     as $file 
) { 
    if (!preg_match('/\.php$/', $file)) continue; 

    echo 'Checking ' . $file . ':', "\n"; 

    $code = file_get_contents($file); 
    $stmts = $parser->parse($code); 

    // for includes within the file 
    $cwd = getcwd(); 
    chdir(dirname($file)); 

    $scope->pushScope(); 
    $traverser->traverse($stmts); 
    $scope->popScope(); 

    chdir($cwd); 

    echo "\n"; 
} 

Sadece çok temel bir uygulama var ve ben yoğun test etmedi ama $GLOBALS ve $$varVars ile vahşi gitmeyin komut için çalışmak olmalıdır: Bunun için PHP-Parser gerekir. Temel çözünürlüğü içerir.

+5

Sadece bir FYI, bir Apache ortamında 'if (! Preg_match ('/ \. Php $ /', $ dosya)) devam edin ; 'fool kanıtı değildir. Apache, bir PHP betiği olarak '/ \. Php (\. | $) /' Ile eşleşen herhangi bir şeyi yürütür (ve Windows'da '' 'değiştiricisine sahip olur). Bu, PHP destekli sitelerin dünyasında birçok güvenlik deliğine neden olan Apache tuhaflığının biraz bilinen bir parçasıdır. Bu gibi görünüyor * Muhtemelen bir +1 hak ediyor, ne kadar (özellikle PHPParser) ne anlama geldiğini anlayamadığım bir şey vermek için ne yaptıklarını söyleyemem. – DaveRandom

0

Bu (php manuel yorumların birinden) çalışması gerekir:

if (ini_get('register_globals')) { 
    foreach ($GLOBALS as $int_temp_name => $int_temp_value) { 
     if (!in_array($int_temp_name, array (
       'GLOBALS', 
       '_FILES', 
       '_REQUEST', 
       '_COOKIE', 
       '_SERVER', 
       '_ENV', 
       '_SESSION', 
       ini_get('session.name'), 
       'int_temp_name', 
       'int_temp_value' 
      ))) { 
      unset ($GLOBALS[$int_temp_name]); 
     } 
    } 
} 
+0

Bu, küresel olarak globals'ı nasıl algılayacak? –

+1

Evet ya da siz * register_globals'ı kapatmanız yeterli. Ancak OP, bir komut dosyasının açılıp açılmayacağını değil, bir komut dosyasının açık olup olmadığını programlı olarak algılamanın mümkün olup olmadığını soruyor. Ayrıca bu, çeşitli durumlarda * açılabilen değişkenlerin çoğunu yakalamaz. Peki ya 'Argv'? Peki ya $ argc? $ HTTP_RAW_POST_DATA? – DaveRandom

+0

Evet, siz ikiniz de doğru olup olmadığını kontrol edin. 2 –

İlgili konular