HEX
Server: nginx/1.18.0
System: Linux iZj6c1ieg2jrpk1z5tzi19Z 6.3.9-1.el7.elrepo.x86_64 #1 SMP PREEMPT_DYNAMIC Wed Jun 21 22:18:40 EDT 2023 x86_64
User: www (1001)
PHP: 8.2.4
Disabled: passthru,exec,system,putenv,chroot,chgrp,chown,shell_exec,popen,proc_open,pcntl_exec,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,imap_open,apache_setenv
Upload Files
File: /www/wwwroot/www.cytocare.cn/wp-content/plugins/security-safe/core/includes/Janitor.php
<?php

	namespace SovereignStack\SecuritySafe;

	// Prevent Direct Access
	( defined( 'ABSPATH' ) ) || die;

	use WP_Site;

	/**
	 * Class Janitor - Cleans up after the plugin
	 *
	 * @package SecuritySafe
	 * @since 2.0.0
	 */
	class Janitor {

		/**
		 * Janitor constructor.
		 */
		function __construct() {

			// Register enable plugin process
			add_action( "activated_plugin", [ self::class, 'enable_plugin'], 10, 2 );

			add_action( 'upgrader_process_complete', [ self::class, 'upgrade_complete' ], 10, 2 );

			// Cleanup Settings, Database, & Crons on Plugin Disable
			register_deactivation_hook( SECSAFE_FILE, [ self::class, 'disable_plugin' ] );

			// Add Cleanup Action
			add_action( 'secsafe_cleanup_tables_daily', [ self::class, 'cleanup_tables' ] );

			// Schedule Cleanup Services
			if ( ! wp_next_scheduled( 'secsafe_cleanup_tables_daily' ) ) {

				wp_schedule_event( time(), 'daily', 'secsafe_cleanup_tables_daily' );

			}

			// Create Tables For New Blog
			add_action( 'wp_insert_site', [ self::class, 'wp_insert_site' ] );

		}

		/**
		 * Writes to debug.log for troubleshooting
		 *
		 * @param string $message Message entered into the log
		 * @param string $file Location of the file where the error occured
		 * @param string $line Line number of where the error occured
		 *
		 * @since 0.1.0
		 */
		public static function log( string $message, string $file = '', string $line = '' ) : void {

			// Debug must be on
			if ( SECSAFE_DEBUG ) {

				$message = ( $message ) ? $message : 'Error: Log Message not defined!';
				$message .= ( $file && $line ) ? ' - ' . 'Occurred on line ' . $line . ' in file ' . $file : '';

				error_log( date( 'Y-M-j h:m:s' ) . " - " . $message . "\n", 3, SECSAFE_DIR . '/debug.log' );

			}

		}

		/**
		 * Run functions after upgrade
		 *
		 * @param object $upgrader_object
		 * @param array $options
		 *
		 * @since  2.0.1
		 */
		public static function upgrade_complete( object $upgrader_object, array $options ) : void {

			if (
				isset( $options['action'] ) && $options['action'] == 'update' &&
				isset( $options['type'] ) && $options['type'] == 'plugin'
			) {

				if ( isset( $options['plugins'] ) ) {

					if ( is_array( $options['plugins'] ) ) {

						foreach ( $options['plugins'] as $plugin ) {
							self::enable_plugin( $plugin );
						}

					} elseif ( is_string( $options['plugins'] ) ) {
						self::enable_plugin( $options['plugins'] );
					}

				}

			}

		}

		/**
		 * Creates database tables
		 *
		 * @since  2.5.0
		 *
		 * @param string $plugin
		 * @param bool   $network_wide
		 *
		 * @return void
		 */
		public static function enable_plugin( string $plugin, bool $network_wide = false ) : void {

			if (SECSAFE_PLUGIN === $plugin ) {

				global $SecuritySafe;

				if ( empty( $SecuritySafe ) ) {
					Plugin::init();
				}

				$sites = Yoda::get_sites();

				foreach( $sites as $site) {

					if ( is_multisite() ) {
						switch_to_blog( $site->blog_id );
					}

					if ( is_plugin_active( SECSAFE_PLUGIN ) ) {

						// Create Logs Table
						Janitor::create_table_logs();

						// Create Stats Table
						Janitor::create_table_stats();

						$args = [];
						$args['type'] = '404s';
						$args['threats'] = 0;
						$args['status'] = 'test';
						$args['details'] = ( isset( $args['details'] ) ) ? $args['details'] : sprintf( __( '%s plugin enabled.', SECSAFE_TRANSLATE ), SECSAFE_NAME );

						// Log Test 404
						Janitor::add_entry( $args );

						// Log Actual Activity
						Janitor::log_activity( $args );

					}

					if ( is_multisite() ) {
						restore_current_blog();
					}

				}

			}

		}

		/**
		 * Runs when a new blog is added in a multisite setup
		 *
		 * @since WP 5.1
		 * @since 1.0.0
		 *
		 * @param \WP_Site $new_site
		 *
		 * @return void
		 */
		public static function wp_insert_site( WP_Site $new_site ): void {

			self::enable_plugin( SECSAFE_PLUGIN );

		}

		/**
		 * Creates Firewall Table
		 *
		 * @since  2.0.0
		 */
		public static function create_table_logs() : void {

			global $wpdb;

			$table_main = Yoda::get_table_main();

			// Must have two spaces after PRIMARY KEY, UNIQUE and INDEX
			$wpdb->query( "CREATE TABLE IF NOT EXISTS `$table_main` (
	            ID BIGINT NOT NULL AUTO_INCREMENT,
	            `type` VARCHAR(10) NOT NULL default '',
	            `date` DATETIME NOT NULL,
	            `date_expire` DATETIME NOT NULL,
	            `ip` VARCHAR(50) NOT NULL default '',
	            `username` VARCHAR(50) NOT NULL default '',
	            `uri` VARCHAR(512) NOT NULL,
	            `referer` VARCHAR(512) NOT NULL default '',
	            `user_agent` VARCHAR(512) NOT NULL default '',
	            `threats` TINYINT(1) NOT NULL default '0',
	            `status` VARCHAR(10) NOT NULL default '',
	            `details` VARCHAR(512) NOT NULL default '',
	            `score` TINYINT(3) NOT NULL default '0',
	            PRIMARY KEY  (ID),
	            UNIQUE  (ID),
	            INDEX  (type, status)
	        ) ". $wpdb->get_charset_collate() .";" );

			// In case the table already existed, we are going to ensure it has the correct charset and collate
			$wpdb->query("ALTER TABLE `$table_main` CONVERT TO CHARACTER SET $wpdb->charset COLLATE $wpdb->collate;");

		}

		/**
		 * Creates Stats Table
		 *
		 * @since  2.0.0
		 */
		public static function create_table_stats() : void {

			global $wpdb;

			$table_stats = Yoda::get_table_stats();

			// Must have two spaces after PRIMARY KEY and UNIQUE
			$wpdb->query( "CREATE TABLE IF NOT EXISTS `$table_stats` (
	            `date` DATETIME NOT NULL,
	            `404s` BIGINT NOT NULL default '0',
	            `404s_threats` BIGINT NOT NULL default '0',
	            `blocked` BIGINT NOT NULL default '0',
	            `threats` BIGINT NOT NULL default '0',
	            `logins` BIGINT NOT NULL default '0',
	            `logins_failed` BIGINT NOT NULL default '0',
	            `logins_success` BIGINT NOT NULL default '0',
	            `logins_threats` BIGINT NOT NULL default '0',
	            `logins_blocked` BIGINT NOT NULL default '0',
	            PRIMARY KEY  (date),
	            UNIQUE  (date)
	        ) ". $wpdb->get_charset_collate() .";" );

			// In case the table already existed, we are going to ensure it has the correct charset and collate
			$wpdb->query("ALTER TABLE `$table_stats` CONVERT TO CHARACTER SET $wpdb->charset COLLATE $wpdb->collate;");

		}

		/**
		 * Add entry to database
		 *
		 * @param array $args
		 *
		 * @return bool
		 *
		 * @since  2.0.0
		 */
		public static function add_entry( array $args ) : bool {

			global $wpdb;

			// Prevent Caching
			Janitor::prevent_caching();

			$args   = ( isset( $args['type'] ) ) ? $args : [];
			$type   = ( isset( $args['type'] ) ) ? $args['type'] : false;
			$types  = Yoda::get_types();
			$result = false; // Default

			// Require Valid Type
			if ( isset( $types[ $type ] ) ) {

				/**
				 * Statically set for now
				 * @todo  log all wp cron activity not just Security Safe's
				 */
				$args['date'] = date( 'Y-m-d H:i:s' );

				if (
					$args['type'] != 'activity' &&
					$args['type'] != 'allow_deny'
				) {

					$args['uri']        = ( isset( $_SERVER['REQUEST_URI'] ) ) ? filter_var( $_SERVER['REQUEST_URI'], FILTER_SANITIZE_URL ) : '';
					$args['referer']    = ( isset( $_SERVER['HTTP_REFERER'] ) ) ? filter_var( $_SERVER['HTTP_REFERER'], FILTER_SANITIZE_URL ) : '';
					$args['user_agent'] = Yoda::get_user_agent();
					$args['ip']         = Yoda::get_ip();

					$args['threats'] = ( isset( $args['threats'] ) && $args['threats'] ) ? 1 : 0;
					$args['score']   = ( isset( $args['score'] ) ) ? $args['score'] : 0;

					// Record Stats
					Janitor::add_stats( $args );

				}

				// Trim Data
				$targs = [];

				foreach ( $args as $key => $value ) {

					// Limit to 512 Characters
					$targs[ $key ] = substr( $value, 0, 512 );

				}

				// Add data to DB and insert() is sanitized by WP
				$result = $wpdb->insert( Yoda::get_table_main(), $targs );

				if ( ! $result ) {

					// Create Logs Table
					Janitor::create_table_logs();

					// Try Again now that a table exist
					$result = $wpdb->insert( Yoda::get_table_main(), $targs );

				}

			}

			return (bool) $result;

		}

		/**
		 * Prevent plugins like WP Super Cache and W3TC from caching any data on this page.
		 *
		 * @since  2.2.3
		 */
		public static function prevent_caching() : void {

			( defined( 'DONOTCACHEOBJECT' ) ) || define( 'DONOTCACHEOBJECT', true );
			( defined( 'DONOTCACHEDB' ) )  || define( 'DONOTCACHEDB', true );
			( defined( 'DONOTCACHEPAGE' ) ) || define( 'DONOTCACHEPAGE', true );

		}

		/**
		 * Add stats into the db
		 *
		 * @param array $args
		 *
		 * @since  2.0.0
		 */
		public static function add_stats( array $args ) : void {

			global $wpdb;

			$date = date( 'Y-m-d 00:00:00', strtotime( $args['date'] ) );

			$stats['blocked'] = $blocked = ( isset( $args['status'] ) && $args['status'] == 'blocked' ) ? 1 : 0;
			$stats['threats'] = $threats = ( isset( $args['threats'] ) && $args['threats'] ) ? 1 : 0;

			$stats['404s']         = $e404s = ( $args['type'] == '404s' ) ? 1 : 0;
			$stats['404s_threats'] = $e404s_threats = ( $threats && $args['type'] == '404s' ) ? 1 : 0;

			$stats['logins']         = $logins = ( isset( $args['type'] ) && $args['type'] == 'logins' ) ? 1 : 0;
			$stats['logins_failed']  = $logins_failed = ( $logins && isset( $args['status'] ) && $args['status'] == 'failed' ) ? 1 : 0;
			$stats['logins_success'] = $logins_success = ( $logins && isset( $args['status'] ) && $args['status'] == 'success' ) ? 1 : 0;
			$stats['logins_threats'] = $logins_threats = ( $logins && $threats ) ? 1 : 0;
			$stats['logins_blocked'] = $logins_blocked = ( $logins && $blocked ) ? 1 : 0;

			// Get the current day's stats
			$table = Yoda::get_table_stats();

			// Update
			$query = "
            UPDATE $table 
            SET 404s = 404s + $e404s,
            404s_threats = 404s_threats + $e404s_threats,
            blocked = blocked + $blocked,
            threats = threats + $threats,
            logins = logins + $logins,
            logins_failed = logins_failed + $logins_failed,
            logins_success = logins_success + $logins_success,
            logins_threats = logins_threats + $logins_threats,
            logins_blocked = logins_blocked + $logins_blocked
        ";

			$query_date = $query . " WHERE date like '$date'";

			$exist = $wpdb->query( $query_date );

			if ( ! $exist ) {

				$stats['date'] = $date;

				// Add
				$result = $wpdb->insert( Yoda::get_table_stats(), $stats );

				if ( ! $result ) {

					// Create Stats Table
					Janitor::create_table_stats();

					// Try Again now that a table exist
					$wpdb->insert( Yoda::get_table_stats(), $stats );

				}

			}

		}

		/**
		 * Logs activity into database
		 *
		 * @param array $args
		 *
		 * @since 2.1.1
		 */
		public static function log_activity( array $args ) : void {

			$user = wp_get_current_user();

			// Log Actual Activity
			$args               = ( is_array( $args ) ) ? $args : [];
			$args['type']       = 'activity';
			$args['threats']    = '0';
			$args['score']      = '0';
			$args['user_agent'] = Yoda::get_user_agent();
			$args['username']   = ( isset( $user->user_login ) ) ? $user->user_login : 'unknown';
			$args['ip']         = Yoda::get_ip();
			$args['status']     = ( defined( 'DOING_CRON' ) ) ? 'automatic' : 'unknown';
			$args['status']     = ( $args['status'] == 'unknown' && isset( $user->user_login ) ) ? 'manual' : $args['status'];

			Janitor::add_entry( $args );

		}

		/**
		 * Removes the settings from the database on plugin deactivation
		 *
		 * @since  0.3.5
		 */
		public static function disable_plugin() : void {

			// Remove Cron
			wp_clear_scheduled_hook( 'secsafe_cleanup_tables_daily' );

			$site_settings = get_option( SECSAFE_OPTIONS );

			// Check to see if the user wants us to cleanup the data
			if ( isset( $site_settings['general']['cleanup'] ) && $site_settings['general']['cleanup'] == '1' ) {

				// Delete Settings
				delete_option( SECSAFE_OPTIONS );

				// Delete Tables
				Janitor::drop_table( SECSAFE_DB_FIREWALL );
				Janitor::drop_table( SECSAFE_DB_STATS );

			} else {

				// Log Activity
				$args            = [];
				$args['details'] = sprintf( __( '%s plugin disabled.', SECSAFE_TRANSLATE ), SECSAFE_NAME );
				Janitor::log_activity( $args );

			}

		}

		/**
		 * Drop table in the database
		 *
		 * @param string $table
		 *
		 * @return bool
		 *
		 * @since  2.0.0
		 */
		private static function drop_table( string $table ) : bool {

			global $wpdb;

			$dropped = false;

			if ( in_array( $table, [ SECSAFE_DB_FIREWALL, SECSAFE_DB_STATS ] ) ) {

				$table = $wpdb->prefix . $table;

				$dropped = $wpdb->query( "DROP TABLE IF EXISTS $table" );

			}

			return $dropped;

		}

		/**
		 * This cleans up the database when the daily cron runs
		 *
		 * @since 2.0.0
		 */
		public static function cleanup_tables() : void {

			Janitor::cleanup_type( '404s' );
			Janitor::cleanup_type( 'logins' );
			Janitor::cleanup_type( 'activity' );
			Janitor::expire_type( 'allow_deny' );

		}

		/**
		 * Deletes all rows in excess of limit for a specific type
		 *
		 * @param string $type
		 *
		 * @since 2.0.0
		 */
		private static function cleanup_type( string $type ) : void {

			global $wpdb;

			$types = Yoda::get_types();

			// Require Valid Type
			if ( isset( $types[ $type ] ) ) {

				$args = [];

				$limit = Yoda::get_display_limits( $type, true );

				$table_main = Yoda::get_table_main();

				$query = "SELECT COUNT(type) FROM $table_main WHERE type = '$type'";

				// Count how many exist
				$exists = (int) $wpdb->get_var( $query );

				$args['details'] = '[';

				// If more than limit
				if ( $exists > $limit ) {

					// Calculate amount to delete
					$delete = $exists - $limit;

					$query = "DELETE FROM $table_main WHERE type = '$type' ORDER BY date ASC LIMIT $delete";

					$result = $wpdb->query( $query );

					$args['details'] .= (int) $result . '-' . $exists . '-' . $limit;

				} else {

					$args['details'] = '0-' . $exists . '-' . $limit;

				}

				$args['details'] .= '] ' . $type . ' ' . __( 'database maintenance', SECSAFE_TRANSLATE ) . '.';

				// Log Activity
				Janitor::log_activity( $args );

			}

		}

		/**
		 * Deletes all rows for a specific type that have an expire date that is older than now.
		 *
		 * @param string $type
		 *
		 * @since 2.0.0
		 */
		private static function expire_type( string $type ) : void {

			global $wpdb;

			$args = [];

			// Cleanup Valid Types
			$types = Yoda::get_types();

			if ( isset( $types[ $type ] ) ) {

				$table_main = Yoda::get_table_main();

				$ago = date( 'Y-m-d H:i:s', strtotime( '-3 days' ) );

				$query = "DELETE FROM $table_main WHERE type = '$type' AND ( status = 'allow' OR status = 'deny') AND date_expire < '$ago' AND date_expire != '0000-00-00 00:00:00'";

				$result = $wpdb->query( $query );

				$args['details'] = '[' . (int) $result . '] ' . $type . ' ' . __( 'database maintenance', SECSAFE_TRANSLATE ) . '.';

			} else {

				$args['details'] = sprintf( __( 'Error: %s is not a valid type.', SECSAFE_TRANSLATE ), $type );

			}

			// Log Activity
			Janitor::log_activity( $args );

		}

	}