Issues (4122)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

includes/search/SearchMssql.php (7 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * Mssql search engine
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 2 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License along
16
 * with this program; if not, write to the Free Software Foundation, Inc.,
17
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
 * http://www.gnu.org/copyleft/gpl.html
19
 *
20
 * @file
21
 * @ingroup Search
22
 */
23
24
/**
25
 * Search engine hook base class for Mssql (ConText).
26
 * @ingroup Search
27
 */
28
class SearchMssql extends SearchDatabase {
29
	/**
30
	 * Perform a full text search query and return a result set.
31
	 *
32
	 * @param string $term Raw search term
33
	 * @return SqlSearchResultSet
34
	 * @access public
35
	 */
36
	function searchText( $term ) {
37
		$resultSet = $this->db->query( $this->getQuery( $this->filter( $term ), true ) );
38
		return new SqlSearchResultSet( $resultSet, $this->searchTerms );
0 ignored issues
show
It seems like $resultSet defined by $this->db->query($this->...->filter($term), true)) on line 37 can also be of type boolean; however, SqlSearchResultSet::__construct() does only seem to accept object<ResultWrapper>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
39
	}
40
41
	/**
42
	 * Perform a title-only search query and return a result set.
43
	 *
44
	 * @param string $term Raw search term
45
	 * @return SqlSearchResultSet
46
	 * @access public
47
	 */
48
	function searchTitle( $term ) {
49
		$resultSet = $this->db->query( $this->getQuery( $this->filter( $term ), false ) );
50
		return new SqlSearchResultSet( $resultSet, $this->searchTerms );
0 ignored issues
show
It seems like $resultSet defined by $this->db->query($this->...>filter($term), false)) on line 49 can also be of type boolean; however, SqlSearchResultSet::__construct() does only seem to accept object<ResultWrapper>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
51
	}
52
53
	/**
54
	 * Return a partial WHERE clause to limit the search to the given namespaces
55
	 *
56
	 * @return string
57
	 * @private
58
	 */
59
	function queryNamespaces() {
60
		$namespaces = implode( ',', $this->namespaces );
61
		if ( $namespaces == '' ) {
62
			$namespaces = '0';
63
		}
64
		return 'AND page_namespace IN (' . $namespaces . ')';
65
	}
66
67
	/**
68
	 * Return a LIMIT clause to limit results on the query.
69
	 *
70
	 * @param string $sql
71
	 *
72
	 * @return string
73
	 */
74
	function queryLimit( $sql ) {
75
		return $this->db->limitResult( $sql, $this->limit, $this->offset );
0 ignored issues
show
It seems like you code against a concrete implementation and not the interface IDatabase as the method limitResult() does only exist in the following implementations of said interface: Database, DatabaseMysql, DatabaseMysqlBase, DatabaseMysqli, DatabasePostgres, DatabaseSqlite.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
76
	}
77
78
	/**
79
	 * Does not do anything for generic search engine
80
	 * subclasses may define this though
81
	 *
82
	 * @param string $filteredTerm
83
	 * @param bool $fulltext
84
	 * @return string
85
	 */
86
	function queryRanking( $filteredTerm, $fulltext ) {
87
		return ' ORDER BY ftindex.[RANK] DESC'; // return ' ORDER BY score(1)';
88
	}
89
90
	/**
91
	 * Construct the full SQL query to do the search.
92
	 * The guts shoulds be constructed in queryMain()
93
	 *
94
	 * @param string $filteredTerm
95
	 * @param bool $fulltext
96
	 * @return string
97
	 */
98
	function getQuery( $filteredTerm, $fulltext ) {
99
		return $this->queryLimit( $this->queryMain( $filteredTerm, $fulltext ) . ' ' .
100
			$this->queryNamespaces() . ' ' .
101
			$this->queryRanking( $filteredTerm, $fulltext ) . ' ' );
102
	}
103
104
	/**
105
	 * Picks which field to index on, depending on what type of query.
106
	 *
107
	 * @param bool $fulltext
108
	 * @return string
109
	 */
110
	function getIndexField( $fulltext ) {
111
		return $fulltext ? 'si_text' : 'si_title';
112
	}
113
114
	/**
115
	 * Get the base part of the search query.
116
	 *
117
	 * @param string $filteredTerm
118
	 * @param bool $fulltext
119
	 * @return string
120
	 * @private
121
	 */
122 View Code Duplication
	function queryMain( $filteredTerm, $fulltext ) {
123
		$match = $this->parseQuery( $filteredTerm, $fulltext );
124
		$page = $this->db->tableName( 'page' );
0 ignored issues
show
It seems like you code against a concrete implementation and not the interface IDatabase as the method tableName() does only exist in the following implementations of said interface: Database, DatabaseMysql, DatabaseMysqlBase, DatabaseMysqli, DatabasePostgres, DatabaseSqlite.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
125
		$searchindex = $this->db->tableName( 'searchindex' );
0 ignored issues
show
It seems like you code against a concrete implementation and not the interface IDatabase as the method tableName() does only exist in the following implementations of said interface: Database, DatabaseMysql, DatabaseMysqlBase, DatabaseMysqli, DatabasePostgres, DatabaseSqlite.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
126
127
		return 'SELECT page_id, page_namespace, page_title, ftindex.[RANK]' .
128
			"FROM $page,FREETEXTTABLE($searchindex , $match, LANGUAGE 'English') as ftindex " .
129
			'WHERE page_id=ftindex.[KEY] ';
130
	}
131
132
	/** @todo document
133
	 * @param string $filteredText
134
	 * @param bool $fulltext
135
	 * @return string
136
	 */
137
	function parseQuery( $filteredText, $fulltext ) {
138
		global $wgContLang;
139
		$lc = $this->legalSearchChars();
140
		$this->searchTerms = [];
141
142
		# @todo FIXME: This doesn't handle parenthetical expressions.
143
		$m = [];
144
		$q = [];
145
146
		if ( preg_match_all( '/([-+<>~]?)(([' . $lc . ']+)(\*?)|"[^"]*")/',
147
			$filteredText, $m, PREG_SET_ORDER ) ) {
148
			foreach ( $m as $terms ) {
149
				$q[] = $terms[1] . $wgContLang->normalizeForSearch( $terms[2] );
150
151 View Code Duplication
				if ( !empty( $terms[3] ) ) {
152
					$regexp = preg_quote( $terms[3], '/' );
153
					if ( $terms[4] ) {
154
						$regexp .= "[0-9A-Za-z_]+";
155
					}
156
				} else {
157
					$regexp = preg_quote( str_replace( '"', '', $terms[2] ), '/' );
158
				}
159
				$this->searchTerms[] = $regexp;
160
			}
161
		}
162
163
		$searchon = $this->db->addQuotes( implode( ',', $q ) );
164
		$field = $this->getIndexField( $fulltext );
165
		return "$field, $searchon";
166
	}
167
168
	/**
169
	 * Create or update the search index record for the given page.
170
	 * Title and text should be pre-processed.
171
	 *
172
	 * @param int $id
173
	 * @param string $title
174
	 * @param string $text
175
	 * @return bool|ResultWrapper
176
	 */
177
	function update( $id, $title, $text ) {
178
		// We store the column data as UTF-8 byte order marked binary stream
179
		// because we are invoking the plain text IFilter on it so that, and we want it
180
		// to properly decode the stream as UTF-8.  SQL doesn't support UTF8 as a data type
181
		// but the indexer will correctly handle it by this method.  Since all we are doing
182
		// is passing this data to the indexer and never retrieving it via PHP, this will save space
183
		$table = $this->db->tableName( 'searchindex' );
0 ignored issues
show
It seems like you code against a concrete implementation and not the interface IDatabase as the method tableName() does only exist in the following implementations of said interface: Database, DatabaseMysql, DatabaseMysqlBase, DatabaseMysqli, DatabasePostgres, DatabaseSqlite.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
184
		$utf8bom = '0xEFBBBF';
185
		$si_title = $utf8bom . bin2hex( $title );
186
		$si_text = $utf8bom . bin2hex( $text );
187
		$sql = "DELETE FROM $table WHERE si_page = $id;";
188
		$sql .= "INSERT INTO $table (si_page, si_title, si_text) VALUES ($id, $si_title, $si_text)";
189
		return $this->db->query( $sql, 'SearchMssql::update' );
190
	}
191
192
	/**
193
	 * Update a search index record's title only.
194
	 * Title should be pre-processed.
195
	 *
196
	 * @param int $id
197
	 * @param string $title
198
	 * @return bool|ResultWrapper
199
	 */
200
	function updateTitle( $id, $title ) {
201
		$table = $this->db->tableName( 'searchindex' );
0 ignored issues
show
It seems like you code against a concrete implementation and not the interface IDatabase as the method tableName() does only exist in the following implementations of said interface: Database, DatabaseMysql, DatabaseMysqlBase, DatabaseMysqli, DatabasePostgres, DatabaseSqlite.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
202
203
		// see update for why we are using the utf8bom
204
		$utf8bom = '0xEFBBBF';
205
		$si_title = $utf8bom . bin2hex( $title );
206
		$sql = "DELETE FROM $table WHERE si_page = $id;";
207
		$sql .= "INSERT INTO $table (si_page, si_title, si_text) VALUES ($id, $si_title, 0x00)";
208
		return $this->db->query( $sql, 'SearchMssql::updateTitle' );
209
	}
210
}
211