HEX
Server: LiteSpeed
System: Linux premium263.web-hosting.com 4.18.0-553.50.1.lve.el8.x86_64 #1 SMP Thu Apr 17 19:10:24 UTC 2025 x86_64
User: eastcjee (525)
PHP: 8.2.31
Disabled: NONE
Upload Files
File: //proc/thread-self/cwd/wp-content/plugins/ninja-forms/includes/Abilities/Utils/ValidationHelper.php
<?php
/**
 * Validation Helper for Ninja Forms Abilities API
 *
 * Provides comprehensive input validation and sanitization functions
 * for secure handling of user input across all abilities.
 *
 * @package NinjaForms
 * @subpackage Abilities
 * @since 3.13.1
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

// Load response helper
require_once __DIR__ . '/ResponseHelper.php';

/**
 * Class NF_Abilities_Validation
 *
 * Centralized validation for all abilities input
 */
class NF_Abilities_Validation {

	/**
	 * Validate calculation formula for security
	 *
	 * @param string $formula The calculation formula
	 * @return string|WP_Error Validated formula or error
	 */
	public static function validate_calculation_formula( $formula ) {
		if ( empty( $formula ) ) {
			return NF_Abilities_Response::required_field( 'formula' );
		}
		
		// Clean the formula for analysis
		$clean_formula = trim( $formula );
		
		// Check for dangerous PHP functions and constructs
		$dangerous_patterns = array(
			'/\b(eval|exec|system|shell_exec|passthru|file_get_contents|file_put_contents|fopen|fwrite|include|require)\s*\(/i',
			'/\$\w+/', // PHP variables
			'/\bfunction\s*\(/i', // Function definitions
			'/\bnew\s+\w+/i', // Object instantiation
			'/\<\?php/i', // PHP tags
			'/\<script/i', // Script tags
			'/javascript:/i', // JavaScript protocol
			'/on\w+\s*=/i', // Event handlers
		);
		
		foreach ( $dangerous_patterns as $pattern ) {
			if ( preg_match( $pattern, $clean_formula ) ) {
				return NF_Abilities_Response::error(
					'dangerous_formula',
					__( 'Formula contains potentially dangerous code and has been rejected.', 'ninja-forms' ),
					array( 'formula' => $clean_formula )
				);
			}
		}
		
		// Validate allowed characters and constructs for calculation formulas
		$allowed_pattern = '/^[\d\+\-\*\/\(\)\{\}\:\w\s\._,]+$/';
		if ( ! preg_match( $allowed_pattern, $clean_formula ) ) {
			return NF_Abilities_Response::validation_error(
				'formula',
				__( 'Formula contains invalid characters. Only numbers, operators (+, -, *, /), parentheses, and field references {field:key} are allowed.', 'ninja-forms' )
			);
		}
		
		// Validate balanced parentheses
		if ( ! self::validate_balanced_parentheses( $clean_formula ) ) {
			return NF_Abilities_Response::validation_error(
				'formula',
				__( 'Formula has unbalanced parentheses.', 'ninja-forms' )
			);
		}
		
		// Validate merge tag syntax
		if ( ! self::validate_merge_tags( $clean_formula ) ) {
			return NF_Abilities_Response::validation_error(
				'formula',
				__( 'Formula contains invalid merge tag syntax. Use {field:key} or {calc:name} format.', 'ninja-forms' )
			);
		}
		
		return sanitize_text_field( $clean_formula );
	}

	/**
	 * Validate balanced parentheses in formula
	 *
	 * @param string $formula Formula to check
	 * @return bool
	 */
	private static function validate_balanced_parentheses( $formula ) {
		$count = 0;
		$length = strlen( $formula );
		
		for ( $i = 0; $i < $length; $i++ ) {
			$char = $formula[ $i ];
			if ( '(' === $char ) {
				$count++;
			} elseif ( ')' === $char ) {
				$count--;
				if ( $count < 0 ) {
					return false;
				}
			}
		}
		
		return $count === 0;
	}

	/**
	 * Validate merge tag syntax
	 *
	 * @param string $formula Formula to check
	 * @return bool
	 */
	private static function validate_merge_tags( $formula ) {
		// Find all merge tags
		preg_match_all( '/\{[^}]+\}/', $formula, $matches );
		
		if ( empty( $matches[0] ) ) {
			return true; // No merge tags is valid
		}
		
		foreach ( $matches[0] as $tag ) {
			// Valid formats: {field:key}, {calc:name}, {form:property}
			if ( ! preg_match( '/^\{(field|calc|form):[a-zA-Z0-9_]+\}$/', $tag ) ) {
				return false;
			}
		}
		
		return true;
	}

	/**
	 * Validate form ID
	 *
	 * @param mixed $form_id Form ID to validate
	 * @return int|WP_Error Valid form ID or error
	 */
	public static function validate_form_id( $form_id ) {
		if ( empty( $form_id ) ) {
			return NF_Abilities_Response::required_field( 'form_id' );
		}
		
		$form_id = (int) $form_id;
		if ( $form_id <= 0 ) {
			return NF_Abilities_Response::validation_error( 'form_id', __( 'Form ID must be a positive integer.', 'ninja-forms' ) );
		}
		
		// Check if form exists
		global $wpdb;
		$exists = $wpdb->get_var( $wpdb->prepare(
			"SELECT COUNT(*) FROM {$wpdb->prefix}nf3_forms WHERE id = %d",
			$form_id
		) );
		
		if ( ! $exists ) {
			return NF_Abilities_Response::not_found( 'form', $form_id );
		}
		
		return $form_id;
	}

	/**
	 * Validate field data
	 *
	 * @param array $field_data Field data to validate
	 * @return array|WP_Error Sanitized field data or error
	 */
	public static function validate_field_data( $field_data ) {
		if ( ! is_array( $field_data ) ) {
			return NF_Abilities_Response::validation_error( 'field_data', __( 'Field data must be an array.', 'ninja-forms' ) );
		}
		
		// Required field type
		if ( empty( $field_data['type'] ) ) {
			return NF_Abilities_Response::required_field( 'type' );
		}
		
		// Validate field type
		$allowed_field_types = array(
			'textbox', 'textarea', 'email', 'number', 'tel', 'url', 'password',
			'select', 'radio', 'checkbox', 'listcheckbox', 'listradio',
			'date', 'hidden', 'html', 'hr', 'submit', 'starrating', 'rating',
			'spam', 'hcaptcha', 'recaptcha', 'file_upload'
		);
		
		if ( ! in_array( $field_data['type'], $allowed_field_types, true ) ) {
			return NF_Abilities_Response::validation_error(
				'type',
				sprintf(
					/* translators: %s: field type */
					__( 'Invalid field type: %s', 'ninja-forms' ),
					sanitize_text_field( $field_data['type'] )
				)
			);
		}
		
		// Sanitize all field data
		$sanitized = array();
		$sanitized['type'] = sanitize_text_field( $field_data['type'] );
		
		if ( isset( $field_data['label'] ) ) {
			$sanitized['label'] = sanitize_text_field( $field_data['label'] );
		}
		
		if ( isset( $field_data['key'] ) ) {
			$sanitized['key'] = sanitize_key( $field_data['key'] );
		}
		
		if ( isset( $field_data['required'] ) ) {
			$sanitized['required'] = (bool) $field_data['required'];
		}
		
		if ( isset( $field_data['placeholder'] ) ) {
			$sanitized['placeholder'] = sanitize_text_field( $field_data['placeholder'] );
		}
		
		// Sanitize HTML content for HTML field type with restricted tags
		if ( 'html' === $field_data['type'] && isset( $field_data['default'] ) ) {
			$allowed_html = array(
				'p' => array(),
				'br' => array(),
				'strong' => array(),
				'b' => array(),
				'em' => array(),
				'i' => array(),
				'h1' => array(),
				'h2' => array(),
				'h3' => array(),
				'h4' => array(),
				'h5' => array(),
				'h6' => array(),
				'span' => array( 'class' => true ),
			);
			$sanitized['default'] = wp_kses( $field_data['default'], $allowed_html );
		} elseif ( isset( $field_data['default'] ) ) {
			$sanitized['default'] = sanitize_text_field( $field_data['default'] );
		}
		
		// Handle options for select/radio/checkbox fields
		if ( isset( $field_data['options'] ) && is_array( $field_data['options'] ) ) {
			$sanitized['options'] = array();
			foreach ( $field_data['options'] as $option ) {
				if ( is_array( $option ) ) {
					$sanitized_option = array();
					if ( isset( $option['label'] ) ) {
						$sanitized_option['label'] = sanitize_text_field( $option['label'] );
					}
					if ( isset( $option['value'] ) ) {
						$sanitized_option['value'] = sanitize_text_field( $option['value'] );
					}
					if ( isset( $option['calc'] ) ) {
						$sanitized_option['calc'] = sanitize_text_field( $option['calc'] );
					}
					$sanitized['options'][] = $sanitized_option;
				}
			}
		}
		
		return $sanitized;
	}

	/**
	 * Validate and sanitize action settings
	 *
	 * @param array $settings Action settings
	 * @param string $action_type Action type
	 * @return array Sanitized settings
	 */
	public static function validate_action_settings( $settings, $action_type ) {
		if ( ! is_array( $settings ) ) {
			return array();
		}
		
		$sanitized = array();
		
		foreach ( $settings as $key => $value ) {
			$key = sanitize_key( $key );
			
			if ( is_string( $value ) ) {
				// HTML fields for email messages
				if ( in_array( $key, array( 'message', 'email_message', 'success_message' ), true ) ) {
					$allowed_html = array(
						'p' => array(),
						'br' => array(),
						'strong' => array(),
						'b' => array(),
						'em' => array(),
						'i' => array(),
						'a' => array( 'href' => true, 'target' => true ),
						'ul' => array(),
						'ol' => array(),
						'li' => array(),
					);
					$sanitized[ $key ] = wp_kses( $value, $allowed_html );
				} else {
					$sanitized[ $key ] = sanitize_text_field( $value );
				}
			} elseif ( is_array( $value ) ) {
				$sanitized[ $key ] = array_map( 'sanitize_text_field', $value );
			} elseif ( is_bool( $value ) ) {
				$sanitized[ $key ] = $value;
			} elseif ( is_numeric( $value ) ) {
				$sanitized[ $key ] = is_float( $value ) ? floatval( $value ) : intval( $value );
			}
		}
		
		return $sanitized;
	}

	/**
	 * Validate limit parameter
	 *
	 * @param mixed $limit Limit value
	 * @param int $max_limit Maximum allowed limit
	 * @return int|WP_Error Valid limit or error
	 */
	public static function validate_limit( $limit, $max_limit = 100 ) {
		if ( empty( $limit ) ) {
			return 0; // No limit
		}
		
		$limit = (int) $limit;
		
		if ( $limit < 0 ) {
			return NF_Abilities_Response::validation_error( 'limit', __( 'Limit cannot be negative.', 'ninja-forms' ) );
		}
		
		if ( $limit > $max_limit ) {
			return NF_Abilities_Response::validation_error(
				'limit',
				sprintf(
					/* translators: %d: maximum limit */
					__( 'Limit cannot exceed %d.', 'ninja-forms' ),
					$max_limit
				)
			);
		}
		
		return $limit;
	}
}