Issues (2873)

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.

classes/PodsTermSplitting.php (15 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
 * @package Pods
5
 */
6
class Pods_Term_Splitting {
7
8
	/** @var int ID of the formerly shared term */
9
	private $term_id;
10
11
	/** @var int ID of the new term created for the $term_taxonomy_id */
12
	private $new_term_id;
13
14
	/** @var string Taxonomy for the split term */
15
	private $taxonomy;
16
17
	/** @var string */
18
	private $progress_option_name;
19
20
	/** @var array */
21
	private $previous_progress = array();
22
23
	/**
24
	 * @param int    $term_id     ID of the formerly shared term.
25
	 * @param int    $new_term_id ID of the new term created for the $term_taxonomy_id.
26
	 * @param string $taxonomy    Taxonomy for the split term.
27
	 */
28
	public function __construct( $term_id, $new_term_id, $taxonomy ) {
29
30
		$this->term_id     = $term_id;
31
		$this->new_term_id = $new_term_id;
32
		$this->taxonomy    = $taxonomy;
33
34
		$this->progress_option_name = "_pods_term_split_{$term_id}_{$taxonomy}";
35
36
	}
37
38
	/**
39
	 *
40
	 */
41
	public function split_shared_term() {
42
43
		// Stash any previous progress
44
		$this->previous_progress = $this->get_progress();
45
		if ( empty( $this->previous_progress ) ) {
46
			$this->append_progress( 'started' );
47
			$this->append_progress( "new term ID: {$this->new_term_id}" );
48
		}
49
50
		// Get the Pod information if the taxonomy is a Pod
51
		$taxonomy_pod = $this->get_pod_info();
52
53
		// Is the taxonomy a Pod?
54
		if ( is_array( $taxonomy_pod ) ) {
55
			$this->update_podsrel_taxonomy( $taxonomy_pod['id'] );
56
57
			// Update the Pods table if the taxonomy is a table based Pod
58
			if ( 'table' === $taxonomy_pod['storage'] ) {
59
				$this->update_pod_table( $taxonomy_pod['pod_table'] );
60
			}
61
		}
62
63
		// Track down all fields related to the target taxonomy and update stored term IDs as necessary
64
		$this->update_relationships_to_term();
65
66
		// Clean up
67
		$this->delete_progress();
68
69
	}
70
71
	/**
72
	 * Return the Pod information for the specified taxonomy, or null if the taxonomy isn't a Pod
73
	 *
74
	 * @return array|bool|mixed|null
75
	 */
76
	private function get_pod_info() {
77
78
		$pod_info = null;
79
80
		if ( pods_api()->pod_exists( $this->taxonomy ) ) {
81
82
			// Load the taxonomy Pod
83
			$params   = array(
84
				'name'       => $this->taxonomy,
85
				'table_info' => true,
86
			);
87
			$pod_info = pods_api()->load_pod( $params, false );
88
		}
89
90
		return $pod_info;
91
92
	}
93
94
	/**
95
	 * @param int $pod_id
96
	 */
97
	private function update_podsrel_taxonomy( $pod_id ) {
98
99
		/** @global wpdb $wpdb */
100
		global $wpdb;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
101
102
		$task = "update_podsrel_taxonomy_{$pod_id}";
103
		if ( ! $this->have_done( $task ) ) {
104
105
			// UPDATE {$wpdb->prefix}podsrel SET item_id = {$new_term_id} WHERE pod_id = {$pod_id} AND item_id = {$term_id}
0 ignored issues
show
Unused Code Comprehensibility introduced by
36% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
106
			$table        = "{$wpdb->prefix}podsrel";
107
			$data         = array( 'item_id' => $this->new_term_id );
108
			$where        = array(
109
				'pod_id'  => $pod_id,
110
				'item_id' => $this->term_id,
111
			);
112
			$format       = '%d';
113
			$where_format = '%d';
114
115
			$wpdb->update( $table, $data, $where, $format, $where_format );
116
117
			$this->append_progress( $task );
118
		}
119
120
	}
121
122
	/**
123
	 * @param string $pod_table
124
	 */
125
	private function update_pod_table( $pod_table ) {
126
127
		/** @global wpdb $wpdb */
128
		global $wpdb;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
129
130
		$task = "update_pod_table_{$pod_table}";
131
		if ( ! $this->have_done( $task ) ) {
132
133
			// Prime the values and update
134
			$data         = array( 'id' => $this->new_term_id );
135
			$where        = array( 'id' => $this->term_id );
136
			$format       = '%d';
137
			$where_format = '%d';
138
			$wpdb->update( $pod_table, $data, $where, $format, $where_format );
139
140
			$this->append_progress( $task );
141
		}
142
143
	}
144
145
	/**
146
	 * Track down all fields related to the target taxonomy and update stored term IDs as necessary
147
	 */
148
	private function update_relationships_to_term() {
149
150
		// Loop through all Pods
151
		$all_pods = pods_api()->load_pods();
152
153
		if ( ! is_array( $all_pods ) ) {
154
			return;
155
		}
156
157
		foreach ( $all_pods as $this_pod_id => $this_pod ) {
158
159
			// Loop through all fields in this Pod
160
			foreach ( $this_pod['fields'] as $this_field_name => $this_field ) {
161
162
				// Ignore everything except relationship fields to this taxonomy
163
				if ( 'pick' !== $this_field['type'] || 'taxonomy' !== $this_field['pick_object'] || $this->taxonomy != $this_field['pick_val'] ) {
164
					continue;
165
				}
166
167
				// Update the term ID in podsrel everywhere it is the value for this field
168
				$this->update_podsrel_related_term( $this_field['id'] );
169
170
				// Fix-up any special-case relationships that store term IDs in their own meta table and/or serialized
171
				switch ( $this_pod['type'] ) {
172
173
					case 'post_type':
174
						$this->update_postmeta( $this_pod['name'], $this_field_name );
175
						break;
176
177
					case 'comment':
178
						$this->update_commentmeta( $this_field_name );
179
						break;
180
181
					case 'user':
182
						$this->update_usermeta( $this_field_name );
183
						break;
184
185
					case 'settings':
186
						$this->update_setting_meta( $this_pod['name'], $this_field_name );
187
						break;
188
				}
189
			}//end foreach
190
		}//end foreach
191
192
	}
193
194
	/**
195
	 * @param int $field_id
196
	 */
197
	private function update_podsrel_related_term( $field_id ) {
198
199
		/** @global wpdb $wpdb */
200
		global $wpdb;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
201
202
		$task = "update_podsrel_related_term_{$field_id}";
203
		if ( ! $this->have_done( $task ) ) {
204
205
			// UPDATE {$wpdb->prefix}podsrel SET related_item_id = {$new_term_id} WHERE field_id = {$field_id} AND related_item_id = {$term_id}
0 ignored issues
show
Unused Code Comprehensibility introduced by
36% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
206
			$table        = "{$wpdb->prefix}podsrel";
207
			$data         = array(
208
				'related_item_id' => $this->new_term_id,
209
			);
210
			$where        = array(
211
				'field_id'        => $field_id,
212
				'related_item_id' => $this->term_id,
213
			);
214
			$format       = '%d';
215
			$where_format = '%d';
216
			$wpdb->update( $table, $data, $where, $format, $where_format );
217
218
			$this->append_progress( $task );
219
		}
220
221
	}
222
223
	/**
224
	 * Called for all fields related to the target taxonomy that are in a post_type
225
	 *
226
	 * @param string $pod_name
227
	 * @param string $field_name
228
	 */
229
	private function update_postmeta( $pod_name, $field_name ) {
230
231
		/** @global wpdb $wpdb */
232
		global $wpdb;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
233
234
		// Fix up the unserialized data
235
		$task = "update_postmeta_{$pod_name}_{$field_name}_unserialized";
236
		if ( ! $this->have_done( $task ) ) {
237
238
			$wpdb->query(
239
				$wpdb->prepare(
240
					"
241
				UPDATE
242
					{$wpdb->postmeta} AS meta
243
					LEFT JOIN {$wpdb->posts} AS t
244
					ON meta.post_id = t.ID
245
				SET
246
					meta_value = %s
247
				WHERE
248
					meta_key = %s
249
					AND meta_value = %s
250
					AND t.post_type = %s
251
				", $this->new_term_id, $field_name, $this->term_id, $pod_name
252
				)
253
			);
254
255
			$this->append_progress( $task );
256
		}//end if
257
258
		// Fix up the serialized data
259
		$task = "update_postmeta_{$pod_name}_{$field_name}_serialized";
260
		if ( ! $this->have_done( $task ) ) {
261
262
			$meta_key           = sprintf( '_pods_%s', $field_name );
263
			$target_serialized  = sprintf( ';i:%s;', $this->term_id );
264
			$replace_serialized = sprintf( ';i:%s;', $this->new_term_id );
265
266
			$wpdb->query(
267
				$wpdb->prepare(
268
					"
269
				UPDATE
270
				    {$wpdb->postmeta} AS meta
271
			    LEFT JOIN {$wpdb->posts} AS t
272
					ON meta.post_id = t.ID
273
				SET
274
					meta.meta_value = REPLACE( meta.meta_value, %s, %s )
275
				WHERE
276
				    meta.meta_key = %s
277
					AND t.post_type = %s
278
					AND meta_value LIKE '%%%s%%'
279
				", $target_serialized, $replace_serialized, $meta_key, $pod_name, pods_sanitize_like( $target_serialized )
280
				)
281
			);
282
283
			$this->append_progress( $task );
284
		}//end if
285
286
	}
287
288
	/**
289
	 * Called for all fields related to the target taxonomy that are in a comment Pod
290
	 *
291
	 * @param string $field_name
292
	 */
293
	private function update_commentmeta( $field_name ) {
294
295
		/** @global wpdb $wpdb */
296
		global $wpdb;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
297
298
		// Fix up the unserialized data
299
		$task = "update_commentmeta_{$field_name}_unserialized";
300
		if ( ! $this->have_done( $task ) ) {
301
302
			$table        = $wpdb->commentmeta;
303
			$data         = array( 'meta_value' => $this->new_term_id );
0 ignored issues
show
Detected usage of meta_value, possible slow query.
Loading history...
304
			$where        = array(
305
				'meta_key'   => $field_name,
0 ignored issues
show
Detected usage of meta_key, possible slow query.
Loading history...
306
				'meta_value' => $this->term_id,
0 ignored issues
show
Detected usage of meta_value, possible slow query.
Loading history...
307
			);
308
			$format       = '%s';
309
			$where_format = array( '%s', '%s' );
310
			$wpdb->update( $table, $data, $where, $format, $where_format );
311
312
			$this->append_progress( $task );
313
		}
314
315
		// Fix up the serialized data
316
		$task = "update_commentmeta_{$field_name}_serialized";
317
		if ( ! $this->have_done( $task ) ) {
318
319
			$meta_key           = sprintf( '_pods_%s', $field_name );
320
			$target_serialized  = sprintf( ';i:%s;', $this->term_id );
321
			$replace_serialized = sprintf( ';i:%s;', $this->new_term_id );
322
323
			$wpdb->query(
324
				$wpdb->prepare(
325
					"
326
				UPDATE
327
				    {$wpdb->commentmeta}
328
				SET
329
					meta_value = REPLACE( meta_value, %s, %s )
330
				WHERE
331
				    meta_key = %s
332
					AND meta_value LIKE '%%%s%%'
333
				", $target_serialized, $replace_serialized, $meta_key, pods_sanitize_like( $target_serialized )
334
				)
335
			);
336
337
			$this->append_progress( $task );
338
		}//end if
339
340
	}
341
342
	/**
343
	 * Called for all fields related to the target taxonomy that are in a user Pod
344
	 *
345
	 * @param string $field_name
346
	 */
347
	private function update_usermeta( $field_name ) {
348
349
		/** @global wpdb $wpdb */
350
		global $wpdb;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
351
352
		// Fix up the unserialized data
353
		$task = "update_usermeta_{$field_name}_unserialized";
354
		if ( ! $this->have_done( $task ) ) {
355
356
			$table        = $wpdb->usermeta;
357
			$data         = array( 'meta_value' => $this->new_term_id );
0 ignored issues
show
Detected usage of meta_value, possible slow query.
Loading history...
358
			$where        = array(
359
				'meta_key'   => $field_name,
0 ignored issues
show
Detected usage of meta_key, possible slow query.
Loading history...
360
				'meta_value' => $this->term_id,
0 ignored issues
show
Detected usage of meta_value, possible slow query.
Loading history...
361
			);
362
			$format       = '%s';
363
			$where_format = array( '%s', '%s' );
364
			$wpdb->update( $table, $data, $where, $format, $where_format );
365
366
			$this->append_progress( $task );
367
		}
368
369
		// Fix up the serialized data
370
		$task = "update_usermeta_{$field_name}_serialized";
371
		if ( ! $this->have_done( $task ) ) {
372
373
			$meta_key           = sprintf( '_pods_%s', $field_name );
374
			$target_serialized  = sprintf( ';i:%s;', $this->term_id );
375
			$replace_serialized = sprintf( ';i:%s;', $this->new_term_id );
376
377
			$wpdb->query(
378
				$wpdb->prepare(
379
					"
380
				UPDATE
381
				    {$wpdb->usermeta}
382
				SET
383
					meta_value = REPLACE( meta_value, %s, %s )
384
				WHERE
385
				    meta_key = %s
386
					AND meta_value LIKE '%%%s%%'
387
				", $target_serialized, $replace_serialized, $meta_key, pods_sanitize_like( $target_serialized )
388
				)
389
			);
390
391
			$this->append_progress( $task );
392
		}//end if
393
394
	}
395
396
	/**
397
	 * Called for all fields related to the target taxonomy that are in a user Pod
398
	 *
399
	 * @param string $pod_name
400
	 * @param string $field_name
401
	 */
402
	private function update_setting_meta( $pod_name, $field_name ) {
403
404
		/** @global wpdb $wpdb */
405
		global $wpdb;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
406
407
		$option_name = "{$pod_name}_{$field_name}";
408
409
		// Fix up the unserialized data
410
		$task = "update_setting_meta_{$pod_name}_{$field_name}_unserialized";
411
		if ( ! $this->have_done( $task ) ) {
412
413
			// UPDATE {$wpdb->options} SET option_value = '{$new_term_id}' WHERE option_name = '{$pod_name}_{$field_name}' AND option_value = '{$term_id}'
414
			$table        = $wpdb->options;
415
			$data         = array( 'option_value' => $this->new_term_id );
416
			$where        = array(
417
				'option_name'  => $option_name,
418
				'option_value' => $this->term_id,
419
			);
420
			$format       = '%s';
421
			$where_format = array( '%s', '%s' );
422
			$wpdb->update( $table, $data, $where, $format, $where_format );
423
424
			$this->append_progress( $task );
425
		}
426
427
		// Fix up the serialized data
428
		$task = "update_setting_meta_{$pod_name}_{$field_name}_serialized";
429
		if ( ! $this->have_done( $task ) ) {
430
431
			$target_serialized  = sprintf( ';i:%s;', $this->term_id );
432
			$replace_serialized = sprintf( ';i:%s;', $this->new_term_id );
433
434
			$wpdb->query(
435
				$wpdb->prepare(
436
					"
437
				UPDATE
438
					{$wpdb->options}
439
				SET
440
					option_value = REPLACE( option_value, %s, %s )
441
				WHERE
442
					option_name = %s
443
					AND option_value LIKE '%%%s%%'
444
				", $target_serialized, $replace_serialized, $option_name, pods_sanitize_like( $target_serialized )
445
				)
446
			);
447
448
			$this->append_progress( $task );
449
		}//end if
450
451
	}
452
453
	/**
454
	 * @param string $task_name
455
	 *
456
	 * @return bool
457
	 */
458
	private function have_done( $task_name ) {
459
460
		return in_array( $task_name, $this->previous_progress );
461
462
	}
463
464
	/**
465
	 * @return array
466
	 */
467
	private function get_progress() {
468
469
		return get_option( $this->progress_option_name, array() );
470
	}
471
472
	/**
473
	 * @param $data
474
	 */
475
	private function append_progress( $data ) {
476
477
		// Get the current progress array
478
		$current_progress = $this->get_progress();
479
		if ( ! is_array( $current_progress ) ) {
480
			$current_progress = array();
481
		}
482
483
		// Tack on the new data
484
		$updated_progress = array_merge( $current_progress, array( $data ) );
485
486
		// Note: we don't want autoload set and you cannot specify autoload via update_option
487
		if ( ! empty( $current_progress ) && is_array( $current_progress ) ) {
488
			update_option( $this->progress_option_name, $updated_progress );
489
		} else {
490
			add_option( $this->progress_option_name, $updated_progress, '', false );
491
		}
492
493
	}
494
495
	/**
496
	 *
497
	 */
498
	private function delete_progress() {
499
500
		delete_option( $this->progress_option_name );
501
502
	}
503
504
}
505