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.

includes/wpinv-payment-functions.php (2 issues)

Labels
1
<?php
2
function wpinv_is_subscription_payment( $invoice = '' ) {
3
	if ( empty( $invoice ) ) {
4
		return false;
5
	}
6
7
	if ( ! is_object( $invoice ) && is_scalar( $invoice ) ) {
8
		$invoice = wpinv_get_invoice( $invoice );
9
	}
10
11
	if ( empty( $invoice ) ) {
12
		return false;
13
	}
14
15
	if ( $invoice->is_renewal() ) {
16
		return true;
17
	}
18
19
	return false;
20
}
21
22
function wpinv_payment_link_transaction_id( $invoice = '' ) {
23
	if ( empty( $invoice ) ) {
24
		return false;
25
	}
26
27
	if ( ! is_object( $invoice ) && is_scalar( $invoice ) ) {
28
		$invoice = wpinv_get_invoice( $invoice );
29
	}
30
31
	if ( empty( $invoice ) ) {
32
		return false;
33
	}
34
35
	return apply_filters( 'wpinv_payment_details_transaction_id-' . $invoice->gateway, $invoice->get_transaction_id(), $invoice->ID, $invoice );
36
}
37
38
function wpinv_subscription_initial_payment_desc( $amount, $period, $interval, $trial_period = '', $trial_interval = 0 ) {
39
	$interval   = (int)$interval > 0 ? (int)$interval : 1;
40
41
	if ( $trial_interval > 0 && ! empty( $trial_period ) ) {
42
		$amount = __( 'Free', 'invoicing' );
43
		$interval = $trial_interval;
44
		$period = $trial_period;
45
	}
46
47
	$description = '';
48
	switch ( $period ) {
49
		case 'D':
50
		case 'day':
51
			$description = wp_sprintf( _n( '%s for the first day.', '%1$s for the first %2$d days.', $interval, 'invoicing' ), $amount, $interval );
52
			break;
53
		case 'W':
54
		case 'week':
55
			$description = wp_sprintf( _n( '%s for the first week.', '%1$s for the first %2$d weeks.', $interval, 'invoicing' ), $amount, $interval );
56
			break;
57
		case 'M':
58
		case 'month':
59
			$description = wp_sprintf( _n( '%s for the first month.', '%1$s for the first %2$d months.', $interval, 'invoicing' ), $amount, $interval );
60
			break;
61
		case 'Y':
62
		case 'year':
63
			$description = wp_sprintf( _n( '%s for the first year.', '%1$s for the first %2$d years.', $interval, 'invoicing' ), $amount, $interval );
64
			break;
65
	}
66
67
	return apply_filters( 'wpinv_subscription_initial_payment_desc', $description, $amount, $period, $interval, $trial_period, $trial_interval );
68
}
69
70
function wpinv_subscription_recurring_payment_desc( $amount, $period, $interval, $bill_times = 0, $trial_period = '', $trial_interval = 0 ) {
71
	$interval   = (int)$interval > 0 ? (int)$interval : 1;
72
	$bill_times = (int)$bill_times > 0 ? (int)$bill_times : 0;
73
74
	$description = '';
75
	switch ( $period ) {
76
		case 'D':
77
		case 'day':
78
			if ( (int)$bill_times > 0 ) {
79
				if ( $interval > 1 ) {
80
					if ( $bill_times > 1 ) {
81
						$description = wp_sprintf( __( '%1$s for each %2$d days, for %3$d installments.', 'invoicing' ), $amount, $interval, $bill_times );
82
					} else {
83
						$description = wp_sprintf( __( '%1$s for %2$d days.', 'invoicing' ), $amount, $interval );
84
					}
85
				} else {
86
					$description = wp_sprintf( _n( '%s for one day.', '%1$s for each day, for %2$d installments.', $bill_times, 'invoicing' ), $amount, $bill_times );
87
				}
88
			} else {
89
				$description = wp_sprintf( _n( '%s for each day.', '%1$s for each %2$d days.', $interval, 'invoicing' ), $amount, $interval );
90
			}
91
			break;
92
		case 'W':
93
		case 'week':
94
			if ( (int)$bill_times > 0 ) {
95
				if ( $interval > 1 ) {
96
					if ( $bill_times > 1 ) {
97
						$description = wp_sprintf( __( '%1$s for each %2$d weeks, for %3$d installments.', 'invoicing' ), $amount, $interval, $bill_times );
98
					} else {
99
						$description = wp_sprintf( __( '%1$s for %2$d weeks.', 'invoicing' ), $amount, $interval );
100
					}
101
				} else {
102
					$description = wp_sprintf( _n( '%s for one week.', '%1$s for each week, for %2$d installments.', $bill_times, 'invoicing' ), $amount, $bill_times );
103
				}
104
			} else {
105
				$description = wp_sprintf( _n( '%s for each week.', '%1$s for each %2$d weeks.', $interval, 'invoicing' ), $amount, $interval );
106
			}
107
			break;
108
		case 'M':
109
		case 'month':
110
			if ( (int)$bill_times > 0 ) {
111
				if ( $interval > 1 ) {
112
					if ( $bill_times > 1 ) {
113
						$description = wp_sprintf( __( '%1$s for each %2$d months, for %3$d installments.', 'invoicing' ), $amount, $interval, $bill_times );
114
					} else {
115
						$description = wp_sprintf( __( '%1$s for %2$d months.', 'invoicing' ), $amount, $interval );
116
					}
117
				} else {
118
					$description = wp_sprintf( _n( '%s for one month.', '%1$s for each month, for %2$d installments.', $bill_times, 'invoicing' ), $amount, $bill_times );
119
				}
120
			} else {
121
				$description = wp_sprintf( _n( '%s for each month.', '%1$s for each %2$d months.', $interval, 'invoicing' ), $amount, $interval );
122
			}
123
			break;
124
		case 'Y':
125
		case 'year':
126
			if ( (int)$bill_times > 0 ) {
127
				if ( $interval > 1 ) {
128
					if ( $bill_times > 1 ) {
129
						$description = wp_sprintf( __( '%1$s for each %2$d years, for %3$d installments.', 'invoicing' ), $amount, $interval, $bill_times );
130
					} else {
131
						$description = wp_sprintf( __( '%1$s for %2$d years.', 'invoicing' ), $amount, $interval );
132
					}
133
				} else {
134
					$description = wp_sprintf( _n( '%s for one year.', '%1$s for each year, for %2$d installments.', $bill_times, 'invoicing' ), $amount, $bill_times );
135
				}
136
			} else {
137
				$description = wp_sprintf( _n( '%s for each year.', '%1$s for each %2$d years.', $interval, 'invoicing' ), $amount, $interval );
138
			}
139
			break;
140
	}
141
142
	return apply_filters( 'wpinv_subscription_recurring_payment_desc', $description, $amount, $period, $interval, $bill_times, $trial_period, $trial_interval );
143
}
144
145
function wpinv_subscription_payment_desc( $invoice ) {
146
	if ( empty( $invoice ) ) {
147
		return null;
148
	}
149
150
	$description = '';
151
	if ( $invoice->is_parent() && $item = $invoice->get_recurring( true ) ) {
152
		if ( $item->has_free_trial() ) {
153
			$trial_period = $item->get_trial_period();
154
			$trial_interval = $item->get_trial_interval();
155
		} else {
156
			$trial_period = '';
157
			$trial_interval = 0;
158
		}
159
160
		$description = wpinv_get_billing_cycle( $invoice->get_total(), $invoice->get_recurring_details( 'total' ), $item->get_recurring_period(), $item->get_recurring_interval(), $item->get_recurring_limit(), $trial_period, $trial_interval, $invoice->get_currency() );
161
	}
162
163
	return apply_filters( 'wpinv_subscription_payment_desc', $description, $invoice );
164
}
165
166
function wpinv_get_billing_cycle( $initial, $recurring, $period, $interval, $bill_times, $trial_period = '', $trial_interval = 0, $currency = '' ) {
167
	$initial_total      = wpinv_round_amount( $initial );
168
	$recurring_total    = wpinv_round_amount( $recurring );
169
170
	if ( $trial_interval > 0 && ! empty( $trial_period ) ) {
171
		// Free trial
172
	} else {
173
		if ( $bill_times == 1 ) {
174
			$recurring_total = $initial_total;
175
		} elseif ( $bill_times > 1 && $initial_total != $recurring_total ) {
176
			$bill_times--;
177
		}
178
	}
179
180
	$initial_amount     = wpinv_price( $initial_total, $currency );
181
	$recurring_amount   = wpinv_price( $recurring_total, $currency );
182
183
	$recurring          = wpinv_subscription_recurring_payment_desc( $recurring_amount, $period, $interval, $bill_times, $trial_period, $trial_interval );
184
185
	if ( $initial_total != $recurring_total ) {
186
		$initial        = wpinv_subscription_initial_payment_desc( $initial_amount, $period, $interval, $trial_period, $trial_interval );
187
188
		$description    = wp_sprintf( __( '%1$s Then %2$s', 'invoicing' ), $initial, $recurring );
189
	} else {
190
		$description    = $recurring;
191
	}
192
193
	return apply_filters( 'wpinv_get_billing_cycle', $description, $initial, $recurring, $period, $interval, $bill_times, $trial_period, $trial_interval, $currency );
194
}
195
196
/**
197
 * Calculates the card name form a card number.
198
 *
199
 *
200
 * @param string $card_number Card number.
201
 * @return string
202
 */
203
function getpaid_get_card_name( $card_number ) {
204
205
	// Known regexes.
206
	$regexes = array(
207
		'/^4/'                     => __( 'Visa', 'invoicing' ),
208
		'/^5[1-5]/'                => __( 'Mastercard', 'invoicing' ),
209
		'/^3[47]/'                 => __( 'Amex', 'invoicing' ),
210
		'/^3(?:0[0-5]|[68])/'      => __( 'Diners Club', 'invoicing' ),
211
		'/^6(?:011|5)/'            => __( 'Discover', 'invoicing' ),
212
		'/^(?:2131|1800|35\d{3})/' => __( 'JCB', 'invoicing' ),
213
	);
214
215
	// Confirm if one matches.
216
	foreach ( $regexes as $regex => $card ) {
217
		if ( preg_match( $regex, $card_number ) >= 1 ) {
218
			return $card;
219
		}
220
	}
221
222
	// None matched.
223
	return __( 'Card', 'invoicing' );
224
225
}
226
227
/**
228
 * Sends an error response during checkout.
229
 *
230
 * @param WPInv_Invoice|int|null $invoice
231
 */
232
function wpinv_send_back_to_checkout( $invoice = null ) {
233
	$response = array( 'success' => false );
234
	$invoice  = wpinv_get_invoice( $invoice );
235
236
	// Was an invoice created?
237
	if ( ! empty( $invoice ) ) {
238
		$invoice             = is_scalar( $invoice ) ? new WPInv_Invoice( $invoice ) : $invoice;
0 ignored issues
show
The condition is_scalar($invoice) is always false.
Loading history...
239
		$response['invoice'] = $invoice->get_id();
240
		do_action( 'getpaid_checkout_invoice_exception', $invoice );
241
	}
242
243
	// Do we have any errors?
244
	if ( wpinv_get_errors() ) {
245
		$response['data'] = getpaid_get_errors_html( true, false );
246
	} else {
247
		$response['data'] = __( 'An error occured while processing your payment. Please try again.', 'invoicing' );
248
	}
249
250
	wp_send_json( $response );
251
}
252
253
/**
254
 * Returns the reCAPTCHA site key.
255
 *
256
 * @return string
257
 */
258
function getpaid_get_recaptcha_site_key() {
259
	return apply_filters( 'getpaid_recaptcha_site_key', wpinv_get_option( 'recaptcha_site_key', '' ) );
260
}
261
262
/**
263
 * Returns the reCAPTCHA secret key.
264
 *
265
 * @return string
266
 */
267
function getpaid_get_recaptcha_secret_key() {
268
	return apply_filters( 'getpaid_recaptcha_secret_key', wpinv_get_option( 'recaptcha_secret_key', '' ) );
269
}
270
271
/**
272
 * Checks if reCAPTCHA is enabled.
273
 *
274
 * @return bool
275
 */
276
function getpaid_is_recaptcha_enabled() {
277
	return wpinv_get_option( 'enable_recaptcha', false ) && getpaid_get_recaptcha_site_key() && getpaid_get_recaptcha_secret_key();
278
}
279
280
/**
281
 * Returns the reCAPTCHA version.
282
 *
283
 * @return string
284
 */
285
function getpaid_get_recaptcha_version() {
286
	return apply_filters( 'getpaid_recaptcha_version', wpinv_get_option( 'recaptcha_version', 'v2' ) );
287
}
288
289
function getpaid_recaptcha_api_url() {
290
	// Prevent conflicts with Ninja Forms recaptcha.
291
	if ( ! empty( $_REQUEST['action'] ) && $_REQUEST['action'] == 'geodir_ninja_forms' ) {
292
		$url = '';
293
	} else {
294
		$url = getpaid_recaptcha_get_api_url();
295
	}
296
297
	return apply_filters( 'getpaid_recaptcha_api_url', $url );
298
}
299
300
function getpaid_recaptcha_get_api_url() {
301
	return add_query_arg(
302
		array(
303
			'render' => 'v2' === getpaid_get_recaptcha_version() ? 'explicit' : getpaid_get_recaptcha_site_key(),
304
		),
305
		'https://www.google.com/recaptcha/api.js'
306
	);
307
}
308
309
/**
310
 * Returns recaptcha settings.
311
 *
312
 * @return array
313
 */
314
function getpaid_get_recaptcha_settings() {
315
	$settings = array(
316
		'enabled' => getpaid_is_recaptcha_enabled(),
317
		'version' => getpaid_get_recaptcha_version(),
318
	);
319
320
	if ( ! getpaid_is_recaptcha_enabled() ) {
321
		return $settings;
322
	}
323
324
	$settings['sitekey'] = getpaid_get_recaptcha_site_key();
325
326
	// Version 2 render params.
327
	if ( 'v2' === getpaid_get_recaptcha_version() ) {
328
		$settings['render_params'] = array(
329
			'sitekey'  => getpaid_get_recaptcha_site_key(),
330
			'theme'    => 'light',
331
			'size'     => 'normal',
332
			'tabindex' => 0,
333
		);
334
	}
335
336
	return apply_filters( 'getpaid_recaptcha_settings', $settings );
337
}
338
339
/**
340
 * Displays reCAPTCHA before payment button.
341
 */
342
function getpaid_display_recaptcha_before_payment_button() {
343
	if ( ! getpaid_is_recaptcha_enabled() || 'v2' !== getpaid_get_recaptcha_version() ) {
344
		return;
345
	}
346
347
	printf(
348
		'<div class="getpaid-recaptcha-wrapper"><div class="g-recaptcha mw-100 overflow-hidden my-2" id="getpaid-recaptcha-%s"></div></div>',
349
		esc_attr( wp_unique_id() )
350
	);
351
}
352
add_action( 'getpaid_before_payment_form_pay_button', 'getpaid_display_recaptcha_before_payment_button' );
353
354
/**
355
 * Validates the reCAPTCHA response.
356
 *
357
 * @param GetPaid_Payment_Form_Submission $submission
358
 */
359
function getpaid_validate_recaptcha_response( $submission ) {
360
361
	// Check if reCAPTCHA is enabled.
362
	if ( ! getpaid_is_recaptcha_enabled() ) {
363
		return;
364
	}
365
366
	$token = $submission->get_field( 'g-recaptcha-response' );
0 ignored issues
show
Are you sure the assignment to $token is correct as $submission->get_field('g-recaptcha-response') targeting GetPaid_Payment_Form_Submission::get_field() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
367
368
	// Abort if no token was provided.
369
	if ( empty( $token ) ) {
370
		wp_send_json_error( 'v2' === getpaid_get_recaptcha_version() ? __( 'Please confirm that you are not a robot.', 'invoicing' ) : __( "Unable to verify that you're not a robot. Please try again.", 'invoicing' ) );
371
	}
372
373
	$result = wp_remote_post(
374
		'https://www.google.com/recaptcha/api/siteverify',
375
		array(
376
			'body' => array(
377
				'secret'   => getpaid_get_recaptcha_secret_key(),
378
				'response' => $token,
379
			),
380
		)
381
	);
382
383
	// Site not reachable, give benefit of doubt.
384
	if ( is_wp_error( $result ) ) {
385
		return;
386
	}
387
388
	$result = json_decode( wp_remote_retrieve_body( $result ), true );
389
390
	if ( empty( $result['success'] ) && ! in_array( 'missing-input-secret', $result['error-codes'], true ) && ! in_array( 'invalid-input-secret', $result['error-codes'], true ) ) {
391
		wp_send_json_error( __( "Unable to verify that you're not a robot. Please try again.", 'invoicing' ) );
392
	}
393
394
	// For v3, check the score.
395
	$minimum_score = apply_filters( 'getpaid_recaptcha_minimum_score', 0.4 );
396
	if ( 'v3' === getpaid_get_recaptcha_version() && ( empty( $result['score'] ) || $result['score'] < $minimum_score ) ) {
397
		wp_send_json_error( __( "Unable to verify that you're not a robot. Please try again.", 'invoicing' ) );
398
	}
399
}
400
add_action( 'getpaid_checkout_error_checks', 'getpaid_validate_recaptcha_response' );
401