Brian Wolff has submitted this change and it was merged. (
https://gerrit.wikimedia.org/r/393537 )
Change subject: Fix remaining tests (mostly phpcs)
......................................................................
Fix remaining tests (mostly phpcs)
Change-Id: Ic802e332ab0fbabc3434b308a41353eaafbab69b
---
M .phpcs.xml
M src/GenericSecurityCheckPlugin.php
M src/MediaWikiSecurityCheckPlugin.php
M src/PreTaintednessVisitor.php
M src/SecurityCheckPlugin.php
M src/TaintednessBaseVisitor.php
M src/TaintednessVisitor.php
7 files changed, 536 insertions(+), 89 deletions(-)
diff --git a/.phpcs.xml b/.phpcs.xml
index b7513f7..d5331bd 100644
--- a/.phpcs.xml
+++ b/.phpcs.xml
@@ -3,6 +3,7 @@
<rule ref="./vendor/mediawiki/mediawiki-codesniffer/MediaWiki">
<exclude
name="MediaWiki.NamingConventions.PrefixedGlobalFunctions.wfPrefix"/>
<exclude name="MediaWiki.Usage.ScalarTypeHintUsage.Found"/>
+ <exclude
name="MediaWiki.Commenting.FunctionComment.MissingParamComment"/>
</rule>
<file>.</file>
<arg name="bootstrap"
value="./vendor/mediawiki/mediawiki-codesniffer/utils/bootstrap-ci.php"/>
diff --git a/src/GenericSecurityCheckPlugin.php
b/src/GenericSecurityCheckPlugin.php
index 51f9684..0d21534 100644
--- a/src/GenericSecurityCheckPlugin.php
+++ b/src/GenericSecurityCheckPlugin.php
@@ -15,6 +15,9 @@
*/
class GenericSecurityCheckPlugin extends SecurityCheckPlugin {
+ /**
+ * @inheritDoc
+ */
protected function getCustomFuncTaints() : array {
return [];
}
diff --git a/src/MediaWikiSecurityCheckPlugin.php
b/src/MediaWikiSecurityCheckPlugin.php
index bc6c4a4..75d5166 100644
--- a/src/MediaWikiSecurityCheckPlugin.php
+++ b/src/MediaWikiSecurityCheckPlugin.php
@@ -6,6 +6,9 @@
class MediaWikiSecurityCheckPlugin extends SecurityCheckPlugin {
+ /**
+ * @inheritDoc
+ */
protected function getCustomFuncTaints() : array {
return [
// '\Message::__construct' =>
SecurityCheckPlugin::YES_TAINT,
diff --git a/src/PreTaintednessVisitor.php b/src/PreTaintednessVisitor.php
index 70c0172..46f0f83 100644
--- a/src/PreTaintednessVisitor.php
+++ b/src/PreTaintednessVisitor.php
@@ -5,12 +5,35 @@
use ast\Node\Decl;
use Phan\Debug;
+/**
+ * Class for visiting any nodes we want to handle in pre-order.
+ *
+ * Unlike TaintednessVisitor, this is solely used to set taint
+ * on variable objects, and not to determine the taint of the
+ * current node, so this class does not return anything.
+ */
class PreTaintednessVisitor extends TaintednessBaseVisitor {
+ /**
+ * Handle any node not otherwise handled.
+ *
+ * Currently a no-op.
+ *
+ * @param Node $node
+ */
public function visit( Node $node ) {
- // no-op
}
+ /**
+ * Visit a foreach loop
+ *
+ * This is done in pre-order so that we can handle
+ * the loop condition prior to determine the taint
+ * of the loop variable, prior to evaluating the
+ * loop body.
+ *
+ * @param Node $node
+ */
public function visitForeach( Node $node ) {
// TODO: Could we do something better here detecting the array
// type
@@ -44,17 +67,29 @@
// getVariable can throw an IssueException if var
doesn't exist.
$this->debug( __METHOD__, "Exception " . get_class( $e
) . $e->getMessage() . "" );
}
-
- // The foreach as a block cannot be tainted.
- return SecurityCheckPlugin::NO_TAINT;
}
+ /**
+ * @see visitMethod
+ * @param Decl $node
+ * @return void Just has a return statement in case visitMethod changes
+ */
public function visitFuncDecl( Decl $node ) {
return $this->visitMethod( $node );
}
/**
+ * Set the taintedness of parameters to method/function.
+ *
+ * Parameters that are ints (etc) are clearly safe so
+ * this marks them as such. For other parameters, it
+ * creates a map between the function object and the
+ * parameter object so if anyone later calls the method
+ * with a dangerous argument we can determine if we need
+ * to output a warning.
+ *
* Also handles FuncDecl
+ * @param Decl $node
*/
public function visitMethod( Decl $node ) {
// var_dump( __METHOD__ ); Debug::printNode( $node );
diff --git a/src/SecurityCheckPlugin.php b/src/SecurityCheckPlugin.php
index 0eb4490..94d6b5b 100644
--- a/src/SecurityCheckPlugin.php
+++ b/src/SecurityCheckPlugin.php
@@ -44,20 +44,12 @@
const YES_EXEC_TAINT = 2040;
/**
- * @param CodeBase $code_base
- * The code base in which the node exists
+ * Called on every node in the AST in post-order
*
- * @param Context $context
- * The context in which the node exits. This is
- * the context inside the given node rather than
- * the context outside of the given node
- *
- * @param Node $node
- * The php-ast Node being analyzed.
- *
- * @param Node $node
- * The parent node of the given node (if one exists).
- *
+ * @param CodeBase $code_base The code base in which the node exists
+ * @param Context $context The context in which the node exits.
+ * @param Node $node The php-ast Node being analyzed.
+ * @param Node $parent_node The parent node of the given node (if one
exists).
* @return void
*/
public function analyzeNode(
@@ -66,19 +58,32 @@
Node $node,
Node $parent_node = null
) {
- // echo __METHOD__ . ' ' .\ast\get_kind_name($node->kind) . " (Parent:
" . ($parent_node ? \ast\get_kind_name($parent_node->kind) : "N/A") . ")\n";
$oldMem = memory_get_peak_usage();
- ( new TaintednessVisitor( $code_base, $context, $this ) )(
- $node
- );
+ // This would also return the taint of the current node,
+ // but we don't need that here so we discard the return value.
+ $visitor = new TaintednessVisitor( $code_base, $context, $this
);
+ $visitor( $node );
$newMem = memory_get_peak_usage();
$diff = floor( ( $newMem - $oldMem ) / ( 1024 * 1024 ) );
if ( $diff > 10 ) {
- echo "Memory Spike! " . $context . " "
.\ast\get_kind_name( $node->kind ) .
- " diff=$diff MB; cur=" . floor( ( memory_get_usage() /
( 1024 * 1024 ) ) ) . " MB\n";
+ $cur = floor( ( memory_get_usage() / ( 1024 * 1024 ) )
);
+ $visitor->debug( __METHOD__, "Memory Spike! " .
\ast\get_kind_name( $node->kind ) .
+ " diff=$diff MB; cur=$cur MB\n"
+ );
}
}
+ /**
+ * Called on every node in the ast, but in pre-order
+ *
+ * We only need this for a couple things, namely
+ * structural elements that cause a new variable to be
+ * declared (e.g. method declarations, foreach loops)
+ *
+ * @param CodeBase $code_base
+ * @param Context $context
+ * @param Node $node
+ */
public function preAnalyzeNode( CodeBase $code_base, Context $context,
Node $node ) {
( new PreTaintednessVisitor( $code_base, $context, $this ) )(
$node );
}
diff --git a/src/TaintednessBaseVisitor.php b/src/TaintednessBaseVisitor.php
index 14d87e8..d04cd3b 100644
--- a/src/TaintednessBaseVisitor.php
+++ b/src/TaintednessBaseVisitor.php
@@ -17,13 +17,23 @@
use Phan\Library\Set;
use Phan\Exception\IssueException;
+/**
+ * Base class for the Tainedness visitor subclass. Mostly contains
+ * utility methods.
+ */
abstract class TaintednessBaseVisitor extends AnalysisVisitor {
/** @var SecurityCheckPlugin */
protected $plugin;
+ /** @var null|string|bool|resource filehandle to output debug messages
*/
private $debugOutput = null;
+ /**
+ * @param CodeBase $code_base
+ * @param Context $context
+ * @param SecurityCheckPlugin $plugin The instance of the plugin we're
using
+ */
public function __construct(
CodeBase $code_base,
Context $context,
@@ -81,7 +91,16 @@
}
/**
- * It is assumed you already checked that right is tainted in some way.
+ * Merge the info on original cause of taint to left variable
+ *
+ * If you have something like $left = $right, merge any information
+ * about what tainted $right into $left as $right's taint may now
+ * have tainted $left (Or may not if the assingment is in a branch
+ * or its not a local variable).
+ *
+ * @note It is assumed you already checked that right is tainted in
some way.
+ * @param TypedElementInterface $left (LHS-ish variable)
+ * @param TypedElementInterface $right (RHS-ish variable)
*/
protected function mergeTaintError( TypedElementInterface $left,
TypedElementInterface $right ) {
if ( !property_exists( $left, 'taintedOriginalError' ) ) {
@@ -102,8 +121,13 @@
* @param int $taintedness One of the class constants
* @param bool $override Override taintedness or just take max.
*/
- protected function setTaintedness( TypedElementInterface $variableObj,
int $taintedness, $override = true ) {
- // $this->debug( __METHOD__, "begin for \$" .
$variableObj->getName() . " <- $taintedness (override=$override)" );
+ protected function setTaintedness(
+ TypedElementInterface $variableObj,
+ int $taintedness,
+ $override = true
+ ) {
+ // $this->debug( __METHOD__, "begin for \$" .
$variableObj->getName()
+ // . " <- $taintedness (override=$override)" );
assert( $taintedness >= 0, $taintedness );
@@ -115,12 +139,12 @@
if ( property_exists( $variableObj, 'taintednessHasOuterScope' )
|| !( $this->context->getScope() instanceof
FunctionLikeScope )
) {
-// $this->debug( __METHOD__, "\$" . $variableObj->getName() . " has outer
scope - " . get_class( $this->context->getScope()) . "" );
+// $this->debug( __METHOD__, "\$" . $variableObj->getName() . " has outer
scope - "
+// . get_class( $this->context->getScope()) . "" );
// If the current context is not a FunctionLikeScope,
then
// it might be a class, or an if branch, or global. In
any case
// its probably a non-local variable (or in the if
case, code
// that may not be executed).
- //
if ( !property_exists( $variableObj,
'taintednessHasOuterScope' )
&& ( $this->context->getScope() instanceof
BranchScope )
@@ -146,7 +170,8 @@
$variableObjLinks =
$variableObj->taintedMethodLinks ?? new Set;
$variableObj->taintedMethodLinks =
$methodLinks->union( $variableObjLinks );
$parentVarObj->taintedMethodLinks =&
$variableObj->taintedMethodLinks;
- $combinedOrig = (
$variableObj->taintedOriginalError ?? '' ) . (
$parentVarObj->taintedOriginalError ?? '' );
+ $combinedOrig = (
$variableObj->taintedOriginalError ?? '' )
+ . (
$parentVarObj->taintedOriginalError ?? '' );
if ( strlen( $combinedOrig ) > 254 ) {
$this->debug( __METHOD__, "Too
long original error! $variableObj" );
$combinedOrig = substr(
$combinedOrig, 0, 250 ) . '...';
@@ -185,7 +210,9 @@
if ( strlen( $variableObj->taintedOriginalError ) > 254
) {
$this->debug( __METHOD__, "Too long original
error! $variableObj" );
- $variableObj->taintedOriginalError = substr(
$variableObj->taintedOriginalError, 0, 250 ) . '...';
+ $variableObj->taintedOriginalError = substr(
+ $variableObj->taintedOriginalError, 0,
250
+ ) . '...';
}
}
}
@@ -206,6 +233,7 @@
/**
* This is also for methods and other function like things
*
+ * @param FunctionInterface $func What function/method to look up
* @return int[] Array with "overall" key, and numeric keys.
* The overall key specifies what taint the function returns
* irrespective of its arguments. The numeric keys are how
@@ -274,7 +302,14 @@
}
/**
+ * Given a type, determine what type of taint
+ *
+ * e.g. Integers are probably untainted since its hard to do evil
+ * with them, but mark strings as unknown since we don't know.
+ *
* Only use as a fallback
+ * @param UnionType $types The types
+ * @return int The taint in question
*/
protected function getTaintByReturnType( UnionType $types ) : int {
$taint = SecurityCheckPlugin::NO_TAINT;
@@ -314,6 +349,10 @@
}
/**
+ * Get the built in taint of a function/method
+ *
+ * This is used for when people special case if a function is tainted.
+ *
* @param FullyQualifiedFunctionLikeName $fqsen Function to check
* @return null|array Null if no info, otherwise the taint for the
function
*/
@@ -325,9 +364,14 @@
return $taint;
}
+ /**
+ * Get name of current method (for debugging purposes)
+ *
+ * @return string Name of method or "[no method]"
+ */
protected function getCurrentMethod() {
return $this->context->isInFunctionLikeScope() ?
- $this->context->getFunctionLikeFQSEN() : '[no method]';
+ (string)$this->context->getFunctionLikeFQSEN() : '[no
method]';
}
/**
@@ -338,7 +382,7 @@
*
* FIXME maybe it should try and turn into phan object.
* @param Mixed $expr An expression from the AST tree.
- * @return int
+ * @return int The taint
*/
protected function getTaintedness( $expr ) : int {
$type = gettype( $expr );
@@ -367,6 +411,12 @@
}
}
+ /**
+ * Give an AST node, find its taint
+ *
+ * @param Node $node
+ * @return int The taint
+ */
protected function getTaintednessNode( Node $node ) : int {
// Debug::printNode( $node );
$r = ( new TaintednessVisitor( $this->code_base,
$this->context, $this->plugin ) )(
@@ -376,6 +426,12 @@
return $r;
}
+ /**
+ * Given a phan object (not method/function) find its taint
+ *
+ * @param TypedElementInterface $variableObj
+ * @return int The taint
+ */
protected function getTaintednessPhanObj( TypedElementInterface
$variableObj ) : int {
$taintedness = SecurityCheckPlugin::UNKNOWN_TAINT;
if ( $variableObj instanceof FunctionInterface ) {
@@ -387,12 +443,19 @@
} else {
$type = $variableObj->getUnionType();
$taintedness = $this->getTaintByReturnType( $type );
- // echo $this->dbgInfo() . " \$" .
$variableObj->getName() . " first sight. taintedness set to $taintedness due to
type $type\n";
+ // $this->debug( " \$" . $variableObj->getName() . "
first sight."
+ // . " taintedness set to $taintedness due to type
$type\n";
}
assert( is_int( $taintedness ) && $taintedness >= 0 );
return $taintedness;
}
+ /**
+ * Quick wrapper to get the ContextNode for a node
+ *
+ * @param Node $node
+ * @return ContextNode
+ */
protected function getCtxN( Node $node ) {
return new ContextNode(
$this->code_base,
@@ -418,6 +481,10 @@
*
* @suppress PhanTypeMismatchForeach No idea why its confused
* @suppress PhanUndeclaredMethod it checks method_exists()
+ * @param Node $node AST node in question
+ * @param bool $all Whether or not to only include objects that
directly correspond
+ * In particular, whether or not to include Method objects for method
calls.
+ * @return Array Array of various phan objects corresponding to $node
*/
protected function getPhanObjsForNode( Node $node, $all = false ) {
$cn = $this->getCtxN( $node );
@@ -440,13 +507,25 @@
}
}
} catch ( IssueException $e ) {
- $this->debug( __METHOD__,
"Cannot determine property or var name [1] (Maybe don't know what class) - " .
$e->getIssueInstance() );
+ $this->debug( __METHOD__,
+ "Cannot determine
property or var " .
+ "name [1] (Maybe don't
know what class) - "
+ . $e->getIssueInstance()
+ );
return [];
} catch ( Exception $e ) {
- $this->debug( __METHOD__,
"Cannot determine property or var name [2] (Maybe don't know what class) - " .
get_class( $e ) . $e->getMessage() );
+ $this->debug( __METHOD__,
+ "Cannot determine
property or var " .
+ "name [2] (Maybe don't
know what class) - "
+ . get_class( $e ) .
$e->getMessage() );
return [];
}
- $this->debug( __METHOD__, "Cannot
determine property [3] (Maybe don't know what class) - " . ( method_exists( $e,
'getIssueInstance' ) ? $e->getIssueInstance() : get_class( $e ) .
$e->getMessage() ) );
+ $this->debug( __METHOD__, "Cannot
determine " .
+ "property [3] (Maybe don't know
what class) - " .
+ ( method_exists( $e,
'getIssueInstance' )
+ ? $e->getIssueInstance()
+ : get_class( $e ) .
$e->getMessage() )
+ );
return [];
}
case \ast\AST_VAR:
@@ -568,7 +647,9 @@
// Debug::printNode( $node );
// This should really be a visitor that
recurses into
// things.
- echo __METHOD__ . $this->dbgInfo() . " FIXME
unhandled case" . \ast\get_kind_name( $node->kind ) . "\n";
+ $this->debug( __METHOD__, "FIXME unhandled case"
+ . \ast\get_kind_name( $node->kind ) .
"\n"
+ );
return [];
}
}
@@ -591,12 +672,18 @@
* later, we can traceback anything it might affect
*
* @suppress PhanTypeMismatchProperty
+ * @param Variable $param The variable object for the parameter
+ * @param FunctionInterface $func The function/method in question
+ * @param int $i Which argument number is $param
*/
protected function linkParamAndFunc( Variable $param, FunctionInterface
$func, int $i ) {
if ( !( $param instanceof Variable ) ) {
// Probably a PassByReferenceVariable.
// TODO, handling of PassByReferenceVariable probably
wrong here.
- $this->debug( __METHOD__, "Called on a non-variable \$"
. $param->getName() . " of type " . get_class( $param ) . ". May be handled
wrong." );
+ $this->debug( __METHOD__, "Called on a non-variable \$"
+ . $param->getName() . " of type " . get_class(
$param )
+ . ". May be handled wrong."
+ );
}
if ( !property_exists( $func, 'taintedVarLinks' ) ) {
$func->taintedVarLinks = [];
@@ -618,7 +705,22 @@
}
}
- protected function mergeTaintDependencies( TypedElementInterface $lhs,
TypedElementInterface $rhs ) {
+ /**
+ * Given a LHS and RHS make all the methods that can set RHS also for
LHS
+ *
+ * Given 2 variables (e.g. $lhs = $rhs ), see to it that any
function/method
+ * which we marked as being able to set the value of rhs, is also marked
+ * as being able to set the value of lhs.
+ *
+ * This also merges the information on what line caused the taint.
+ *
+ * @param TypedElementInterface $lhs Source of method list
+ * @param TypedElementInterface $rhs Destination of merged method list
+ */
+ protected function mergeTaintDependencies(
+ TypedElementInterface $lhs,
+ TypedElementInterface $rhs
+ ) {
$taintLHS = $this->getTaintedness( $lhs );
$taintRHS = $this->getTaintedness( $rhs );
/********************
@@ -629,7 +731,8 @@
$taintLHS < SecurityCheckPlugin::PRESERVE_TAINT ||
$taintRHS !== SecurityCheckPlugin::PRESERVE_TAINT
) {
- $this->debug( __METHOD__, "FIXME merging dependencies
where LHS and RHS are not both preserved taint. lhs=$taintLHS; rhs=$taintRHS" );
+ $this->debug( __METHOD__, "FIXME merging dependencies
where" .
+ " LHS and RHS are not both preserved taint.
lhs=$taintLHS; rhs=$taintRHS" );
} */
if ( $taintRHS & SecurityCheckPlugin::YES_EXEC_TAINT ) {
@@ -665,11 +768,15 @@
}
/**
+ * Mark any function setting a specific variable as EXEC taint
+ *
* If you do something like echo $this->foo;
* This method is called to make all things that set $this->foo
* as TAINT_EXEC.
*
- * TODO delete all dependencies as no longer needed.
+ * @todo delete all dependencies as no longer needed (or are they?)
+ * @param TypedElementInterface $var The variable in question
+ * @param int $taint What taint to mark them as.
*/
protected function markAllDependentMethodsExec(
TypedElementInterface $var,
@@ -691,7 +798,8 @@
$paramTaint = [ 'overall' =>
SecurityCheckPlugin::NO_TAINT ];
foreach ( $paramInfo as $i => $_ ) {
$paramTaint[$i] = $taint;
- // $this->debug( __METHOD__ , "Setting method
$method arg $i as $taint due to depenency on $var" );
+ // $this->debug( __METHOD__ , "Setting method
$method" .
+ // " arg $i as $taint due to depenency
on $var" );
}
$this->setFuncTaint( $method, $paramTaint );
}
@@ -710,7 +818,13 @@
/**
* This happens when someone call foo( $evilTaintedVar );
*
- * It makes sure that anything that foo sets will become tainted.
+ * It makes sure that any variable that the function foo() sets takes on
+ * the taint of the supplied argument.
+ *
+ * @todo FIXME this needs to handle different types of taint.
+ *
+ * @param FunctionInterface $method The function or method in question
+ * @param int $i The number of the argument in question.
*/
protected function markAllDependentVarsYes( FunctionInterface $method,
int $i ) {
if ( $method->isInternal() ) {
@@ -724,11 +838,9 @@
return;
}
$oldMem = memory_get_peak_usage();
- // echo __METHOD__ . $this->dbgInfo() . "Setting all vars
depending on $method as tainted\n";
foreach ( $method->taintedVarLinks[$i] as $var ) {
$curVarTaint = $this->getTaintedness( $var );
$newTaint = $this->mergeAddTaint( $curVarTaint,
SecurityCheckPlugin::YES_TAINT );
- // echo __METHOD__ . $this->dbgInfo() . "Setting $var
as $newTaint due to dependency on $method\n";
$this->setTaintedness( $var, $newTaint );
}
// Maybe delete links??
@@ -740,7 +852,9 @@
}
/**
- * @param int taint
+ * Are any of the YES (i.e HTML_TAINT) taint flags set
+ *
+ * @param int $taint
* @return bool If the variable has known (non-execute taint)
*/
protected function isYesTaint( $taint ) {
@@ -748,7 +862,9 @@
}
/**
- * @param int taint
+ * Does the taint have one of EXEC flags set
+ *
+ * @param int $taint
* @return bool If the variable has any exec taint
*/
protected function isExecTaint( $taint ) {
@@ -771,6 +887,10 @@
* Convert exec to yes taint
*
* Special flags like UNKNOWN or INAPLICABLE are discarded.
+ * Any YES flags are also discarded
+ *
+ * @param int $taint The taint in question
+ * @return int The taint with all the EXEC to yes, and all other flags
off
*/
protected function execToYesTaint( int $taint ) : int {
return ( $taint & SecurityCheckPlugin::EXEC_TAINT ) >> 1;
@@ -795,12 +915,14 @@
/**
* Is taint likely a false positive
- * @param $taint
*
* Taint is a false positive if it has the unknown flag but
* none of the yes flags.
+ *
+ * @param int $taint
+ * @return bool
*/
- protected function isLikelyFalsePositive( $taint ) {
+ protected function isLikelyFalsePositive( int $taint ) : bool {
return ( $taint & SecurityCheckPlugin::UNKNOWN_TAINT ) !== 0
&& ( $taint & SecurityCheckPlugin::YES_TAINT ) === 0;
}
@@ -856,8 +978,9 @@
*
* @param Mixed $node Either a Node or a string, int, etc. The
expression
* @param int $taintedness The taintedness in question
- * @param FunctionInterface $func The function/method we are in.
+ * @param FunctionInterface $curFunc The function/method we are in.
* @return Array numeric keys for each parameter taint and 'overall' key
+ * @suppress PhanTypeMismatchForeach
*/
protected function matchTaintToParam(
$node,
@@ -913,7 +1036,6 @@
}
$paramTaint['overall'] = ( $otherTaint | $taintRemaining ) &
$taintedness;
- // $this->debug( __METHOD__, " otherTaint $otherTaint,
taintRemaining $taintRemaining, taintedness = $taintedness, overall " .
$paramTaint['overall'] );
return $paramTaint;
}
@@ -923,11 +1045,15 @@
* @param string $method __METHOD__ in question
* @param string $msg debug message
*/
- protected function debug( $method, $msg ) {
- if ( !$this->debugOutput ) {
+ public function debug( $method, $msg ) {
+ if ( $this->debugOutput === null ) {
$errorOutput = getenv( "SECCHECK_DEBUG" );
- if ( $errorOutput ) {
+ if ( $errorOutput && $errorOutput !== '-' ) {
$this->debugOutput = fopen( $errorOutput, "w" );
+ } elseif ( $errorOutput === '-' ) {
+ $this->debugOutput = '-';
+ } else {
+ $this->debugOutput = false;
}
}
$line = $method . "\33[1m" . $this->dbgInfo() . " \33[0m" .
$msg . "\n";
@@ -942,9 +1068,12 @@
}
/**
- * Make sure func taint is well formed
+ * Make sure func taint array is well formed
*
- * @param array the taint of a function
+ * @warning Will cause an assertion failure if not well formed.
+ *
+ * @param array $taint the taint of a function
+ * @return void
*/
protected function checkFuncTaint( array $taint ) {
assert(
diff --git a/src/TaintednessVisitor.php b/src/TaintednessVisitor.php
index 35428da..aa4b54c 100644
--- a/src/TaintednessVisitor.php
+++ b/src/TaintednessVisitor.php
@@ -37,25 +37,31 @@
public function visit( Node $node ) : int {
// This method will be called on all nodes for which
// there is no implementation of it's kind visitor.
- //
+
// To see what kinds of nodes are passing through here,
// you can run `Debug::printNode($node)`.
- /* echo __METHOD__ . ' ';
- //var_dump( $this->context );
- echo ' ';
- Debug::printNode($node); */
- # echo __METHOD__ . $this->dbgInfo() . " setting unknown taint
for " . \ast\get_kind_name( $node->kind ) . "\n";
# Debug::printNode( $node );
$this->debug( __METHOD__, "unhandled case " .
\ast\get_kind_name( $node->kind ) );
return SecurityCheckPlugin::UNKNOWN_TAINT;
}
+ /**
+ * @param Node $node
+ * @return int Taint
+ */
public function visitFuncDecl( Decl $node ) : int {
return $this->visitMethod( $node );
}
/**
+ * Visit a method decleration
+ *
+ * At this point we should have already hit a return statement
+ * so if we haven't yet, mark this function as no taint.
+ *
* Also handles FuncDecl
+ * @param Decl $node
+ * @return int Taint
*/
public function visitMethod( Decl $node ) : int {
$method = $this->context->getFunctionLikeInScope(
$this->code_base );
@@ -78,8 +84,6 @@
// until later, so setting this to NO_TAINT here might
miss
// some issues in the inbetween period.
$this->setFuncTaint( $method, [ 'overall' =>
SecurityCheckPlugin::NO_TAINT ] );
-
- // $this->debug( __METHOD__, $method . " is set to no
taint due to lack of return and side effects." );
}
return SecurityCheckPlugin::INAPLICABLE_TAINT;
}
@@ -88,100 +92,189 @@
// separate methods so we can use visit to output debugging
// for anything we miss.
+ /**
+ * @param Node $node
+ * @return int Taint
+ */
public function visitStmtList( Node $node ) : int {
return SecurityCheckPlugin::INAPLICABLE_TAINT;
}
+ /**
+ * @param Node $node
+ * @return int Taint
+ */
public function visitArgList( Node $node ) : int {
return SecurityCheckPlugin::INAPLICABLE_TAINT;
}
+ /**
+ * @param Node $node
+ * @return int Taint
+ */
public function visitParamList( Node $node ) : int {
return SecurityCheckPlugin::INAPLICABLE_TAINT;
}
/**
- * Params should be handled in PreTaintednessVisitor
+ * @note Params should be handled in PreTaintednessVisitor
+ * @param Node $node
+ * @return int Taint
*/
public function visitParam( Node $node ) : int {
return SecurityCheckPlugin::INAPLICABLE_TAINT;
}
+ /**
+ * @param Node $node
+ * @return int Taint
+ */
public function visitClass( Decl $node ) : int {
return SecurityCheckPlugin::INAPLICABLE_TAINT;
}
+ /**
+ * @param Node $node
+ * @return int Taint
+ */
public function visitClassConstDecl( Node $node ) : int {
return SecurityCheckPlugin::INAPLICABLE_TAINT;
}
+ /**
+ * @param Node $node
+ * @return int Taint
+ */
public function visitIf( Node $node ) : int {
return SecurityCheckPlugin::INAPLICABLE_TAINT;
}
+ /**
+ * @param Node $node
+ * @return int Taint
+ */
public function visitThrow( Node $node ) : int {
return SecurityCheckPlugin::INAPLICABLE_TAINT;
}
- // Actual property decleration is PropElem
+ /**
+ * Actual property decleration is PropElem
+ * @param Node $node
+ * @return int Taint
+ */
public function visitPropDecl( Node $node ) : int {
return SecurityCheckPlugin::INAPLICABLE_TAINT;
}
+ /**
+ * @param Node $node
+ * @return int Taint
+ */
public function visitConstElem( Node $node ) : int {
return SecurityCheckPlugin::INAPLICABLE_TAINT;
}
+ /**
+ * @param Node $node
+ * @return int Taint
+ */
public function visitUse( Node $node ) : int {
return SecurityCheckPlugin::INAPLICABLE_TAINT;
}
+ /**
+ * @param Node $node
+ * @return int Taint
+ */
public function visitBreak( Node $node ) : int {
return SecurityCheckPlugin::INAPLICABLE_TAINT;
}
+ /**
+ * @param Node $node
+ * @return int Taint
+ */
public function visitContinue( Node $node ) : int {
return SecurityCheckPlugin::INAPLICABLE_TAINT;
}
+ /**
+ * @param Node $node
+ * @return int Taint
+ */
public function visitGoto( Node $node ) : int {
return SecurityCheckPlugin::INAPLICABLE_TAINT;
}
+ /**
+ * @param Node $node
+ * @return int Taint
+ */
public function visitCatch( Node $node ) : int {
return SecurityCheckPlugin::INAPLICABLE_TAINT;
}
+ /**
+ * @param Node $node
+ * @return int Taint
+ */
public function visitNamespace( Node $node ) : int {
return SecurityCheckPlugin::INAPLICABLE_TAINT;
}
+ /**
+ * @param Node $node
+ * @return int Taint
+ */
public function visitSwitch( Node $node ) : int {
return SecurityCheckPlugin::INAPLICABLE_TAINT;
}
+ /**
+ * @param Node $node
+ * @return int Taint
+ */
public function visitSwitchCase( Node $node ) : int {
return SecurityCheckPlugin::INAPLICABLE_TAINT;
}
+ /**
+ * @param Node $node
+ * @return int Taint
+ */
public function visitWhile( Node $node ) : int {
return SecurityCheckPlugin::INAPLICABLE_TAINT;
}
+ /**
+ * @param Node $node
+ * @return int Taint
+ */
public function visitUnset( Node $node ) : int {
return SecurityCheckPlugin::INAPLICABLE_TAINT;
}
+ /**
+ * @param Node $node
+ * @return int Taint
+ */
public function visitTry( Node $node ) : int {
return SecurityCheckPlugin::INAPLICABLE_TAINT;
}
+ /**
+ * @param Node $node
+ * @return int Taint
+ */
public function visitAssignOp( Node $node ) : int {
return $this->visitAssign( $node );
}
/**
* Also handles visitAssignOp
+ *
+ * @param Node $node
+ * @return int Taint
*/
public function visitAssign( Node $node ) : int {
// echo __METHOD__ . $this->dbgInfo() . ' ';
@@ -210,10 +303,6 @@
$rhsTaintedness = $this->mergeAddTaint(
$rhsTaintedness, $lhsTaintedness );
}
- /* if ( !(SecurityCheckPlugin::PRESERVE_TAINT &
$rhsTaintedness) && (SecurityCheckPlugin::PRESERVE_TAINT & $lhsTaintedness) ) {
- $this->debug( __METHOD__, "Preserve on LHS but not RHS
in assignment" );
- } */
-
// If we're assigning to a variable we know will be output later
// raise an issue now.
if ( !$this->isSafeAssignment( $lhsTaintedness, $rhsTaintedness
) ) {
@@ -226,7 +315,9 @@
);
}
foreach ( $variableObjs as $variableObj ) {
- // echo $this->dbgInfo() . " " . $variableObj . " now
merging in taintedness " . $rhsTaintedness . " (previously $lhsTaintedness)\n";
+ // echo $this->dbgInfo() . " " . $variableObj .
+ // " now merging in taintedness " . $rhsTaintedness
+ // . " (previously $lhsTaintedness)\n";
$this->setTaintedness( $variableObj, $rhsTaintedness,
$override );
try {
if ( !is_object( $node->children['expr'] ) ) {
@@ -234,7 +325,8 @@
}
$rhsObjs = $this->getPhanObjsForNode(
$node->children['expr'] );
} catch ( Exception $e ) {
- $this->debug( __METHOD__, "Cannot get phan
object for RHS of assign " . get_class( $e ) . $e->getMessage() );
+ $this->debug( __METHOD__, "Cannot get phan
object for RHS of assign "
+ . get_class( $e ) . $e->getMessage() );
continue;
}
foreach ( $rhsObjs as $rhsObj ) {
@@ -244,6 +336,10 @@
return $rhsTaintedness;
}
+ /**
+ * @param Node $node
+ * @return int Taint
+ */
public function visitBinaryOp( Node $node ) : int {
$safeBinOps =
// Unsure about BITWISE ops, since
@@ -278,14 +374,28 @@
return $res;
}
+ /**
+ * @todo We need more fine grained handling of arrays.
+ *
+ * @param Node $node
+ * @return int Taint
+ */
public function visitDim( Node $node ) : int {
return $this->getTaintednessNode( $node->children['expr'] );
}
+ /**
+ * @param Node $node
+ * @return int Taint
+ */
public function visitPrint( Node $node ) : int {
return $this->visitEcho( $node );
}
+ /**
+ * @param Node $node
+ * @return int Taint
+ */
public function visitIncludeOrEval( Node $node ) : int {
// FIXME this should be handled differently,
// since any taint is bad for this case unlike echo
@@ -294,6 +404,12 @@
/**
* Also handles print, eval() and include() (for now).
+ *
+ * We assume a web based system, where outputting HTML via echo
+ * is bad. This will have false positives in a CLI environment.
+ *
+ * @param Node $node
+ * @return int Taint
*/
public function visitEcho( Node $node ) : int {
$taintedness = $this->getTaintedness( $node->children['expr'] );
@@ -308,7 +424,10 @@
"Echoing tainted expression ($taintedness)"
. $this->getOriginalTaintLine(
$node->children['expr'] )
);
- } elseif ( is_object( $node->children['expr'] ) || $taintedness
& SecurityCheckPlugin::PRESERVE_TAINT ) {
+ } elseif (
+ is_object( $node->children['expr'] ) ||
+ $taintedness & SecurityCheckPlugin::PRESERVE_TAINT
+ ) {
$phanObjs = $this->getPhanObjsForNode(
$node->children['expr'] );
foreach ( $phanObjs as $phanObj ) {
$this->debug( __METHOD__, "Setting $phanObj
exec due to echo" );
@@ -329,10 +448,18 @@
return SecurityCheckPlugin::NO_TAINT;
}
+ /**
+ * @param Node $node
+ * @return int Taint
+ */
public function visitStaticCall( Node $node ) : int {
return $this->visitMethodCall( $node );
}
+ /**
+ * @param Node $node
+ * @return int Taint
+ */
public function visitNew( Node $node ) : int {
if ( $node->children['class']->kind === \ast\AST_NAME ) {
return $this->visitMethodCall( $node );
@@ -343,7 +470,17 @@
}
/**
- * This handles MethodCall and StaticCall
+ * Somebody calls a method or function
+ *
+ * This has to figure out:
+ * Is the return value of the call tainted
+ * Are any of the arguments tainted
+ * Does the function do anything scary with its arguments
+ * It also has to maintain quite a bit of book-keeping.
+ *
+ * This also handles (function) call, static call, and new operator
+ * @param Node $node
+ * @return int Taint
*/
public function visitMethodCall( Node $node ) : int {
$oldMem = memory_get_peak_usage();
@@ -360,7 +497,10 @@
try {
if ( $node->kind === \ast\AST_NEW ) {
$clazzName =
$node->children['class']->children['name'];
- $fqsen =
FullyQualifiedMethodName::fromStringInContext( $clazzName . '::__construct',
$this->context );
+ $fqsen =
FullyQualifiedMethodName::fromStringInContext(
+ $clazzName . '::__construct',
+ $this->context
+ );
if ( !$this->code_base->hasMethodWithFQSEN(
$fqsen ) ) {
echo __METHOD__ . "FIXME no constructor
or parent class";
throw new exception( "Cannot find
__construct" );
@@ -378,12 +518,14 @@
$funcName = $func->getFQSEN();
$taint = $this->getTaintOfFunction( $func );
} catch ( IssueException $e ) {
- $this->debug( __METHOD__, "FIXME complicated case not
handled. Maybe func not defined." . $e->getIssueInstance() );
+ $this->debug( __METHOD__, "FIXME complicated case not
handled. "
+ . "Maybe func not defined." .
$e->getIssueInstance() );
$func = null;
$funcName = '[UNKNOWN FUNC]';
$taint = [ 'overall' =>
SecurityCheckPlugin::UNKNOWN_TAINT ];
} catch ( Exception $e ) {
- $this->debug( __METHOD__, "FIXME complicated case not
handled. Maybe func not defined. " . get_class( $e ) . $e->getMessage() );
+ $this->debug( __METHOD__, "FIXME complicated case not
handled."
+ . " Maybe func not defined. " . get_class( $e )
. $e->getMessage() );
$func = null;
$funcName = '[UNKNOWN FUNC]';
$taint = [ 'overall' =>
SecurityCheckPlugin::UNKNOWN_TAINT ];
@@ -404,7 +546,8 @@
if ( isset( $taint[$i] ) ) {
$effectiveArgTaintedness = $curArgTaintedness &
( $taint[$i] | $this->execToYesTaint(
$taint[$i] ) );
- # $this->debug( __METHOD__, "effective
$effectiveArgTaintedness via arg $i $funcName" );
+ # $this->debug( __METHOD__, "effective
$effectiveArgTaintedness"
+ # . " via arg $i $funcName" );
} elseif ( ( $taint['overall'] &
( SecurityCheckPlugin::PRESERVE_TAINT |
SecurityCheckPlugin::UNKNOWN_TAINT )
) ) {
@@ -414,14 +557,16 @@
// pass the taint through.
// FIXME, could maybe check if type is safe
like int.
$effectiveArgTaintedness = $curArgTaintedness;
- # $this->debug( __METHOD__, "effective
$effectiveArgTaintedness via preserve or unkown $funcName" );
+ # $this->debug( __METHOD__, "effective
$effectiveArgTaintedness"
+ # . " via preserve or unkown $funcName"
);
} else {
// This parameter has no taint info.
// And overall this function doesn't depend on
param
// for taint and isn't unknown.
// So we consider this argument untainted.
$effectiveArgTaintedness =
SecurityCheckPlugin::NO_TAINT;
- # $this->debug( __METHOD__, "effective
$effectiveArgTaintedness via no taint info $funcName" );
+ # $this->debug( __METHOD__, "effective
$effectiveArgTaintedness"
+ # . " via no taint info $funcName" );
}
// -------Start complex reference parameter bit--------/
@@ -458,7 +603,8 @@
$pobjLinks = $pobj->taintedMethodLinks
?? new Set;
$pobj->taintedMethodLinks =
$methodLinks->union( $pobjLinks );
$methodVar->taintedMethodLinks =&
$pobj->taintedMethodLinks;
- $combinedOrig = (
$pobj->taintedOriginalError ?? '' ) . ( $methodVar->taintedOriginalError ?? ''
);
+ $combinedOrig = (
$pobj->taintedOriginalError ?? '' )
+ . (
$methodVar->taintedOriginalError ?? '' );
if ( strlen( $combinedOrig ) > 255 ) {
$combinedOrig = substr(
$combinedOrig, 0, 250 ) . '...';
}
@@ -475,7 +621,8 @@
// someFunc( $execArg ) for pass by reference where
// the parameter is later executed outside the func.
if ( $func && $this->isYesTaint( $curArgTaintedness ) )
{
- # $this->debug( __METHOD__, "cur arg $i is YES
taint ($curArgTaintedness). Marking dependent $funcName" );
+ # $this->debug( __METHOD__, "cur arg $i is YES
taint " .
+ # "($curArgTaintedness). Marking dependent
$funcName" );
// Mark all dependent vars as tainted.
$this->markAllDependentVarsYes( $func, $i );
}
@@ -497,7 +644,9 @@
$taintedArg = $argument->children['name'] ?? '[arg #' .
( $i + 1 ) . ']';
// We use curArgTaintedness here, as we aren't checking
what taint
// gets passed to return value, but which taint is
EXECed.
- // $this->debug( __METHOD__, "Checking safe assing
$funcName arg=$i paramTaint= " .( $taint[$i] ?? "MISSING" ). " vs argTaint=
$curArgTaintedness" );
+ // $this->debug( __METHOD__, "Checking safe assing
$funcName" .
+ // " arg=$i paramTaint= " . ( $taint[$i] ??
"MISSING" ) .
+ // " vs argTaint= $curArgTaintedness" );
if ( !$this->isSafeAssignment( $taint[$i] ?? 0,
$curArgTaintedness ) ) {
$containingMethod = $this->getCurrentMethod();
$this->plugin->emitIssue(
@@ -549,10 +698,24 @@
| ( $overallArgTaint & ~SecurityCheckPlugin::EXEC_TAINT
);
}
+ /**
+ * A function call
+ *
+ * @param Node $node
+ * @return int Taint
+ */
public function visitCall( Node $node ) : int {
return $this->visitMethodCall( $node );
}
+ /**
+ * A variable (e.g. $foo)
+ *
+ * This always considers superglobals as tainted
+ *
+ * @param Node $node
+ * @return int Taint
+ */
public function visitVar( Node $node ) : int {
try {
$varName = $this->getCtxN( $node )->getVariableName();
@@ -579,6 +742,12 @@
return $this->getTaintednessPhanObj( $variableObj );
}
+ /**
+ * A global decleration. Assume most globals are untainted.
+ *
+ * @param Node $node
+ * @return int Taint
+ */
public function visitGlobal( Node $node ) : int {
assert( isset( $node->children['var'] ) &&
$node->children['var']->kind === \ast\AST_VAR );
$varName = $node->children['var']->children['name'];
@@ -605,6 +774,16 @@
return SecurityCheckPlugin::INAPLICABLE_TAINT;
}
+ /**
+ * Set the taint of the function based on what's returned
+ *
+ * This attempts to match the return value up to the argument
+ * to figure out which argument might taint the function. This won't
+ * work in complex cases though.
+ *
+ * @param Node $node
+ * @return int Taint
+ */
public function visitReturn( Node $node ) : int {
if ( !$this->context->isInFunctionLikeScope() ) {
$this->debug( __METHOD__, "return outside func?" );
@@ -646,6 +825,8 @@
/**
* @suppress PhanTypeMismatchForeach
+ * @param Node $node
+ * @return int Taint
*/
public function visitArray( Node $node ) : int {
$curTaint = SecurityCheckPlugin::NO_TAINT;
@@ -656,6 +837,11 @@
return $curTaint;
}
+ /**
+ * A => B
+ * @param Node $node
+ * @return int Taint
+ */
public function visitArrayElem( Node $node ) : int {
return $this->mergeAddTaint(
$this->getTaintedness( $node->children['value'] ),
@@ -663,26 +849,49 @@
);
}
+ /**
+ * A foreach() loop
+ *
+ * The variable from the loop condition has its taintedness
+ * transferred in PreTaintednessVisitor
+ * @param Node $node
+ * @return int Taint
+ */
public function visitForeach( Node $node ) : int {
// This is handled by PreTaintednessVisitor.
return SecurityCheckPlugin::NO_TAINT;
}
+ /**
+ * @param Node $node
+ * @return int Taint
+ */
public function visitClassConst( Node $node ) : int {
return SecurityCheckPlugin::NO_TAINT;
}
+ /**
+ * @param Node $node
+ * @return int Taint
+ */
public function visitConst( Node $node ) : int {
// We are going to assume nobody is doing stupid stuff like
// define( "foo", $_GET['bar'] );
return SecurityCheckPlugin::NO_TAINT;
}
+ /**
+ * The :: operator (for props)
+ * @param Node $node
+ * @return int Taint
+ */
public function visitStaticProp( Node $node ) : int {
try {
$props = $this->getPhanObjsForNode( $node );
} catch ( Exception $e ) {
- $this->debug( __METHOD__, "Cannot understand static
class prop. " . get_class( $e ) . " - {$e->getMessage()}" );
+ $this->debug( __METHOD__, "Cannot understand static
class prop. "
+ . get_class( $e ) . " - {$e->getMessage()}"
+ );
// Debug::printNode( $node );
return SecurityCheckPlugin::UNKNOWN_TAINT;
}
@@ -698,11 +907,17 @@
return $taint;
}
+ /**
+ * The -> operator (when not a method call)
+ * @param Node $node
+ * @return int Taint
+ */
public function visitProp( Node $node ) : int {
try {
$props = $this->getPhanObjsForNode( $node );
} catch ( Exception $e ) {
- // $this->debug( __METHOD__, "Cannot understand class
prop. " . get_class($e) . " - {$e->getMessage()}" );
+ // $this->debug( __METHOD__, "Cannot understand class
prop. "
+ // . get_class($e) . " - {$e->getMessage()}" );
// Debug::printNode( $node );
return SecurityCheckPlugin::UNKNOWN_TAINT;
}
@@ -726,7 +941,10 @@
is_string(
$node->children['expr']->children['name'] ) &&
is_string( $node->children['prop'] )
) {
- $this->debug( __METHOD__, "Could not find
Property \$" . $node->children['expr']->children['name'] . "->" .
$node->children['prop'] . "" );
+ $this->debug( __METHOD__, "Could not find
Property \$" .
+
$node->children['expr']->children['name'] . "->" .
+ $node->children['prop']
+ );
} else {
$this->debug( __METHOD__, "Unexpected number of
phan objs " . count( $props ) . "" );
Debug::printNode( $node );
@@ -741,20 +959,32 @@
}
/**
- * When a prop is declared
+ * When a class property is declared
+ * @param Node $node
+ * @return int Taint
*/
public function visitPropElem( Node $node ) : int {
assert( $this->context->isInClassScope() );
$clazz = $this->context->getClassInScope( $this->code_base );
assert( $clazz->hasPropertyWithName( $this->code_base,
$node->children['name'] ) );
- $prop = $clazz->getPropertyByNameInContext( $this->code_base,
$node->children['name'], $this->context );
+ $prop = $clazz->getPropertyByNameInContext(
+ $this->code_base,
+ $node->children['name'],
+ $this->context
+ );
// FIXME should this be NO?
- // $this->debug( __METHOD__, "Setting taint preserve if not set
yet for \$" . $node->children['name'] . "" );
+ // $this->debug( __METHOD__, "Setting taint preserve if not set"
+ // . " yet for \$" . $node->children['name'] . "" );
$this->setTaintedness( $prop, SecurityCheckPlugin::NO_TAINT,
false );
return SecurityCheckPlugin::INAPLICABLE_TAINT;
}
+ /**
+ * Ternary operator.
+ * @param Node $node
+ * @return int Taint
+ */
public function visitConditional( Node $node ) : int {
if ( $node->children['true'] === null ) {
// $foo ?: $bar;
@@ -766,6 +996,10 @@
return $this->mergeAddTaint( $t, $f );
}
+ /**
+ * @param Node $node
+ * @return int Taint
+ */
public function visitName( Node $node ) : int {
// FIXME I'm a little unclear on what a name is in php.
// I think this means literal true, false, null
@@ -776,10 +1010,21 @@
return SecurityCheckPlugin::NO_TAINT;
}
+ /**
+ * @todo Is this right? The child condition should
+ * be visited when going in post order analysis anyways,
+ * and the taint of an If statement isn't really its condition.
+ * @param Node $node
+ * @return int Taint
+ */
public function visitIfElem( Node $node ) : int {
return $this->getTaintedness( $node->children['cond'] );
}
+ /**
+ * @param Node $node
+ * @return int Taint
+ */
public function visitUnaryOp( Node $node ) : int {
// ~ and @ are the only two unary ops
// that can preserve taint (others cast bool or int)
@@ -790,6 +1035,10 @@
return $this->getTaintedness( $node->children['expr'] );
}
+ /**
+ * @param Node $node
+ * @return int The taint
+ */
public function visitCast( Node $node ) : int {
// Casting between an array and object maintains
// taint. Casting an object to a string calls __toString().
@@ -805,6 +1054,10 @@
}
/**
+ * The taint is the taint of all the child elements
+ *
+ * @param Node $node
+ * @return int the taint
* @suppress PhanTypeMismatchForeach
*/
public function visitEncapsList( Node $node ) : int {
@@ -815,14 +1068,32 @@
return $taint;
}
+ /**
+ * Visit a node that is always safe
+ *
+ * @param Node $node
+ * @return int Taint
+ */
public function visitIsset( Node $node ) : int {
return SecurityCheckPlugin::NO_TAINT;
}
+ /**
+ * Visit a node that is always safe
+ *
+ * @param Node $node
+ * @return int Taint
+ */
public function visitMagicConst( Node $node ) : int {
return SecurityCheckPlugin::NO_TAINT;
}
+ /**
+ * Visit a node that is always safe
+ *
+ * @param Node $node
+ * @return int Taint
+ */
public function visitInstanceOf( Node $node ) : int {
return SecurityCheckPlugin::NO_TAINT;
}
--
To view, visit https://gerrit.wikimedia.org/r/393537
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: Ic802e332ab0fbabc3434b308a41353eaafbab69b
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/tools/phan/SecurityCheckPlugin
Gerrit-Branch: master
Gerrit-Owner: Brian Wolff <[email protected]>
Gerrit-Reviewer: Brian Wolff <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits