Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
92.20% covered (success)
92.20%
532 / 577
34.78% covered (danger)
34.78%
8 / 23
CRAP
0.00% covered (danger)
0.00%
0 / 1
MWVisitor
92.20% covered (success)
92.20%
532 / 577
34.78% covered (danger)
34.78%
8 / 23
233.32
0.00% covered (danger)
0.00%
0 / 1
 analyzeCallNode
95.83% covered (success)
95.83%
23 / 24
0.00% covered (danger)
0.00%
0 / 1
11
 checkExternalLink
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
3
 doSelectWrapperSpecialHandling
100.00% covered (success)
100.00%
22 / 22
100.00% covered (success)
100.00%
1 / 1
6
 triggerHook
79.49% covered (warning)
79.49%
31 / 39
0.00% covered (danger)
0.00%
0 / 1
10.86
 hookArgsContainReference
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
4
 extractHookArgs
83.33% covered (warning)
83.33%
5 / 6
0.00% covered (danger)
0.00%
0 / 1
3.04
 getHookTypeForRegistrationMethod
71.43% covered (warning)
71.43%
5 / 7
0.00% covered (danger)
0.00%
0 / 1
4.37
 handleNormalHookRegistration
58.33% covered (warning)
58.33%
7 / 12
0.00% covered (danger)
0.00%
0 / 1
5.16
 handleParserHookRegistration
83.33% covered (warning)
83.33%
5 / 6
0.00% covered (danger)
0.00%
0 / 1
3.04
 registerHook
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 visitReturn
100.00% covered (success)
100.00%
20 / 20
100.00% covered (success)
100.00%
1 / 1
6
 handleGetQueryInfoReturn
96.67% covered (success)
96.67%
29 / 30
0.00% covered (danger)
0.00%
0 / 1
9
 checkMakeList
97.73% covered (success)
97.73%
43 / 44
0.00% covered (danger)
0.00%
0 / 1
12
 literalListConstToName
66.67% covered (warning)
66.67%
10 / 15
0.00% covered (danger)
0.00%
0 / 1
8.81
 checkSQLOptions
96.55% covered (success)
96.55%
28 / 29
0.00% covered (danger)
0.00%
0 / 1
7
 checkJoinCond
77.78% covered (warning)
77.78%
35 / 45
0.00% covered (danger)
0.00%
0 / 1
14.85
 visitReturnOfFunctionHook
95.00% covered (success)
95.00%
19 / 20
0.00% covered (danger)
0.00%
0 / 1
12
 getCallableFromHookRegistration
90.00% covered (success)
90.00%
18 / 20
0.00% covered (danger)
0.00%
0 / 1
12.14
 getSingleCallable
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
5
 getCallbackForVar
81.82% covered (warning)
81.82%
9 / 11
0.00% covered (danger)
0.00%
0 / 1
5.15
 visitAssign
100.00% covered (success)
100.00%
27 / 27
100.00% covered (success)
100.00%
1 / 1
16
 detectHTMLForm
97.66% covered (success)
97.66%
167 / 171
0.00% covered (danger)
0.00%
0 / 1
56
 visitArray
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3namespace SecurityCheckPlugin;
4
5use ast\Node;
6use Phan\Analysis\PostOrderAnalysisVisitor;
7use Phan\AST\ContextNode;
8use Phan\Exception\CodeBaseException;
9use Phan\Exception\InvalidFQSENException;
10use Phan\Exception\IssueException;
11use Phan\Language\Element\FunctionInterface;
12use Phan\Language\Element\Method;
13use Phan\Language\FQSEN\FullyQualifiedClassName;
14use Phan\Language\FQSEN\FullyQualifiedFunctionLikeName;
15use Phan\Language\FQSEN\FullyQualifiedFunctionName;
16use Phan\Language\FQSEN\FullyQualifiedMethodName;
17use Phan\Language\UnionType;
18
19/**
20 * MediaWiki specific node visitor
21 *
22 * Copyright (C) 2017  Brian Wolff <bawolff@gmail.com>
23 *
24 * This program is free software; you can redistribute it and/or modify
25 * it under the terms of the GNU General Public License as published by
26 * the Free Software Foundation; either version 2 of the License, or
27 * (at your option) any later version.
28 *
29 * This program is distributed in the hope that it will be useful,
30 * but WITHOUT ANY WARRANTY; without even the implied warranty of
31 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
32 * GNU General Public License for more details.
33 *
34 * You should have received a copy of the GNU General Public License along
35 * with this program; if not, write to the Free Software Foundation, Inc.,
36 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
37 */
38class MWVisitor extends TaintednessVisitor {
39    /**
40     * @todo This is a temporary hack. Proper solution is refactoring/avoiding overrideContext
41     * @var bool|null
42     * @suppress PhanWriteOnlyProtectedProperty
43     */
44    protected $isHook;
45
46    /**
47     * Try and recognize hook registration
48     * @inheritDoc
49     */
50    protected function analyzeCallNode( Node $node, iterable $funcs ): void {
51        parent::analyzeCallNode( $node, $funcs );
52        if ( !isset( $node->children['method'] ) ) {
53            // Called by visitCall
54            return;
55        }
56
57        assert( is_array( $funcs ) && count( $funcs ) === 1 );
58        $method = $funcs[0];
59        assert( $method instanceof Method );
60
61        // Should this be getDefiningFQSEN() instead?
62        $methodName = (string)$method->getFQSEN();
63        $parserFQSEN = MediaWikiHooksHelper::getInstance()->getMwParserClassFQSEN( $this->code_base )->__toString();
64        // $this->debug( __METHOD__, "Checking to see if we should register $methodName" );
65        switch ( $methodName ) {
66            case "$parserFQSEN::setFunctionHook":
67            case "$parserFQSEN::setHook":
68                $type = $this->getHookTypeForRegistrationMethod( $methodName );
69                if ( $type === null ) {
70                    break;
71                }
72                // $this->debug( __METHOD__, "registering $methodName as $type" );
73                $this->handleParserHookRegistration( $node, $type );
74                break;
75            case '\Hooks::register':
76                $this->handleNormalHookRegistration( $node );
77                break;
78            case '\Hooks::run':
79            case '\Hooks::runWithoutAbort':
80                $this->triggerHook( $node );
81                break;
82            case '\Linker::makeExternalLink':
83                $this->checkExternalLink( $node );
84                break;
85            default:
86                $this->doSelectWrapperSpecialHandling( $node, $method );
87        }
88    }
89
90    /**
91     * Linker::makeExternalLink escaping depends on third argument
92     *
93     * @param Node $node
94     */
95    private function checkExternalLink( Node $node ): void {
96        $escapeArg = $this->resolveValue( $node->children['args']->children[2] ?? true );
97        $text = $node->children['args']->children[1] ?? null;
98        if ( !$escapeArg && $text instanceof Node ) {
99            $this->maybeEmitIssueSimplified(
100                new Taintedness( SecurityCheckPlugin::HTML_EXEC_TAINT ),
101                $text,
102                "Calling Linker::makeExternalLink with user controlled text " .
103                "and third argument set to false"
104            );
105        }
106    }
107
108    /**
109     * Special casing for complex format of IDatabase::select
110     *
111     * This handles the $options, and $join_cond. Other args are
112     * handled through normal means
113     *
114     * @param Node $node Either an AST_METHOD_CALL or AST_STATIC_CALL
115     * @param Method $method
116     */
117    private function doSelectWrapperSpecialHandling( Node $node, Method $method ): void {
118        $relevantMethods = [
119            'makeList' => true,
120            'select' => true,
121            'selectField' => true,
122            'selectFieldValues' => true,
123            'selectSQLText' => true,
124            'selectRowCount' => true,
125            'selectRow' => true,
126        ];
127