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/class-getpaid-customer.php (1 issue)

1
<?php
2
/**
3
 * Contains the customer class
4
 *
5
 * @since   1.0.15
6
 */
7
8
defined( 'ABSPATH' ) || exit;
9
10
/**
11
 * Customer class.
12
 *
13
 * @since 1.0.15
14
 *
15
 */
16
class GetPaid_Customer extends GetPaid_Data {
17
18
	/**
19
	 * Which data store to load.
20
	 *
21
	 * @var string
22
	 */
23
    protected $data_store_name = 'customer';
24
25
    /**
26
	 * This is the name of this object type.
27
	 *
28
	 * @var string
29
	 */
30
	protected $object_type = 'customer';
31
32
	/**
33
	 * Get the customer if ID is passed, otherwise the customer is new and empty.
34
	 *
35
	 * @param int|string|GetPaid_Customer|object $customer customer id, object, or email.
36
	 */
37
	public function __construct( $customer = 0 ) {
38
39
        // Setup default customer data.
40
        $this->setup_default_data();
41
42
		if ( is_numeric( $customer ) ) {
43
			$this->set_id( $customer );
44
		} elseif ( $customer instanceof self ) {
45
			$this->set_id( $customer->get_id() );
46
		} elseif ( is_string( $customer ) && $customer_id = self::get_customer_id_by( $customer, 'email' ) ) {
47
			$this->set_id( $customer_id );
48
		} elseif ( ! empty( $customer->id ) ) {
49
			$this->set_id( $customer->id );
50
		}
51
52
        // Load the datastore.
53
		$this->data_store = GetPaid_Data_Store::load( $this->data_store_name );
54
55
		if ( $this->get_id() > 0 ) {
56
			$this->data_store->read( $this );
57
        }
58
59
        $this->set_object_read( true );
60
	}
61
62
    /**
63
	 * Sets up default customer data.
64
	 */
65
	private function setup_default_data() {
66
67
        $this->data = array(
68
			'user_id'        => 0,
69
			'email'          => '',
70
			'email_cc'       => '',
71
			'status'         => 'active',
72
			'purchase_value' => 0,
73
			'purchase_count' => 0,
74
			'date_created'   => current_time( 'mysql' ),
75
			'date_modified'  => current_time( 'mysql' ),
76
			'uuid'           => wp_generate_uuid4(),
77
		);
78
79
        // Add address fields.
80
		foreach ( array_keys( getpaid_user_address_fields() ) as $field ) {
81
82
            if ( isset( $this->data[ $field ] ) ) {
83
                continue;
84
            }
85
86
            // Country.
87
            if ( 'country' === $field ) {
88
                $this->data[ $field ] = wpinv_get_default_country();
89
                continue;
90
            }
91
92
            // State.
93
            if ( 'state' === $field ) {
94
                $this->data[ $field ] = wpinv_get_default_state();
95
                continue;
96
            }
97
98
			$this->data[ $field ] = '';
99
		}
100
101
        $this->default_data = $this->data;
102
	}
103
104
	/**
105
	 * Given a customer email or user id, it returns a customer id.
106
	 *
107
	 * @static
108
	 * @param string $value
109
	 * @since 1.0.15
110
	 * @return int
111
	 */
112
	public static function get_customer_id_by( $value, $by = 'email' ) {
113
		global $wpdb;
114
115
        // Prepare value.
116
        if ( 'email' === $by ) {
117
            $value = sanitize_email( $value );
118
        } elseif ( 'user_id' === $by ) {
119
            $value = absint( $value );
120
        } else {
121
            return 0;
122
        }
123
124
        if ( empty( $value ) ) {
125
            return 0;
126
        }
127
128
		// Maybe retrieve from the cache.
129
        $cache_key   = 'getpaid_customer_ids_by_' . $by;
130
		$customer_id = wp_cache_get( $value, $cache_key );
131
		if ( false !== $customer_id ) {
132
			return $customer_id;
133
		}
134
135
        if ( 'email' === $by ) {
136
            $customer_id = (int) $wpdb->get_var(
137
                $wpdb->prepare( "SELECT id FROM {$wpdb->prefix}getpaid_customers WHERE email=%s LIMIT 1", $value )
138
            );
139
        } elseif ( 'user_id' === $by ) {
140
            $customer_id = (int) $wpdb->get_var(
141
                $wpdb->prepare( "SELECT id FROM {$wpdb->prefix}getpaid_customers WHERE user_id=%d LIMIT 1", $value )
142
            );
143
        }
144
145
		// Update the cache with our data
146
		wp_cache_set( $value, $customer_id, $cache_key );
147
148
		return $customer_id;
149
150
	}
151
152
	/**
153
     * Clears the customer's cache.
154
     */
155
    public function clear_cache() {
156
        wp_cache_delete( $this->get( 'email' ), 'getpaid_customer_ids_by_email' );
157
        wp_cache_delete( $this->get( 'user_id' ), 'getpaid_customer_ids_by_user_id' );
158
		wp_cache_delete( $this->get_id(), 'getpaid_customers' );
159
	}
160
161
	/*
162
	|--------------------------------------------------------------------------
163
	| CRUD methods
164
	|--------------------------------------------------------------------------
165
	|
166
	| Methods which create, read, update and delete discounts from the database.
167
	|
168
    */
169
170
    /*
171
	|--------------------------------------------------------------------------
172
	| Getters
173
	|--------------------------------------------------------------------------
174
	*/
175
176
    /**
177
     * Margic method for retrieving a property.
178
     *
179
     * @param  string $key The key to fetch.
180
     * @param  string $context View or edit context.
181
     */
182
    public function get( $key, $context = 'view' ) {
183
184
        // Maybe strip _wpinv_ prefix from key.
185
        $key = str_replace( '_wpinv_', '', $key );
186
187
        // Check if we have a helper method for that.
188
        if ( method_exists( $this, 'get_' . $key ) ) {
189
            return call_user_func( array( $this, 'get_' . $key ), $context );
190
        }
191
192
		return $this->get_prop( $key, $context );
193
194
    }
195
196
	/*
197
	|--------------------------------------------------------------------------
198
	| Setters
199
	|--------------------------------------------------------------------------
200
	|
201
	| Functions for setting customer data. These should not update anything in the
202
	| database itself and should only change what is stored in the class
203
	| object.
204
	*/
205
206
    /**
207
     * Margic method for setting a property.
208
     *
209
     * @param string $key The key to fetch.
210
     * @param mixed $value The new value.
211
     */
212
    public function set( $key, $value ) {
213
214
        // Check if we have a helper method for that.
215
        if ( method_exists( $this, 'set_' . $key ) ) {
216
            return call_user_func( array( $this, 'set_' . $key ), $value );
0 ignored issues
show
Security Code Execution introduced by
array($this, 'set_' . $key) can contain request data and is used in code execution context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read from $_POST, and Data is passed through wpinv_clean(), and wpinv_clean($_POST[$key]) is assigned to $save_data
    in includes/admin/class-getpaid-admin-profile.php on line 180
  2. $save_data is assigned to $key
    in includes/admin/class-getpaid-admin-profile.php on line 196
  3. GetPaid_Customer::set() is called
    in includes/admin/class-getpaid-admin-profile.php on line 197
  4. Enters via parameter $key
    in includes/class-getpaid-customer.php on line 212

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
217
        }
218
219
		return $this->set_prop( $key, $value );
220
221
    }
222
223
	/**
224
	 * Sets customer status.
225
	 *
226
	 * @since 1.0.0
227
	 * @param  string $status New status.
228
	 */
229
	public function set_status( $status ) {
230
231
		if ( in_array( $status, array( 'active', 'inactive', 'blocked' ), true ) ) {
232
			return $this->set_prop( 'status', $status );
233
		}
234
235
		$this->set_prop( 'status', 'inactive' );
236
	}
237
238
	/**
239
	 * Sets the purchase value.
240
	 *
241
	 * @since 1.0.0
242
	 * @param float $purchase_value.
243
	 */
244
	public function set_purchase_value( $purchase_value ) {
245
		$this->set_prop( 'purchase_value', (float) $purchase_value );
246
	}
247
248
    /**
249
	 * Sets the purchase count.
250
	 *
251
	 * @since 1.0.0
252
	 * @param int $purchase_count.
253
	 */
254
	public function set_purchase_count( $purchase_count ) {
255
		$this->set_prop( 'purchase_count', absint( $purchase_count ) );
256
	}
257
258
    /**
259
	 * Sets the user id.
260
	 *
261
	 * @since 1.0.0
262
	 * @param int $user_id.
263
	 */
264
	public function set_user_id( $user_id ) {
265
		$this->set_prop( 'user_id', absint( $user_id ) );
266
	}
267
268
    /**
269
	 * Sets the email.
270
	 *
271
	 * @since 1.0.0
272
	 * @param string $email.
273
	 */
274
	public function set_email( $email ) {
275
        $email = is_string( $email ) ? sanitize_email( $email ) : '';
276
		$this->set_prop( 'email', $email );
277
	}
278
279
    /**
280
	 * Sets the email cc.
281
	 *
282
	 * @since 1.0.0
283
	 * @param string $email_cc.
284
	 */
285
	public function set_email_cc( $email_cc ) {
286
        $email_cc = implode( ', ', wp_parse_list( $email_cc ) );
287
		$this->set_prop( 'email_cc', $email_cc );
288
	}
289
290
    /**
291
	 * Sets the created date.
292
	 *
293
	 * @since 1.0.0
294
	 * @param  string $date_created date created.
295
	 */
296
	public function set_date_created( $date_created ) {
297
298
		$date = strtotime( $date_created );
299
300
        if ( $date && $date_created !== '0000-00-00 00:00:00'  && $date_created !== '0000-00-00 00:00' ) {
301
            $this->set_prop( 'date_created', gmdate( 'Y-m-d H:i:s', $date ) );
302
            return;
303
		}
304
305
		$this->set_prop( 'date_created', null );
306
	}
307
308
    /**
309
	 * Sets the created date.
310
	 *
311
	 * @since 1.0.0
312
	 * @param  string $date_modified date created.
313
	 */
314
	public function set_date_modified( $date_modified ) {
315
316
		$date = strtotime( $date_modified );
317
318
        if ( $date && $date_modified !== '0000-00-00 00:00:00'  && $date_modified !== '0000-00-00 00:00' ) {
319
            $this->set_prop( 'date_modified', gmdate( 'Y-m-d H:i:s', $date ) );
320
            return;
321
		}
322
323
		$this->set_prop( 'date_modified', null );
324
	}
325
326
	/*
327
	|--------------------------------------------------------------------------
328
	| Additional methods
329
	|--------------------------------------------------------------------------
330
	|
331
	| This method help you manipulate a customer.
332
	|
333
	*/
334
335
	/**
336
	 * Saves the customer.
337
	 *
338
	 * @since 1.0.0
339
	 */
340
	public function save() {
341
342
        $maybe_set = array(
343
            'uuid'         => wp_generate_uuid4(),
344
            'date_created' => current_time( 'mysql' ),
345
        );
346
347
        foreach ( $maybe_set as $key => $value ) {
348
            $current_value = $this->get( $key );
349
350
            if ( empty( $current_value ) ) {
351
                $this->set( $key, $value );
352
            }
353
        }
354
355
        $this->set( 'date_modified', current_time( 'mysql' ) );
356
357
		return parent::save();
358
	}
359
360
    /**
361
	 * Helper method to clone a customer from a user ID.
362
	 *
363
	 * @since 1.0.0
364
	 * @param int $user_id.
365
	 */
366
	public function clone_user( $user_id ) {
367
        $user = get_userdata( $user_id );
368
369
        if ( empty( $user ) ) {
370
            return;
371
        }
372
373
		$this->set_user_id( $user->ID );
374
        $this->set_email( $user->user_email );
375
        $this->set_purchase_value( getpaid_get_user_total_spend( $user->ID ) );
376
        $this->set_purchase_count( getpaid_count_user_invoices( $user->ID ) );
377
        $this->set( 'first_name', $user->first_name );
378
        $this->set( 'last_name', $user->last_name );
379
		$this->set_date_created( $user->user_registered );
380
381
        // Fetch extra data from WC or old GetPaid.
382
        $prefixes = array(
383
            '_wpinv_',
384
            'billing_',
385
            '',
386
        );
387
388
        foreach ( array_keys( getpaid_user_address_fields() ) as $field ) {
389
390
            foreach ( $prefixes as $prefix ) {
391
392
                // Meta table.
393
                $value = get_user_meta( $user_id, $prefix . $field, true );
394
395
                // UWP table.
396
                $value = ( empty( $value ) && function_exists( 'uwp_get_usermeta' ) ) ? uwp_get_usermeta( $user_id, $prefix . $field ) : $value;
397
398
                if ( ! empty( $value ) ) {
399
                    $this->set( $field, $value );
400
                    continue;
401
                }
402
            }
403
		}
404
	}
405
406
    /**
407
	 * Helper method to migrate an existing user ID to the new customers table.
408
	 *
409
	 * @since 1.0.0
410
	 * @param int $user_id.
411
	 */
412
	public function migrate_from_user( $user_id ) {
413
        $this->clone_user( $user_id );
414
        do_action( 'getpaid_customer_migrated_from_user', $this, $user_id );
415
        $this->save();
416
	}
417
}
418