Issues (1182)

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/cli/class-wc-cli-command.php (5 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
/**
4
 * WooCommerce CLI Command.
5
 *
6
 * Base class that must be extended by any WooCommerce sub commands.
7
 *
8
 * @class    WC_CLI_Command
9
 * @version  2.5.0
10
 * @package  WooCommerce/CLI
11
 * @category CLI
12
 * @author   WooThemes
13
 */
14
class WC_CLI_Command extends WP_CLI_Command {
15
16
	/**
17
	 * Add common cli arguments to argument list before WP_Query is run.
18
	 *
19
	 * @since  2.5.0
20
	 * @param  array $base_args  Required arguments for the query (e.g. `post_type`, etc)
21
	 * @param  array $assoc_args Arguments provided in when invoking the command
22
	 * @return array
23
	 */
24
	protected function merge_wp_query_args( $base_args, $assoc_args ) {
25
		$args = array();
26
27
		// date
28
		if ( ! empty( $assoc_args['created_at_min'] ) || ! empty( $assoc_args['created_at_max'] ) || ! empty( $assoc_args['updated_at_min'] ) || ! empty( $assoc_args['updated_at_max'] ) ) {
29
30
			$args['date_query'] = array();
31
32
			// resources created after specified date
33 View Code Duplication
			if ( ! empty( $assoc_args['created_at_min'] ) ) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
34
				$args['date_query'][] = array( 'column' => 'post_date_gmt', 'after' => $this->parse_datetime( $assoc_args['created_at_min'] ), 'inclusive' => true );
35
			}
36
37
			// resources created before specified date
38 View Code Duplication
			if ( ! empty( $assoc_args['created_at_max'] ) ) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
39
				$args['date_query'][] = array( 'column' => 'post_date_gmt', 'before' => $this->parse_datetime( $assoc_args['created_at_max'] ), 'inclusive' => true );
40
			}
41
42
			// resources updated after specified date
43 View Code Duplication
			if ( ! empty( $assoc_args['updated_at_min'] ) ) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
44
				$args['date_query'][] = array( 'column' => 'post_modified_gmt', 'after' => $this->parse_datetime( $assoc_args['updated_at_min'] ), 'inclusive' => true );
45
			}
46
47
			// resources updated before specified date
48 View Code Duplication
			if ( ! empty( $assoc_args['updated_at_max'] ) ) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
49
				$args['date_query'][] = array( 'column' => 'post_modified_gmt', 'before' => $this->parse_datetime( $assoc_args['updated_at_max'] ), 'inclusive' => true );
50
			}
51
		}
52
53
		// Search.
54
		if ( ! empty( $assoc_args['q'] ) ) {
55
			$args['s'] = $assoc_args['q'];
56
		}
57
58
		// Number of post to show per page.
59
		if ( ! empty( $assoc_args['limit'] ) ) {
60
			$args['posts_per_page'] = $assoc_args['limit'];
61
		}
62
63
		// Number of post to displace or pass over.
64
		if ( ! empty( $assoc_args['offset'] ) ) {
65
			$args['offset'] = $assoc_args['offset'];
66
		}
67
68
		// order (ASC or DESC, DESC by default).
69
		if ( ! empty( $assoc_args['order'] ) ) {
70
			$args['order'] = $assoc_args['order'];
71
		}
72
73
		// orderby.
74
		if ( ! empty( $assoc_args['orderby'] ) ) {
75
			$args['orderby'] = $assoc_args['orderby'];
76
77
			// allow sorting by meta value
78
			if ( ! empty( $assoc_args['orderby_meta_key'] ) ) {
79
				$args['meta_key'] = $assoc_args['orderby_meta_key'];
80
			}
81
		}
82
83
		// allow post status change
84
		if ( ! empty( $assoc_args['post_status'] ) ) {
85
			$args['post_status'] = $assoc_args['post_status'];
86
			unset( $assoc_args['post_status'] );
87
		}
88
89
		// filter by a list of post ids
90
		if ( ! empty( $assoc_args['in'] ) ) {
91
			$args['post__in'] = explode( ',', $assoc_args['in'] );
92
			unset( $assoc_args['in'] );
93
		}
94
95
		// exclude by a list of post ids
96
		if ( ! empty( $assoc_args['not_in'] ) ) {
97
			$args['post__not_in'] = explode( ',', $assoc_args['not_in'] );
98
			unset( $assoc_args['not_in'] );
99
		}
100
101
		// posts page.
102
		$args['paged'] = ( isset( $assoc_args['page'] ) ) ? absint( $assoc_args['page'] ) : 1;
103
104
		$args = apply_filters( 'woocommerce_cli_query_args', $args, $assoc_args );
105
106
		return array_merge( $base_args, $args );
107
	}
108
109
	/**
110
	 * Add common cli arguments to argument list before WP_User_Query is run.
111
	 *
112
	 * @since  2.5.0
113
	 * @param  array $base_args required arguments for the query (e.g. `post_type`, etc)
114
	 * @param  array $assoc_args arguments provided in when invoking the command
115
	 * @return array
116
	 */
117
	protected function merge_wp_user_query_args( $base_args, $assoc_args ) {
118
		$args = array();
119
120
		// Custom Role
121
		if ( ! empty( $assoc_args['role'] ) ) {
122
			$args['role'] = $assoc_args['role'];
123
		}
124
125
		// Search
126
		if ( ! empty( $assoc_args['q'] ) ) {
127
			$args['search'] = $assoc_args['q'];
128
		}
129
130
		// Limit number of users returned.
131
		if ( ! empty( $assoc_args['limit'] ) ) {
132
			$args['number'] = absint( $assoc_args['limit'] );
133
		}
134
135
		// Offset
136
		if ( ! empty( $assoc_args['offset'] ) ) {
137
			$args['offset'] = absint( $assoc_args['offset'] );
138
		}
139
140
		// date
141
		if ( ! empty( $assoc_args['created_at_min'] ) || ! empty( $assoc_args['created_at_max'] ) ) {
142
143
			$args['date_query'] = array();
144
145
			// resources created after specified date
146 View Code Duplication
			if ( ! empty( $assoc_args['created_at_min'] ) ) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
147
				$args['date_query'][] = array( 'after' => $this->parse_datetime( $assoc_args['created_at_min'] ), 'inclusive' => true );
148
			}
149
150
			// resources created before specified date
151
			if ( ! empty( $assoc_args['created_at_max'] ) ) {
152
				$args['date_query'][] = array( 'before' => $this->parse_datetime( $assoc_args['created_at_max'] ), 'inclusive' => true );
153
			}
154
		}
155
156
		// Order (ASC or DESC, ASC by default).
157
		if ( ! empty( $assoc_args['order'] ) ) {
158
			$args['order'] = $assoc_args['order'];
159
		}
160
161
		// Orderby.
162
		if ( ! empty( $assoc_args['orderby'] ) ) {
163
			$args['orderby'] = $assoc_args['orderby'];
164
		}
165
166
		$args = apply_filters( 'woocommerce_cli_user_query_args', $args, $assoc_args );
167
168
		return array_merge( $base_args, $args );
169
	}
170
171
	/**
172
	 * Parse an RFC3339 datetime into a MySQl datetime.
173
	 *
174
	 * Invalid dates default to unix epoch.
175
	 *
176
	 * @since  2.5.0
177
	 * @param  string $datetime RFC3339 datetime
178
	 * @return string MySQl datetime (YYYY-MM-DD HH:MM:SS)
179
	 */
180
	protected function parse_datetime( $datetime ) {
181
		// Strip millisecond precision (a full stop followed by one or more digits)
182
		if ( strpos( $datetime, '.' ) !== false ) {
183
			$datetime = preg_replace( '/\.\d+/', '', $datetime );
184
		}
185
186
		// default timezone to UTC
187
		$datetime = preg_replace( '/[+-]\d+:+\d+$/', '+00:00', $datetime );
188
189
		try {
190
191
			$datetime = new DateTime( $datetime, new DateTimeZone( 'UTC' ) );
192
193
		} catch ( Exception $e ) {
194
195
			$datetime = new DateTime( '@0' );
196
197
		}
198
199
		return $datetime->format( 'Y-m-d H:i:s' );
200
	}
201
202
	/**
203
	 * Format a unix timestamp or MySQL datetime into an RFC3339 datetime.
204
	 *
205
	 * @since  2.5.0
206
	 * @param  int|string $timestamp unix timestamp or MySQL datetime
207
	 * @param  bool $convert_to_utc
208
	 * @return string RFC3339 datetime
209
	 */
210
	protected function format_datetime( $timestamp, $convert_to_utc = false ) {
211
		if ( $convert_to_utc ) {
212
			$timezone = new DateTimeZone( wc_timezone_string() );
213
		} else {
214
			$timezone = new DateTimeZone( 'UTC' );
215
		}
216
217
		try {
218
			if ( is_numeric( $timestamp ) ) {
219
				$date = new DateTime( "@{$timestamp}" );
220
			} else {
221
				$date = new DateTime( $timestamp, $timezone );
222
			}
223
224
			// convert to UTC by adjusting the time based on the offset of the site's timezone
225
			if ( $convert_to_utc ) {
226
				$date->modify( -1 * $date->getOffset() . ' seconds' );
227
			}
228
229
		} catch ( Exception $e ) {
230
			$date = new DateTime( '@0' );
231
		}
232
233
		return $date->format( 'Y-m-d\TH:i:s\Z' );
234
	}
235
236
	/**
237
	 * Get formatter object based on supplied arguments.
238
	 *
239
	 * @since  2.5.0
240
	 * @param  array $assoc_args Associative args from CLI to determine formatting
241
	 * @return \WP_CLI\Formatter
242
	 */
243
	protected function get_formatter( $assoc_args ) {
244
		$args = $this->get_format_args( $assoc_args );
245
		return new \WP_CLI\Formatter( $args );
246
	}
247
248
	/**
249
	 * Get default fields for formatter.
250
	 *
251
	 * Class that extends WC_CLI_Command should override this method.
252
	 *
253
	 * @since  2.5.0
254
	 * @return null|string|array
255
	 */
256
	protected function get_default_format_fields() {
257
		return null;
258
	}
259
260
	/**
261
	 * Get format args that will be passed into CLI Formatter.
262
	 *
263
	 * @since  2.5.0
264
	 * @param  array $assoc_args Associative args from CLI
265
	 * @return array Formatter args
266
	 */
267
	protected function get_format_args( $assoc_args ) {
268
		$format_args = array(
269
			'fields' => $this->get_default_format_fields(),
270
			'field'  => null,
271
			'format' => 'table',
272
		);
273
274
		if ( isset( $assoc_args['fields'] ) ) {
275
			$format_args['fields'] = $assoc_args['fields'];
276
		}
277
278
		if ( isset( $assoc_args['field'] ) ) {
279
			$format_args['field'] = $assoc_args['field'];
280
		}
281
282
		if ( ! empty( $assoc_args['format'] ) && in_array( $assoc_args['format'], array( 'count', 'ids', 'table', 'csv', 'json' ) ) ) {
283
			$format_args['format'] = $assoc_args['format'];
284
		}
285
286
		return $format_args;
287
	}
288
289
	/**
290
	 * Flatten multidimensional array in which nested array will be prefixed with
291
	 * parent keys separated with dot char, e.g. given an array:
292
	 *
293
	 *     array(
294
	 *         'a' => array(
295
	 *             'b' => array(
296
	 *                 'c' => ...
297
	 *             )
298
	 *         )
299
	 *     )
300
	 *
301
	 * a flatten array would contain key 'a.b.c' => ...
302
	 *
303
	 * @since 2.5.0
304
	 * @param array  $arr    Array that may contains nested array
305
	 * @param string $prefix Prefix
306
	 *
307
	 * @return array Flattened array
308
	 */
309
	protected function flatten_array( $arr, $prefix = '' ) {
310
		$flattened = array();
311
		foreach ( $arr as $key => $value ) {
312
			if ( is_array( $value ) ) {
313
				if ( sizeof( $value ) > 0 ) {
314
315
					// Full access to whole elements if indices are numerical.
316
					$flattened[ $prefix . $key ] = $value;
317
318
					// This is naive assumption that if element with index zero
319
					// exists then array indices are numberical.
320
					if ( ! empty( $value[0] ) ) {
321
322
						// Allow size of array to be accessed, i.e., a.b.arr.size
323
						$flattened[ $prefix . $key . '.size' ] = sizeof( $value );
324
					}
325
326
					$flattened = array_merge( $flattened, $this->flatten_array( $value, $prefix . $key . '.' ) );
327
				} else {
328
					$flattened[ $prefix . $key ] = '';
329
330
					// Tells user that size of this array is zero.
331
					$flattened[ $prefix . $key . '.size' ] = 0;
332
				}
333
			} else {
334
				$flattened[ $prefix . $key ] = $value;
335
			}
336
		}
337
338
		return $flattened;
339
	}
340
341
	/**
342
	 * Unflatten array will make key 'a.b.c' becomes nested array:
343
	 *
344
	 *     array(
345
	 *         'a' => array(
346
	 *             'b' => array(
347
	 *                 'c' => ...
348
	 *             )
349
	 *         )
350
	 *     )
351
	 *
352
	 * @since  2.5.0
353
	 * @param  array $arr Flattened array
354
	 * @return array
355
	 */
356
	protected function unflatten_array( $arr ) {
357
		$unflatten = array();
358
359
		foreach ( $arr as $key => $value ) {
360
			$key_list  = explode( '.', $key );
361
			$first_key = array_shift( $key_list );
362
			$first_key = $this->get_normalized_array_key( $first_key );
363
			if ( sizeof( $key_list ) > 0 ) {
364
				$remaining_keys = implode( '.', $key_list );
365
				$subarray       = $this->unflatten_array( array( $remaining_keys => $value ) );
366
367
				foreach ( $subarray as $sub_key => $sub_value ) {
368
					$sub_key = $this->get_normalized_array_key( $sub_key );
369
					if ( ! empty( $unflatten[ $first_key ][ $sub_key ] ) ) {
370
						$unflatten[ $first_key ][ $sub_key ] = array_merge_recursive( $unflatten[ $first_key ][ $sub_key ], $sub_value );
371
					} else {
372
						$unflatten[ $first_key ][ $sub_key ] = $sub_value;
373
					}
374
				}
375
			} else {
376
				$unflatten[ $first_key ] = $value;
377
			}
378
		}
379
380
		return $unflatten;
381
	}
382
383
	/**
384
	 * Get normalized array key. If key is a numeric one it will be converted
385
	 * as absolute integer.
386
	 *
387
	 * @since  2.5.0
388
	 * @param  string $key Array key
389
	 * @return string|int
390
	 */
391
	protected function get_normalized_array_key( $key ) {
392
		if ( is_numeric( $key ) ) {
393
			$key = absint( $key );
394
		}
395
		return $key;
396
	}
397
398
	/**
399
	 * Check if the value is equal to 'yes', 'true' or '1'
400
	 *
401
	 * @since 2.5.4
402
	 * @param  string $value
403
	 * @return boolean
404
	 */
405
	protected function is_true( $value ) {
406
		return ( 'yes' === $value || 'true' === $value || '1' === $value ) ? true : false;
407
	}
408
}
409