2525 */
2626class AlternativeFunctionsSniff extends AbstractFunctionRestrictionsSniff {
2727
28+ /**
29+ * Local input streams which should not be flagged for the file system function checks.
30+ *
31+ * @link http://php.net/manual/en/wrappers.php.php
32+ *
33+ * @var array
34+ */
35+ protected $ allowed_local_streams = array (
36+ 'php://input ' => true ,
37+ 'php://output ' => true ,
38+ 'php://stdin ' => true ,
39+ 'php://stdout ' => true ,
40+ 'php://stderr ' => true ,
41+ );
42+
43+ /**
44+ * Local input streams which should not be flagged for the file system function checks if
45+ * the $filename starts with them.
46+ *
47+ * @link http://php.net/manual/en/wrappers.php.php
48+ *
49+ * @var array
50+ */
51+ protected $ allowed_local_stream_partials = array (
52+ 'php://temp/ ' ,
53+ 'php://fd/ ' ,
54+ );
55+
56+ /**
57+ * Local input stream constants which should not be flagged for the file system function checks.
58+ *
59+ * @link http://php.net/manual/en/wrappers.php.php
60+ *
61+ * @var array
62+ */
63+ protected $ allowed_local_stream_constants = array (
64+ 'STDIN ' => true ,
65+ 'STDOUT ' => true ,
66+ 'STDERR ' => true ,
67+ );
68+
2869 /**
2970 * Groups of functions to restrict.
3071 *
@@ -83,13 +124,13 @@ public function getGroups() {
83124 'since ' => '2.5.0 ' ,
84125 'functions ' => array (
85126 'readfile ' ,
86- 'fopen ' ,
87- 'fsockopen ' ,
88- 'pfsockopen ' ,
89127 'fclose ' ,
128+ 'fopen ' ,
90129 'fread ' ,
91130 'fwrite ' ,
92131 'file_put_contents ' ,
132+ 'fsockopen ' ,
133+ 'pfsockopen ' ,
93134 ),
94135 ),
95136
@@ -202,13 +243,34 @@ public function process_matched_token( $stackPtr, $group_name, $matched_content
202243 return ;
203244 }
204245
205- $ raw_stripped = $ this ->strip_quotes ( $ params [1 ]['raw ' ] );
206- if ( 'php://input ' === $ raw_stripped ) {
207- // This is not a file, but the read-only raw data stream from the request body.
246+ if ( $ this ->is_local_data_stream ( $ params [1 ]['raw ' ] ) === true ) {
247+ // Local data stream.
208248 return ;
209249 }
210250
211- unset( $ params , $ raw_stripped );
251+ unset( $ params );
252+
253+ break ;
254+
255+ case 'readfile ' :
256+ case 'fopen ' :
257+ case 'file_put_contents ' :
258+ /*
259+ * Allow for handling raw data streams from the request body.
260+ */
261+ $ first_param = $ this ->get_function_call_parameter ( $ stackPtr , 1 );
262+
263+ if ( false === $ first_param ) {
264+ // If the file to work with is not set, local data streams don't come into play.
265+ break ;
266+ }
267+
268+ if ( $ this ->is_local_data_stream ( $ first_param ['raw ' ] ) === true ) {
269+ // Local data stream.
270+ return ;
271+ }
272+
273+ unset( $ first_param );
212274
213275 break ;
214276 }
@@ -223,4 +285,29 @@ public function process_matched_token( $stackPtr, $group_name, $matched_content
223285 }
224286 }
225287
288+ /**
289+ * Determine based on the "raw" parameter value, whether a file parameter points to
290+ * a local data stream.
291+ *
292+ * @param string $raw_param_value Raw parameter value.
293+ *
294+ * @return bool True if this is a local data stream. False otherwise.
295+ */
296+ protected function is_local_data_stream ( $ raw_param_value ) {
297+
298+ $ raw_stripped = $ this ->strip_quotes ( $ raw_param_value );
299+ if ( isset ( $ this ->allowed_local_streams [ $ raw_stripped ] )
300+ || isset ( $ this ->allowed_local_stream_constants [ $ raw_param_value ] )
301+ ) {
302+ return true ;
303+ }
304+
305+ foreach ( $ this ->allowed_local_stream_partials as $ partial ) {
306+ if ( strpos ( $ raw_stripped , $ partial ) === 0 ) {
307+ return true ;
308+ }
309+ }
310+
311+ return false ;
312+ }
226313}
0 commit comments