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/class-wc-auth.php (3 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
 * WooCommerce Auth
4
 *
5
 * Handles wc-auth endpoint requests.
6
 *
7
 * @author   WooThemes
8
 * @category API
9
 * @package  WooCommerce/API
10
 * @since    2.4.0
11
 */
12
13
if ( ! defined( 'ABSPATH' ) ) {
14
	exit;
15
}
16
17
if ( ! class_exists( 'WC_Auth' ) ) :
18
19
class WC_Auth {
20
21
	/**
22
	 * Version.
23
	 *
24
	 * @var int
25
	 */
26
	const VERSION = 1;
27
28
	/**
29
	 * Setup class.
30
	 *
31
	 * @since 2.4.0
32
	 */
33
	public function __construct() {
34
		// Add query vars
35
		add_filter( 'query_vars', array( $this, 'add_query_vars' ), 0 );
36
37
		// Register auth endpoint
38
		add_action( 'init', array( __CLASS__, 'add_endpoint' ), 0 );
39
40
		// Handle auth requests
41
		add_action( 'parse_request', array( $this, 'handle_auth_requests' ), 0 );
42
	}
43
44
	/**
45
	 * Add query vars.
46
	 *
47
	 * @since  2.4.0
48
	 *
49
	 * @param  array $vars
50
	 *
51
	 * @return string[]
52
	 */
53
	public function add_query_vars( $vars ) {
54
		$vars[] = 'wc-auth-version';
55
		$vars[] = 'wc-auth-route';
56
		return $vars;
57
	}
58
59
	/**
60
	 * Add auth endpoint.
61
	 *
62
	 * @since 2.4.0
63
	 */
64
	public static function add_endpoint() {
65
		add_rewrite_rule( '^wc-auth/v([1]{1})/(.*)?', 'index.php?wc-auth-version=$matches[1]&wc-auth-route=$matches[2]', 'top' );
66
	}
67
68
	/**
69
	 * Get scope name.
70
	 *
71
	 * @since 2.4.0
72
	 *
73
	 * @param  string $scope
74
	 *
75
	 * @return string
76
	 */
77
	protected function get_i18n_scope( $scope ) {
78
		$permissions = array(
79
			'read'       => __( 'Read', 'woocommerce' ),
80
			'write'      => __( 'Write', 'woocommerce' ),
81
			'read_write' => __( 'Read/Write', 'woocommerce' ),
82
		);
83
84
		return $permissions[ $scope ];
85
	}
86
87
	/**
88
	 * Return a list of permissions a scope allows.
89
	 *
90
	 * @since  2.4.0
91
	 *
92
	 * @param  string $scope
93
	 *
94
	 * @return array
95
	 */
96
	protected function get_permissions_in_scope( $scope ) {
97
		$permissions = array();
98
		switch ( $scope )  {
99 View Code Duplication
			case 'read' :
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...
100
				$permissions[] = __( 'View coupons', 'woocommerce' );
101
				$permissions[] = __( 'View customers', 'woocommerce' );
102
				$permissions[] = __( 'View orders and sales reports', 'woocommerce' );
103
				$permissions[] = __( 'View products', 'woocommerce' );
104
			break;
105 View Code Duplication
			case 'write' :
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...
106
				$permissions[] = __( 'Create webhooks', 'woocommerce' );
107
				$permissions[] = __( 'Create coupons', 'woocommerce' );
108
				$permissions[] = __( 'Create customers', 'woocommerce' );
109
				$permissions[] = __( 'Create orders', 'woocommerce' );
110
				$permissions[] = __( 'Create products', 'woocommerce' );
111
			break;
112 View Code Duplication
			case 'read_write' :
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...
113
				$permissions[] = __( 'Create webhooks', 'woocommerce' );
114
				$permissions[] = __( 'View and manage coupons', 'woocommerce' );
115
				$permissions[] = __( 'View and manage customers', 'woocommerce' );
116
				$permissions[] = __( 'View and manage orders and sales reports', 'woocommerce' );
117
				$permissions[] = __( 'View and manage products', 'woocommerce' );
118
			break;
119
		}
120
		return apply_filters( 'woocommerce_api_permissions_in_scope', $permissions, $scope );
121
	}
122
123
	/**
124
	 * Build auth urls.
125
	 *
126
	 * @since  2.4.0
127
	 *
128
	 * @param  array $data
129
	 * @param  string $endpoint
130
	 *
131
	 * @return string
132
	 */
133
	protected function build_url( $data, $endpoint ) {
134
		$url = wc_get_endpoint_url( 'wc-auth/v' . self::VERSION, $endpoint, home_url( '/' ) );
135
136
		return add_query_arg( array(
137
			'app_name'            => wc_clean( $data['app_name'] ),
138
			'user_id'             => wc_clean( $data['user_id'] ),
139
			'return_url'          => urlencode( $this->get_formatted_url( $data['return_url'] ) ),
140
			'callback_url'        => urlencode( $this->get_formatted_url( $data['callback_url'] ) ),
141
			'scope'               => wc_clean( $data['scope'] ),
142
		), $url );
143
	}
144
145
	/**
146
	 * Decode and format a URL.
147
	 * @param  string $url
148
	 * @return array
149
	 */
150
	protected function get_formatted_url( $url ) {
151
		$url = urldecode( $url );
152
153
		if ( ! strstr( $url, '://' ) ) {
154
			$url = 'https://' . $url;
155
		}
156
157
		return $url;
158
	}
159
160
	/**
161
	 * Make validation.
162
	 *
163
	 * @since  2.4.0
164
	 */
165
	protected function make_validation() {
166
		$params = array(
167
			'app_name',
168
			'user_id',
169
			'return_url',
170
			'callback_url',
171
			'scope'
172
		);
173
174
		foreach ( $params as $param ) {
175
			if ( empty( $_REQUEST[ $param ] ) ) {
176
				throw new Exception( sprintf( __( 'Missing parameter %s', 'woocommerce' ), $param ) );
177
			}
178
		}
179
180
		if ( ! in_array( $_REQUEST['scope'], array( 'read', 'write', 'read_write' ) ) ) {
181
			throw new Exception( sprintf( __( 'Invalid scope %s', 'woocommerce' ), wc_clean( $_REQUEST['scope'] ) ) );
182
		}
183
184
		foreach ( array( 'return_url', 'callback_url' ) as $param ) {
185
			$param = $this->get_formatted_url( $_REQUEST[ $param ] );
186
187
			if ( false === filter_var( $param, FILTER_VALIDATE_URL ) ) {
188
				throw new Exception( sprintf( __( 'The %s is not a valid URL', 'woocommerce' ), $param ) );
189
			}
190
		}
191
192
		$callback_url = $this->get_formatted_url( $_REQUEST['callback_url'] );
193
194
		if ( 0 !== stripos( $callback_url, 'https://' ) ) {
195
			throw new Exception( __( 'The callback_url need to be over SSL', 'woocommerce' ) );
196
		}
197
	}
198
199
	/**
200
	 * Create keys.
201
	 *
202
	 * @since  2.4.0
203
	 *
204
	 * @param  string $app_name
205
	 * @param  string $app_user_id
206
	 * @param  string $scope
207
	 *
208
	 * @return array
209
	 */
210
	protected function create_keys( $app_name, $app_user_id, $scope ) {
211
		global $wpdb;
212
213
		$description = sprintf( __( '%s - API %s (created on %s at %s).', 'woocommerce' ), wc_clean( $app_name ), $this->get_i18n_scope( $scope ), date_i18n( wc_date_format() ), date_i18n( wc_time_format() ) );
214
		$user        = wp_get_current_user();
215
216
		// Created API keys.
217
		$permissions     = ( in_array( $scope, array( 'read', 'write', 'read_write' ) ) ) ? sanitize_text_field( $scope ) : 'read';
218
		$consumer_key    = 'ck_' . wc_rand_hash();
219
		$consumer_secret = 'cs_' . wc_rand_hash();
220
221
		$wpdb->insert(
222
			$wpdb->prefix . 'woocommerce_api_keys',
223
			array(
224
				'user_id'         => $user->ID,
225
				'description'     => $description,
226
				'permissions'     => $permissions,
227
				'consumer_key'    => wc_api_hash( $consumer_key ),
228
				'consumer_secret' => $consumer_secret,
229
				'truncated_key'   => substr( $consumer_key, -7 )
230
			),
231
			array(
232
				'%d',
233
				'%s',
234
				'%s',
235
				'%s',
236
				'%s',
237
				'%s'
238
			)
239
		);
240
241
		return array(
242
			'key_id'          => $wpdb->insert_id,
243
			'user_id'         => $app_user_id,
244
			'consumer_key'    => $consumer_key,
245
			'consumer_secret' => $consumer_secret,
246
			'key_permissions' => $permissions
247
		);
248
	}
249
250
	/**
251
	 * Post consumer data.
252
	 *
253
	 * @since  2.4.0
254
	 *
255
	 * @param  array  $consumer_data
256
	 * @param  string $url
257
	 *
258
	 * @return bool
259
	 * @throws Exception
260
	 */
261
	protected function post_consumer_data( $consumer_data, $url ) {
262
		$params = array(
263
			'body'      => json_encode( $consumer_data ),
264
			'timeout'   => 60,
265
			'headers'   => array(
266
				'Content-Type' => 'application/json;charset=' . get_bloginfo( 'charset' ),
267
			)
268
		);
269
270
		$response = wp_safe_remote_post( esc_url_raw( $url ), $params );
271
272
		if ( is_wp_error( $response ) ) {
273
			throw new Exception( $response->get_error_message() );
274
		} else if ( 200 != $response['response']['code'] ) {
275
			throw new Exception( __( 'An error occurred in the request and at the time were unable to send the consumer data', 'woocommerce' ) );
276
		}
277
278
		return true;
279
	}
280
281
	/**
282
	 * Handle auth requests.
283
	 *
284
	 * @since 2.4.0
285
	 */
286
	public function handle_auth_requests() {
287
		global $wp;
288
289
		if ( ! empty( $_GET['wc-auth-version'] ) ) {
290
			$wp->query_vars['wc-auth-version'] = $_GET['wc-auth-version'];
291
		}
292
293
		if ( ! empty( $_GET['wc-auth-route'] ) ) {
294
			$wp->query_vars['wc-auth-route'] = $_GET['wc-auth-route'];
295
		}
296
297
		// wc-auth endpoint requests
298
		if ( ! empty( $wp->query_vars['wc-auth-version'] ) && ! empty( $wp->query_vars['wc-auth-route'] ) ) {
299
			$this->auth_endpoint( $wp->query_vars['wc-auth-route'] );
300
		}
301
	}
302
303
	/**
304
	 * Auth endpoint.
305
	 *
306
	 * @since 2.4.0
307
	 *
308
	 * @param string $route
309
	 */
310
	protected function auth_endpoint( $route ) {
311
		ob_start();
312
313
		$consumer_data = array();
314
315
		try {
316
			if ( 'yes' !== get_option( 'woocommerce_api_enabled' ) ) {
317
				throw new Exception( __( 'API disabled!', 'woocommerce' ) );
318
			}
319
320
			$route = strtolower( wc_clean( $route ) );
321
			$this->make_validation();
322
323
			// Login endpoint
324
			if ( 'login' == $route && ! is_user_logged_in() ) {
325
				wc_get_template( 'auth/form-login.php', array(
326
					'app_name'     => $_REQUEST['app_name'],
327
					'return_url'   => add_query_arg( array( 'success' => 0, 'user_id' => wc_clean( $_REQUEST['user_id'] ) ), $this->get_formatted_url( $_REQUEST['return_url'] ) ),
328
					'redirect_url' => $this->build_url( $_REQUEST, 'authorize' ),
329
				) );
330
331
				exit;
332
333
			// Redirect with user is logged in
334
			} else if ( 'login' == $route && is_user_logged_in() ) {
335
				wp_redirect( esc_url_raw( $this->build_url( $_REQUEST, 'authorize' ) ) );
336
				exit;
337
338
			// Redirect with user is not logged in and trying to access the authorize endpoint
339
			} else if ( 'authorize' == $route && ! is_user_logged_in() ) {
340
				wp_redirect( esc_url_raw( $this->build_url( $_REQUEST, 'login' ) ) );
341
				exit;
342
343
			// Authorize endpoint
344
			} else if ( 'authorize' == $route && current_user_can( 'manage_woocommerce' ) ) {
345
				wc_get_template( 'auth/form-grant-access.php', array(
346
					'app_name'    => $_REQUEST['app_name'],
347
					'return_url'  => add_query_arg( array( 'success' => 0, 'user_id' => wc_clean( $_REQUEST['user_id'] ) ), $this->get_formatted_url( $_REQUEST['return_url'] ) ),
348
					'scope'       => $this->get_i18n_scope( wc_clean( $_REQUEST['scope'] ) ),
349
					'permissions' => $this->get_permissions_in_scope( wc_clean( $_REQUEST['scope'] ) ),
350
					'granted_url' => wp_nonce_url( $this->build_url( $_REQUEST, 'access_granted' ), 'wc_auth_grant_access', 'wc_auth_nonce' ),
351
					'logout_url'  => wp_logout_url( $this->build_url( $_REQUEST, 'login' ) ),
352
					'user'        => wp_get_current_user()
353
				) );
354
				exit;
355
356
			// Granted access endpoint
357
			} else if ( 'access_granted' == $route && current_user_can( 'manage_woocommerce' ) ) {
358
				if ( ! isset( $_GET['wc_auth_nonce'] ) || ! wp_verify_nonce( $_GET['wc_auth_nonce'], 'wc_auth_grant_access' ) ) {
359
					throw new Exception( __( 'Invalid nonce verification', 'woocommerce' ) );
360
				}
361
362
				$consumer_data = $this->create_keys( $_REQUEST['app_name'], $_REQUEST['user_id'], $_REQUEST['scope'] );
363
				$response      = $this->post_consumer_data( $consumer_data, $this->get_formatted_url( $_REQUEST['callback_url'] ) );
364
365
				if ( $response ) {
366
					wp_redirect( esc_url_raw( add_query_arg( array( 'success' => 1, 'user_id' => wc_clean( $_REQUEST['user_id'] ) ), $this->get_formatted_url( $_REQUEST['return_url'] ) ) ) );
367
					exit;
368
				}
369
			} else {
370
				throw new Exception( __( 'You do not have permissions to access this page!', 'woocommerce' ) );
371
			}
372
		} catch ( Exception $e ) {
373
			$this->maybe_delete_key( $consumer_data );
374
375
			wp_die( sprintf( __( 'Error: %s', 'woocommerce' ), $e->getMessage() ), __( 'Access Denied', 'woocommerce' ), array( 'response' => 401 ) );
376
		}
377
	}
378
379
	/**
380
	 * Maybe delete key.
381
	 *
382
	 * @since 2.4.0
383
	 *
384
	 * @param array $key
385
	 */
386
	private function maybe_delete_key( $key ) {
387
		global $wpdb;
388
389
		if ( isset( $key['key_id'] ) ) {
390
			$wpdb->delete( $wpdb->prefix . 'woocommerce_api_keys', array( 'key_id' => $key['key_id'] ), array( '%d' ) );
391
		}
392
	}
393
}
394
395
endif;
396
397
return new WC_Auth();
398