Issues (850)

Security Analysis    4 potential vulnerabilities

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

  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.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  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.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  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.
  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.
  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.
  Code Injection (1)
Code Injection enables an attacker to execute arbitrary code on the server.
  Variable Injection (2)
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
  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.
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  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.
  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.
  Cross-Site Scripting (1)
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.
  Header Injection
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.

admin/class-getpaid-admin-setup-wizard.php (6 issues)

1
<?php
2
/**
3
 * Setup Wizard Class
4
 *
5
 * Takes new users through some basic steps to setup GetPaid.
6
 *
7
 * @author      AyeCode
8
 * @category    Admin
9
 * @package     GetPaid/Admin
10
 * @version     2.4.0
11
 * @info        GetPaid Setup Wizard.
12
 */
13
defined( 'ABSPATH' ) || exit;
14
15
/**
16
 * GetPaid_Admin_Setup_Wizard class.
17
 */
18
class GetPaid_Admin_Setup_Wizard {
19
20
	/**
21
	 * @var string Current Step
22
	 */
23
	protected $step = '';
24
25
	/**
26
	 * @var string|false Previous Step
27
	 */
28
	protected $previous_step = '';
29
30
	/**
31
	 * @var string|false Next Step
32
	 */
33
	protected $next_step = '';
34
35
	/**
36
	 * @var array All available steps for the setup wizard
37
	 */
38
	protected $steps = array();
39
40
	/**
41
	 * Class constructor.
42
	 *
43
	 * @since 2.4.0
44
	 */
45
	public function __construct() {
46
47
		if ( apply_filters( 'getpaid_enable_setup_wizard', true ) && wpinv_current_user_can_manage_invoicing() ) {
48
			add_action( 'admin_menu', array( $this, 'add_menu' ) );
49
			add_action( 'current_screen', array( $this, 'setup_wizard' ) );
50
			add_action( 'admin_init', array( $this, 'remove_deprecated_functions' ) );
51
		}
52
53
	}
54
55
	/**
56
	 * Add admin menus/screens.
57
	 *
58
	 * @since 2.4.0
59
	 */
60
	public function add_menu() {
61
		add_dashboard_page( '', '', wpinv_get_capability(), 'gp-setup', '' );
62
	}
63
64
	/**
65
	 * Sets up the setup wizard.
66
	 *
67
	 * @since 2.4.0
68
	 */
69
	public function setup_wizard() {
70
71
		if ( isset( $_GET['page'] ) && 'gp-setup' === $_GET['page'] ) {
72
			$this->setup_globals();
73
			$this->maybe_save_current_step();
74
			$this->display_wizard();
75
			exit;
0 ignored issues
show
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
76
		}
77
78
	}
79
80
	public function remove_deprecated_functions() {
81
		// removes deprecated warnings from page
82
		remove_action('admin_print_styles', 'print_emoji_styles');
83
		remove_action( 'admin_head', 'wp_admin_bar_header' );
84
	}
85
86
	/**
87
	 * Sets up class variables.
88
	 *
89
	 * @since 2.4.0
90
	 */
91
	protected function setup_globals() {
92
		$this->steps         = $this->get_setup_steps();
93
		$this->step          = $this->get_current_step();
94
		$this->previous_step = $this->get_previous_step();
95
		$this->next_step     = $this->get_next_step();
96
	}
97
98
	/**
99
	 * Saves the current step.
100
	 *
101
	 * @since 2.4.0
102
	 */
103
	protected function maybe_save_current_step() {
104
		if ( ! empty( $_POST['save_step'] ) && is_callable( $this->steps[ $this->step ]['handler'] ) ) {
105
			call_user_func( $this->steps[ $this->step ]['handler'], $this );
106
		}
107
	}
108
109
	/**
110
	 * Returns the setup steps.
111
	 *
112
	 * @since 2.4.0
113
	 * @return array
114
	 */
115
	protected function get_setup_steps() {
116
117
		$steps = array(
118
119
			'introduction'     => array(
120
				'name'    => __( 'Introduction', 'invoicing' ),
121
				'view'    => array( $this, 'setup_introduction' ),
122
				'handler' => '',
123
			),
124
125
			'business_details' => array(
126
				'name'    => __( 'Business Details', 'invoicing' ),
127
				'view'    => array( $this, 'setup_business' ),
128
				'handler' => '',
129
			),
130
131
			'currency'         => array(
132
				'name'    => __( 'Currency', 'invoicing' ),
133
				'view'    => array( $this, 'setup_currency' ),
134
				'handler' => '',
135
			),
136
137
			'payments'         => array(
138
				'name'    => __( 'Payment Gateways', 'invoicing' ),
139
				'view'    => array( $this, 'setup_payments' ),
140
				'handler' => array( $this, 'setup_payments_save' ),
141
			),
142
143
			'recommend'        => array(
144
				'name'    => __( 'Recommend', 'invoicing' ),
145
				'view'    => array( $this, 'setup_recommend' ),
146
				'handler' => '',
147
			),
148
149
			'next_steps'       => array(
150
				'name'    => __( 'Get Paid', 'invoicing' ),
151
				'view'    => array( $this, 'setup_ready' ),
152
				'handler' => '',
153
			),
154
155
		);
156
157
		return apply_filters( 'getpaid_setup_wizard_steps', $steps );
158
159
	}
160
161
	/**
162
	 * Returns the current step.
163
	 *
164
	 * @since 2.4.0
165
	 * @return string
166
	 */
167
	protected function get_current_step() {
168
		$step = isset( $_GET['step'] ) ? sanitize_key( $_GET['step'] ) : '';
169
		return ! empty( $step ) && in_array( $step, array_keys( $this->steps ) ) ? $step : current( array_keys( $this->steps ) );
170
	}
171
172
	/**
173
	 * Returns the previous step.
174
	 *
175
	 * @since 2.4.0
176
	 * @return string|false
177
	 */
178
	protected function get_previous_step() {
179
180
		$previous = false;
181
		$current  = $this->step;
182
		foreach ( array_keys( $this->steps ) as $step ) {
183
			if ( $current === $step ) {
184
				return $previous;
185
			}
186
187
			$previous = $step;
188
		}
189
190
		return false;
191
	}
192
193
	/**
194
	 * Returns the next step.
195
	 *
196
	 * @since 2.4.0
197
	 * @return string|false
198
	 */
199
	protected function get_next_step() {
200
201
		$on_current = false;
202
		$current    = $this->step;
203
		foreach ( array_keys( $this->steps ) as $step ) {
204
205
			if ( $on_current ) {
206
				return $step;
207
			}
208
209
			if ( $current === $step ) {
210
				return $on_current = true;
0 ignored issues
show
The assignment to $on_current is dead and can be removed.
Loading history...
Bug Best Practice introduced by
The expression return $on_current = true returns the type true which is incompatible with the documented return type false|string.
Loading history...
211
			}
212
}
213
214
		return false;
215
	}
216
217
	/**
218
	 * Displays the setup wizard.
219
	 *
220
	 * @since 2.4.0
221
	 */
222
	public function display_wizard() {
223
		$this->display_header();
224
		$this->display_current_step();
225
		$this->display_footer();
226
	}
227
228
	/**
229
	 * Displays the Wizard Header.
230
	 *
231
	 * @since 2.0.0
232
	 */
233
	public function display_header() {
234
		$steps     = $this->steps;
235
		$current   = $this->step;
236
		$next_step = $this->next_step;
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->next_step can also be of type boolean. However, the property $next_step is declared as type false|string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
237
		array_shift( $steps );
238
		include plugin_dir_path( __FILE__ ) . 'views/wizard-header.php';
239
	}
240
241
	/**
242
	 * Displays the content for the current step.
243
	 *
244
	 * @since 2.4.0
245
	 */
246
	public function display_current_step() {
247
		?>
248
			<div class="gp-setup-content rowx mw-100 text-center mb-3">
249
				<div class="col-12 col-md-5 m-auto">
250
					<?php call_user_func( $this->steps[ $this->step ]['view'], $this ); ?>
251
				</div>
252
			</div>
253
		<?php
254
	}
255
256
	/**
257
	 * Setup Wizard Footer.
258
	 *
259
	 * @since 2.4.0
260
	 */
261
	public function display_footer() {
262
263
		if ( isset( $_GET['step'] ) ) {
264
			$label    = $this->step == 'next_steps' ? __( 'Return to the WordPress Dashboard', 'invoicing' ) : __( 'Skip this step', 'invoicing' );
265
266
			echo '<p class="gd-return-to-dashboard-wrap"> <a href="' . esc_url( $this->get_next_step_link() ) . '" class="gd-return-to-dashboard btn btn-link d-block text-muted">' . esc_html( $label ) . '</a></p>';
267
		}
268
269
		echo '</body></html>';
270
	}
271
272
	/**
273
	 * Introduction step.
274
	 *
275
	 * @since 2.0.0
276
	 */
277
	public function setup_introduction() {
278
		$next_url = $this->get_next_step_link();
279
		include plugin_dir_path( __FILE__ ) . 'views/wizard-introduction.php';
280
	}
281
282
	/**
283
	 * Get the URL for the next step's screen.
284
	 *
285
	 * @param string step   slug (default: current step)
0 ignored issues
show
The type step was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
286
	 *
287
	 * @return string       URL for next step if a next step exists.
288
	 *                      Admin URL if it's the last step.
289
	 *                      Empty string on failure.
290
	 * @since 3.0.0
291
	 */
292
	public function get_next_step_link( $step = '' ) {
293
		if ( ! $step ) {
294
			$step = $this->step;
295
		}
296
297
		$keys = array_keys( $this->steps );
298
		if ( end( $keys ) === $step ) {
299
			return admin_url();
300
		}
301
302
		$step_index = array_search( $step, $keys );
303
		if ( false === $step_index ) {
304
			return '';
305
		}
306
307
		return remove_query_arg( 'settings-updated', add_query_arg( 'step', $keys[ $step_index + 1 ] ) );
308
	}
309
310
	/**
311
	 * Setup maps api.
312
	 *
313
	 * @since 2.0.0
314
	 */
315
	public function setup_business() {
316
		$next_url = $this->get_next_step_link();
317
		$wizard   = $this;
318
		$page     = 'wpinv_settings_general_main';
319
		$section  = 'wpinv_settings_general_main';
320
		include plugin_dir_path( __FILE__ ) . 'views/wizard-settings.php';
321
	}
322
323
	/**
324
	 * Default Location settings.
325
	 *
326
	 * @since 2.0.0
327
	 */
328
	public function setup_currency() {
329
		$next_url = $this->get_next_step_link();
330
		$wizard   = $this;
331
		$page     = 'wpinv_settings_general_currency_section';
332
		$section  = 'wpinv_settings_general_currency_section';
333
		include plugin_dir_path( __FILE__ ) . 'views/wizard-settings.php';
334
	}
335
336
	/**
337
	 * Installation of recommended plugins.
338
	 *
339
	 * @since 1.0.0
340
	 */
341
	public function setup_recommend() {
342
		$next_url            = $this->get_next_step_link();
343
		$recommended_plugins = self::get_recommend_wp_plugins();
344
		include plugin_dir_path( __FILE__ ) . 'views/wizard-plugins.php';
345
	}
346
347
	/**
348
	 * A list of recommended wp.org plugins.
349
	 * @return array
350
	 */
351
	public static function get_recommend_wp_plugins() {
352
		return array(
353
			'ayecode-connect'  => array(
354
				'file' => 'ayecode-connect/ayecode-connect.php',
355
				'url'  => 'https://wordpress.org/plugins/ayecode-connect/',
356
				'slug' => 'ayecode-connect',
357
				'name' => 'AyeCode Connect',
358
				'desc' => __( 'Documentation and Support from within your WordPress admin.', 'invoicing' ),
359
			),
360
			'invoicing-quotes' => array(
361
				'file' => 'invoicing-quotes/wpinv-quote.php',
362
				'url'  => 'https://wordpress.org/plugins/invoicing-quotes/',
363
				'slug' => 'invoicing-quotes',
364
				'name' => 'Customer Quotes',
365
				'desc' => __( 'Create & Send Quotes to Customers and have them accept and pay.', 'invoicing' ),
366
			),
367
			'userswp'          => array(
368
				'file' => 'userswp/userswp.php',
369
				'url'  => 'https://wordpress.org/plugins/userswp/',
370
				'slug' => 'userswp',
371
				'name' => 'UsersWP',
372
				'desc' => __( 'Frontend user login and registration as well as slick profile pages.', 'invoicing' ),
373
			),
374
		);
375
	}
376
377
	/**
378
	 * Dummy Data setup.
379
	 *
380
	 * @since 2.4.0
381
	 */
382
	public function setup_payments() {
383
		$next_url = $this->get_next_step_link();
384
		include plugin_dir_path( __FILE__ ) . 'views/wizard-gateways.php';
385
	}
386
387
	/**
388
	 * Dummy data save.
389
	 *
390
	 * This is done via ajax so we just pass onto the next step.
391
	 *
392
	 * @since 2.0.0
393
	 */
394
	public function setup_payments_save() {
395
		check_admin_referer( 'getpaid-setup-wizard', 'getpaid-setup-wizard' );
396
		wpinv_update_option( 'manual_active', ! empty( $_POST['enable-manual-gateway'] ) );
397
398
		if ( ! empty( $_POST['paypal-email'] ) ) {
399
			wpinv_update_option( 'paypal_email', sanitize_email( $_POST['paypal-email'] ) );
400
			wpinv_update_option( 'paypal_active', 1 );
401
			wpinv_update_option( 'paypal_sandbox', 0 );
402
		}
403
404
		wp_redirect( esc_url_raw( $this->get_next_step_link() ) );
405
		exit;
0 ignored issues
show
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
406
	}
407
408
	/**
409
	 * Final step.
410
	 *
411
	 * @since 2.0.0
412
	 */
413
	public function setup_ready() {
414
		include plugin_dir_path( __FILE__ ) . 'views/wizard-thank-you.php';
415
	}
416
417
}
418
419
new GetPaid_Admin_Setup_Wizard();
420