Changeset 1970866
- Timestamp:
- 11/08/2018 11:31:33 AM (7 years ago)
- Location:
- wp-fingerprint
- Files:
-
- 30 added
- 1 deleted
- 7 edited
- 5 copied
-
tags/2.1 (copied) (copied from wp-fingerprint/trunk)
-
tags/2.1/README.txt (copied) (copied from wp-fingerprint/trunk/README.txt) (2 diffs)
-
tags/2.1/assets (added)
-
tags/2.1/assets/css (added)
-
tags/2.1/assets/css/admin-style.css (added)
-
tags/2.1/fingerprint-command.php (copied) (copied from wp-fingerprint/trunk/fingerprint-command.php) (1 diff)
-
tags/2.1/inc (copied) (copied from wp-fingerprint/trunk/inc)
-
tags/2.1/inc/class-wpfingerprint-diff.php (added)
-
tags/2.1/inc/class-wpfingerprint-hash.php (added)
-
tags/2.1/inc/class-wpfingerprint-model-checksums.php (added)
-
tags/2.1/inc/class-wpfingerprint-model-diffs.php (added)
-
tags/2.1/inc/class-wpfingerprint-plugins.php (modified) (5 diffs)
-
tags/2.1/inc/class-wpfingerprint-runner.php (modified) (1 diff)
-
tags/2.1/inc/class-wpfingerprint-settings-table.php (added)
-
tags/2.1/inc/class-wpfingerprint-settings.php (added)
-
tags/2.1/inc/class-wpfingerprint-transport-local.php (added)
-
tags/2.1/inc/class-wpfingerprint-transport-wpfingerprint.php (added)
-
tags/2.1/inc/class-wpfingerprint-transport-wporg.php (added)
-
tags/2.1/template (added)
-
tags/2.1/template/display-plugins.php (added)
-
tags/2.1/template/settings.php (added)
-
tags/2.1/wp-fingerprint.php (copied) (copied from wp-fingerprint/trunk/wp-fingerprint.php) (8 diffs)
-
trunk/README.txt (modified) (2 diffs)
-
trunk/assets (added)
-
trunk/assets/css (added)
-
trunk/assets/css/admin-style.css (added)
-
trunk/fingerprint-command.php (modified) (1 diff)
-
trunk/inc/class-wpfingerprint-diff.php (added)
-
trunk/inc/class-wpfingerprint-hash.php (added)
-
trunk/inc/class-wpfingerprint-model-checksums.php (added)
-
trunk/inc/class-wpfingerprint-model-diffs.php (added)
-
trunk/inc/class-wpfingerprint-plugins.php (modified) (5 diffs)
-
trunk/inc/class-wpfingerprint-runner.php (modified) (1 diff)
-
trunk/inc/class-wpfingerprint-settings-table.php (added)
-
trunk/inc/class-wpfingerprint-settings.php (added)
-
trunk/inc/class-wpfingerprint-transport-local.php (added)
-
trunk/inc/class-wpfingerprint-transport-wpfingerprint.php (added)
-
trunk/inc/class-wpfingerprint-transport-wporg.php (added)
-
trunk/template (added)
-
trunk/template/display-plugins.php (added)
-
trunk/template/settings.php (added)
-
trunk/wp-fingerprint.php (modified) (8 diffs)
-
trunk/wpfingerprint.php (deleted)
Legend:
- Unmodified
- Added
- Removed
-
wp-fingerprint/tags/2.1/README.txt
r1853983 r1970866 3 3 Tags: security, plugins, checksums 4 4 Requires at least: 4.9 5 Tested up to: 4.9. 45 Tested up to: 4.9.8 6 6 Requires PHP: 5.4 7 7 License: GPLv3 8 Stable tag: 1.0.18 Stable tag: 2.1.0 9 9 == Description == 10 10 WP Fingerprint adds an additional layer of security to your WordPress website, working to check your plugins for signs of hack or exploit. WP Fingerprint works by collecting checksums of your plugins and comparing it with the checksums collected by WP Fingerprint. If the plugin detects any abnormalities it will let you know so you can take immediate action. 11 This plugin transmits and stores checksums on WP Fingerprint servers (all hosted in EU and run by 34SP.com)to work for details see https://wpfingerprint.com/how-it-works/ for the data we collect and store.11 This plugin transmits and stores checksums on WP Fingerprint servers(all hosted in EU and run by 34SP.com) & WordPress.org to work for details see https://wpfingerprint.com/how-it-works/ for the data we collect and store. 12 12 == Installation == 13 13 As normal click activate to activate the plugin and go make a cup of tea while it works away in the background. Allow an hour after installation for WPFingerprint to complete its first set of checks. … … 26 26 27 27 == Changelog == 28 2.1 - Remove notice in admin section, refactored the primary checker, added ability to diff files if source allows, added WP-CLI commands for report and Diff - 16th October 2018 29 2.0.4 - Show the source with a human friendly name, clear down logs so they are not showing spurious data 30 2.0.3 - Added a "what does this mean notification", created clear logs wp-cli command - 26th September 2018 31 2.0.2 - Fixed issue where incorrectly announcing that checksums did not match - 24th September 2018 32 2.0.1 - Fixed bug where Notification counter wasn't showing 33 2.0.0 - Rewritten WP Fingerprint see blog post for full details - 19th September 2018 28 34 1.0.1 - 6th April 2018 29 35 Bug fix - Removed notice, when plugin is not in the results array -
wp-fingerprint/tags/2.1/fingerprint-command.php
r1829106 r1970866 2 2 class Fingerprint_Command extends WP_CLI_Command { 3 3 4 function run( $args, $assoc_args ) 4 /* 5 * Check Plugin against Checksums 6 * @alias run 7 */ 8 function check( $args, $assoc_args ) 5 9 { 6 $path = plugin_dir_path( __FILE__ ); 7 $runner = new WPFingerprint_Runner($path); 8 if(array_key_exists('force',$assoc_args)) 10 $wpfingerprint = new WPFingerprint_Plugin(); 11 if(isset( $wpfingerprint )) 9 12 { 13 $plugin = false; 14 if(isset($args[0])){ 15 $plugin = $args[0]; 16 } 17 $diffs = $wpfingerprint->runner($plugin); 18 $message = 'WP Fingerprint has found: '; 19 $message .= strval($diffs['new_issues']).' New Issues '; 20 $message .= 'has updated '.strval($diffs['updated_issues']).' issues'; 21 $message .= ' and removed '.strval($diffs['removed_isues']).' issues'; 22 WP_CLI::success( $message ); 23 }else{ 24 WP_CLI::error( 'Error Running WP Fingerprint' ); 25 } 10 26 11 $runner->plugins->remove_plugin_checksums(); 27 } 28 /* 29 * Create Checksum for a given plugin 30 * 31 */ 32 function generate( $args, $assoc_args ) 33 { 34 $wpfingerprint = new WPFingerprint_Plugin(); 35 if(empty($args)) $args = array(); 36 $generate = $wpfingerprint->generate($args[0]); 37 if(!empty($generate) || !is_array($generate)){ 38 WP_CLI::error( 'Generate Plugin failed' ); 12 39 } 13 $runner->run(); 40 else{ 41 return json_encode($generate); 42 } 43 } 44 /* 45 * Show any plugins that have currently failed. 46 * 47 */ 48 function report( $args, $assoc_args ) 49 { 50 $format = 'table'; 51 if(isset($assoc_args['format'])) 52 { 53 $format_options = array( 54 'table','csv','yaml','json' 55 ); 56 if(in_array( $assoc_args['format'], $format_options )) 57 { 58 $format = $assoc_args['format']; 59 } 60 } 61 $plugin = false; 62 if(!empty($args)) 63 { 64 $plugin = $args[0]; 65 } 66 $wpfingerprint = new WPFingerprint_Plugin(); 67 $report = $wpfingerprint->runner->report($plugin); 68 WP_CLI\Utils\format_items( $format, $report, 'plugin,file,checksum_local, checksum_remote, last_checked' ); 14 69 } 15 70 71 function diff( $args, $assoc_args ) 72 { 73 if(!empty($args)) 74 { 75 $plugin = $args[0]; 76 $file = $args[1]; 77 } 78 if(!isset($plugin)) 79 { 80 WP_CLI::error( 'Need to specify a plugin' ); 81 die; 82 } 83 if(!isset($file)) 84 { 85 WP_CLI::error( 'Need to specify a file' ); 86 die; 87 } 88 $wpfingerprint = new WPFingerprint_Plugin(); 89 $version = $wpfingerprint->runner->plugins->get_plugin_version($plugin); 90 $wpfingerprint->runner->load('transport-wporg'); 91 $transport_name = 'WPFingerprint_Transport_Wporg'; 92 $transport_name = new $transport_name; 93 $local_file = $wpfingerprint->runner->plugins->read_file_contents($plugin,$file); 94 if(!$local_file) 95 { 96 WP_CLI::error( 'Local file could not be found' ); 97 die; 98 } 99 $remote_file = $transport_name->get_plugin_file($plugin,$version,$file); 100 if(!$remote_file) 101 { 102 WP_CLI::error( 'Remote file could not be found' ); 103 die; 104 } 105 $diff_file = $wpfingerprint->runner->diff->check_file_diff($local_file,$remote_file); 106 $format = 'table'; 107 if(isset($assoc_args['format'])) 108 { 109 $format_options = array( 110 'table','csv','yaml','json' 111 ); 112 if(in_array( $assoc_args['format'], $format_options )) 113 { 114 $format = $assoc_args['format']; 115 } 116 } 117 $report = $wpfingerprint->runner->diff->show_diffs($diff_file); 118 WP_CLI\Utils\format_items( $format, $report, 'line,local,remote' ); 119 } 120 /* 121 * Delete the logs 122 * 123 */ 124 function clear( $args, $assoc_args ) 125 { 126 $wpfingerprint = new WPFingerprint_Plugin(); 127 $wpfingerprint->runner->model_checksums->clear(); 128 WP_CLI::success( 'Wiped Logs' ); 129 } 16 130 } 17 131 WP_CLI::add_command( 'fingerprint', 'Fingerprint_Command' ); -
wp-fingerprint/tags/2.1/inc/class-wpfingerprint-plugins.php
r1829106 r1970866 1 1 <?php 2 2 class WPFingerprint_Plugins{ 3 3 4 4 5 private $plugins; … … 6 7 function __construct() 7 8 { 8 $this->plugins = array(); 9 9 10 } 10 11 11 12 function get() 12 13 { 13 if(empty($this->plugins)) { 14 15 if(empty($this->plugins) && !is_array($this->plugins)) { 14 16 if ( ! function_exists( 'get_plugins' ) ) { 15 17 require_once ABSPATH . 'wp-admin/includes/plugin.php'; … … 54 56 } 55 57 58 function get_all_plugin_names() { 59 $names = array(); 60 foreach ( get_plugins() as $file => $details ) { 61 $names[] = $this->get_plugin_name( $file ); 62 } 63 return $names; 64 } 65 66 function get_some_plugins_names($plugin) 67 { 68 $plugins = array(); 69 if(!is_array($plugin)) 70 { 71 return $plugins[] = $plugin; 72 } 73 else{ 74 //Explicit assumption this is a flat array 75 return $plugin; 76 } 77 } 78 79 function get_plugin_files( $path ) { 80 $folder = dirname( $this->get_absolute_path( $path ) ); 81 if ( WP_PLUGIN_DIR === $folder ) { 82 return (array) $path; 83 } 84 return $this->get_files( trailingslashit( $folder ) ); 85 } 86 87 function get_files( $path ) { 88 $filtered_files = array(); 89 try { 90 $files = new RecursiveIteratorIterator( 91 new RecursiveDirectoryIterator( $path, 92 RecursiveDirectoryIterator::SKIP_DOTS ), 93 RecursiveIteratorIterator::CHILD_FIRST 94 ); 95 foreach ( $files as $file_info ) { 96 $pathname = self::normalize_directory_separators( substr( $file_info->getPathname(), strlen( $path ) ) ); 97 if ( $file_info->isFile()) { 98 $filtered_files[] = $pathname; 99 } 100 } 101 } catch ( Exception $e ) { 102 103 } 104 return $filtered_files; 105 } 106 107 public static function normalize_directory_separators( $path ) { 108 return str_replace( '\\', '/', $path ); 109 } 110 111 function get_plugin_version($slug) 112 { 113 $plugins = $this->get(); 114 foreach($plugins as $plugin) 115 { 116 if($plugin['TextDomain'] == $slug) 117 { 118 return $plugin['Version']; 119 } 120 } 121 } 122 56 123 function get_plugin_checksum($slug) 57 124 { … … 68 135 return update_option('wpfingerprint_checksum', $checksums); 69 136 } 137 70 138 function remove_plugin_checksums() 71 139 { … … 73 141 } 74 142 143 function read_file_contents($plugin, $file) 144 { 145 146 return file_get_contents($this->path($plugin).'/'.$file); 147 } 148 149 function filter_files($slug) 150 { 151 $no_check = array( 152 'readme.txt', 153 'readme.md', 154 'README.md', 155 'README.txt' 156 ); 157 if( in_array($slug, $no_check)) return true; 158 return false; 159 } 160 75 161 } -
wp-fingerprint/tags/2.1/inc/class-wpfingerprint-runner.php
r1829106 r1970866 1 1 <?php 2 2 class WPFingerprint_Runner{ 3 public $plugins; 4 public $hash; 5 public $model_checksums; 6 public $diff; 7 public $model_diffs; 8 public $transport; 3 9 private $path; 4 public $plugins; 5 function __construct($path) 6 { 7 $this->path = $path; 8 $this->load('plugins'); 10 11 function __construct() 12 { 13 if(isset($this->path)){ 14 $this->path = plugin_dir_path( __FILE__ ); 15 } 9 16 if(empty($this->plugins)){ 17 $this->load('plugins'); 10 18 $this->plugins = new WPFingerprint_Plugins; 11 19 } 12 $this->load('api'); 13 } 14 function run() 15 { 16 17 $check_plugins = $this->plugins->all(); 18 $return_plugins = array(); 19 var_dump($check_plugins); 20 foreach ( $check_plugins as $check => $att ) 21 { 22 if(is_dir($att['path'])){ 23 $files = new RecursiveIteratorIterator( 24 new RecursiveDirectoryIterator( $att['path'], 25 RecursiveDirectoryIterator::SKIP_DOTS ), 26 RecursiveIteratorIterator::CHILD_FIRST 27 ); 28 $dir_files =array(); 29 30 foreach($files as $file_info){ 31 $pathname = substr( $file_info->getPathname(), strlen($att['path']) ); 32 $dir_files[$pathname] = hash_file( 'md5', $file_info->getPathname() ); 33 } 34 $att['files'] = $dir_files; 35 unset($dir_files); 36 } 37 else{ 38 $att['files'][$att['path']] = hash_file( 'md5', $att['path']); 39 } 40 $result_checksums = $this->validate_checksums($att['files']); 41 if($att['checksum'] == $result_checksums) 42 { 43 //Don't send the data as its the same as last time 44 unset( $check_plugins[$check]); 45 } 46 else{ 47 $this->plugins->update_plugin_checksums($att['slug'], $result_checksums); 48 $check_plugins[$check] = $att; 49 } 50 51 } 52 var_dump($check_plugins); 53 if( !empty($check_plugins)){ 54 $api = new WPFingerprint_API; 55 $api->fire($check_plugins); 56 } 57 return; 58 } 59 60 function validate_checksums($files){ 61 $string = ''; 62 foreach($files as $file) 63 { 64 $string .= $file; 65 } 66 return hash( 'md5', $string); 67 } 68 20 21 if(empty($this->hash)){ 22 $this->load('hash'); 23 $this->hash = new WPFingerprint_Hash; 24 } 25 if(empty($this->model_checksums)){ 26 $this->load('model-checksums'); 27 $this->model_checksums = new WPFingerprint_Model_Checksums; 28 } 29 if(empty($this->diff)){ 30 31 $this->load('diff'); 32 $this->diff = new WPFingerprint_Diff; 33 } 34 if(empty($this->model_diffs)){ 35 $this->load('model-diffs'); 36 $this->model_diffs = new WPFingerprint_Model_Diffs; 37 } 38 } 69 39 //poormans autoloader 70 40 function load($class) 71 41 { 72 73 return require_once $this->path . 'inc/class-wpfingerprint-'.$class.'.php'; 42 return require_once $this->path . 'class-wpfingerprint-'.$class.'.php'; 43 } 44 45 46 /* 47 * Run command, Compares Checksums 48 * 49 */ 50 51 function run( $plugin = false ) 52 { 53 $time = time(); 54 /* 55 * Step 1 - Get plugins to check 56 */ 57 $plugins_list = array(); 58 $plugin_info = array(); 59 if(!isset($plugin) || !$plugin) 60 { 61 62 //Get all plugins 63 $plugins_list = $this->plugins->get_all_plugin_names(); 64 } 65 else{ 66 $plugins_list = $this->plugins->get_some_plugins_names($plugin); 67 } 68 //We have nothing to do. 69 if(empty($plugins_list)) return false; 70 /* 71 * Step 2 - Get local checksums. 72 */ 73 $local_checksums = array(); 74 foreach($plugins_list as $plugin_name) 75 { 76 $local_checksums[$plugin_name] = $this->generate_plugin_checksum( $plugin_name ); 77 } 78 79 //We have nothing to do. 80 if(empty($local_checksums)) return false; 81 82 /* 83 * Step 3, 4 - Get Remote Checksums 84 */ 85 86 $remote_checksums = array(); 87 foreach($plugins_list as $plugin_name) 88 { 89 $remote_get = array(); 90 $remote_get = $this->generate_remote_plugin_checksum( $plugin_name ); 91 if(!empty($remote_get)){ 92 $remote_checksums[$plugin_name] = $remote_get['checksums']; 93 $plugin_info[$plugin_name] = array( 94 'source' => $remote_get['source'], 95 'version' => $remote_get['version'], 96 ); 97 } 98 } 99 100 //We have nothing to do. 101 if(empty($remote_checksums)) return false; 102 103 /* 104 * Step 5,6,7 - Do comparison 105 */ 106 $added_files = array(); 107 $not_valid_checksums = array(); 108 $file_diffs = array(); 109 110 foreach($plugins_list as $plugin_name) 111 { 112 $compare = array(); 113 if(!empty($remote_checksums[$plugin_name]) && is_array($remote_checksums[$plugin_name])) 114 { 115 $compare = $this->compare_checksums( $local_checksums[$plugin_name], $remote_checksums[$plugin_name]); 116 } 117 if(!empty($compare['added'])) 118 { 119 $added_files[$plugin_name] = $compare['added']; 120 } 121 if(!empty($compare['invalid'])) 122 { 123 /* 124 * Step 8 - Check if Support Diff 125 */ 126 if($this->diff_available($plugin_info[$plugin_name]['source'])) 127 { 128 $version = $plugin_info[$plugin_name]['version']; 129 $files = array_keys($compare['invalid']); 130 $file_diffs[$plugin_name] = $this->generate_plugin_diff($plugin_name, $version, $files, $plugin_info[$plugin_name]['source']); 131 if(is_array($file_diffs) && !empty($file_diffs)) 132 { 133 foreach($file_diffs as $file_diff_file => $file_diff_contents) 134 { 135 if(empty($file_diff_file) || !is_array($file_diff_file)) 136 { 137 //Remove from file Diffs and $not_valid_checksums 138 unset($compare['invalid'][$file_diff_file]); 139 unset($file_diffs[$file_diff_file]); 140 } 141 } 142 } 143 } 144 $not_valid_checksums[$plugin_name] = $compare['invalid']; 145 } 146 } 147 /* 148 * Step 10 & 11 Get Previous Fails, Store new ones 149 */ 150 151 $previous_fails = $this->get_previous_fails(); 152 $update_ids = array(); 153 $remove_ids = array(); 154 if(!empty($previous_fails)) 155 { 156 foreach($previous_fails as $plugin => $versions) 157 { 158 if(array_key_exists($plugin, $not_valid_checksums)) 159 { 160 foreach($versions as $version => $content) 161 { 162 if($plugin_info[$plugin]['version'] == $version ) 163 { 164 foreach($content as $file => $file_checksums) 165 { 166 if(array_key_exists($file, $not_valid_checksums[$plugin])) 167 { 168 //ok compare the Local Checksums 169 if($file_checksums['local_checksum'] == $not_valid_checksums[$plugin][$file][0]) 170 { 171 //Update the time sheet 172 $update_ids[] = $file_checksums['id']; 173 //unset 174 unset($not_valid_checksums[$plugin][$file]); 175 } 176 } 177 else{ 178 //Remove ID 179 $remove_ids[] = $file_checksums['id']; 180 } 181 } 182 } 183 else{ 184 foreach($content as $file) 185 { 186 //Remove no longer seen checksums by Version 187 $remove_ids[] = $file['id']; 188 } 189 } 190 } 191 192 } 193 } 194 195 } 196 //Remove IDs 197 if(!empty($remove_ids) && is_array($remove_ids)) 198 { 199 $this->remove_checksums($remove_ids); 200 } 201 //Update IDs 202 if(!empty($update_ids) && is_array($update_ids)) 203 { 204 $this->update_checksums($update_ids); 205 } 206 //Add New Checksums 207 if(!empty($not_valid_checksums) && is_array($not_valid_checksums)) 208 { 209 foreach($not_valid_checksums as $plugin => $files) 210 { 211 if(is_array($files) && !empty($files)) 212 { 213 $this->add_checksums($plugin, $plugin_info[$plugin]['version'],$files, $plugin_info[$plugin]['source']); 214 } 215 else{ 216 //Clean it up 217 unset($not_valid_checksums[$plugin]); 218 } 219 } 220 } 221 222 /* 223 * Tidy up 224 */ 225 //Finished 226 update_option( 'wpfingerprint_last_run', time() ); 227 $return = array( 228 'time_taken' => time() - $time, 229 'new_issues' => count($not_valid_checksums,1), 230 'removed_isues' => count($remove_ids,0), 231 'updated_issues' => count($update_ids,0) 232 ); 233 do_action('wp_fingerprint_runner',array($not_valid_checksums,$return)); 234 return $return; 235 } 236 237 function compare_checksums($local_checksums, $remote_checksums) 238 { 239 $added_file = array(); 240 $invalid_checksum = array(); 241 foreach($local_checksums as $file => $checksum) 242 { 243 /* 244 * Step 5/6 - filter files 245 */ 246 if(!$this->plugins->filter_files($file)){ 247 /* 248 * Step 7 - Look for added and invalid checksums 249 */ 250 if(!isset($remote_checksums[$file])) 251 { 252 $added_file[$file] = $checksum; 253 } 254 elseif($remote_checksums[$file] != $checksum) 255 { 256 if(!is_array($remote_checksums[$file]) || !in_array($checksum, $remote_checksums[$file])) 257 { 258 $invalid_checksum[$file] = array($checksum,$remote_checksums[$file]); 259 } 260 } 261 } 262 } 263 return array( 264 'added' => $added_file, 265 'invalid' => $invalid_checksum 266 ); 267 } 268 269 function diff_available( $transport ) 270 { 271 $transport_object = 'transport-'.$transport; 272 return $this->$transport_object->get_option('diff'); 273 } 274 275 function generate_plugin_checksum( $plugin = false ) 276 { 277 if(!isset($plugin) || $plugin == false) return; 278 279 $path = $this->plugins->path($plugin); 280 $files = $this->plugins->get_files($path); 281 $checksums = array(); 282 283 foreach ( $files as $file ) 284 { 285 $checksums[ltrim($file,'/')] = $this->hash->get_sha256( $path.$file ); 286 } 287 return $checksums; 288 } 289 290 function generate_remote_plugin_checksum( $plugin = false ) 291 { 292 if(!isset($plugin) || $plugin == false) return; 293 $transports = $this->select_transport( $plugin ); 294 $version = $this->plugins->get_plugin_version( $plugin ); 295 foreach($transports as $transport) 296 { 297 $transport_object = 'transport-'.$transport; 298 $remote_checksums = $this->$transport_object->get_plugin_checksums( $plugin, $version ); 299 if(!empty($remote_checksums) ) 300 { 301 return array( 302 'source' => $transport, 303 'version' => $version, 304 'checksums' => $remote_checksums, 305 ); 306 } 307 } 308 return false; 309 310 } 311 312 function generate_plugin_diff( $plugin, $version, $files, $transport) 313 { 314 if(!isset($plugin) || $plugin == false) return false; 315 if(!isset($version) || $version == false) return false; 316 $transport_object = 'transport-'.$transport; 317 $diffs = array(); 318 foreach ( $files as $file ) 319 { 320 $local_file = $this->plugins->read_file_contents($plugin,$file); 321 $remote_file = $this->$transport_object->get_plugin_file($plugin,$version,$file); 322 323 $diff_file = $this->diff->check_file_diff($local_file,$remote_file); 324 $diffs[$file] = $this->diff->show_diffs($diff_file); 325 } 326 return $diffs; 327 } 328 329 function get_previous_fails() 330 { 331 $get_fails = $this->model_checksums->get(); 332 if( empty($get_fails) ) return false; 333 334 $failed_plugins = array(); 335 foreach($get_fails as $fail) 336 { 337 $checksum = array( 338 'id' => $fail->id, 339 'local_checksum' => $fail->checksum_local, 340 'remote_checksum' => $fail->checksum_remote 341 ); 342 $failed_plugins[$fail->plugin][$fail->version][$fail->filename] = $checksum; 343 } 344 return $failed_plugins; 345 } 346 347 function remove_checksums($ids) 348 { 349 foreach($ids as $id) 350 { 351 $this->model_checksums->remove($id); 352 } 353 } 354 355 function update_checksums($ids) 356 { 357 foreach($ids as $id) 358 { 359 $this->model_checksums->update_last_checked($id); 360 } 361 } 362 function add_checksums($plugin, $version, $files, $source) 363 { 364 foreach($files as $filename => $data) 365 { 366 $checksums = array( 367 'local' => $data[0], 368 'remote' => $data[1], 369 'source' => $source 370 ); 371 $this->model_checksums->set($plugin, $version,$filename,$checksums); 372 } 373 } 374 375 private function select_transport( $plugin = false ) 376 { 377 $transports = array( 378 'wporg', 379 'local', 380 'wpfingerprint' 381 ); 382 //Add and load our transports 383 foreach( $transports as $transport ) 384 { 385 $transport_object = 'transport-'.$transport; 386 $this->load( $transport_object ); 387 $transport_name = 'WPFingerprint_Transport_'.ucwords( $transport ); 388 $this->$transport_object = new $transport_name; 389 } 390 //Add your own transport such as a remote repository, need to load your own handler 391 return apply_filters( 'wp_fingerprint_transports', $transports, $plugin ); 392 } 393 394 function report( $plugin = false) 395 { 396 $report = array(); 397 $files = $this->model_checksums->get($plugin); 398 foreach($files as $file) 399 { 400 $report[] = array( 401 'plugin' => $file->plugin, 402 'file' => $file->filename, 403 'checksum_local' => $file->checksum_local, 404 'checksum_remote' => $file->checksum_remote, 405 'last_checked' => $file->last_checked 406 ); 407 } 408 return $report; 74 409 } 75 410 } -
wp-fingerprint/tags/2.1/wp-fingerprint.php
r1853983 r1970866 3 3 Plugin Name: WP Fingerprint 4 4 Description: WP Fingerprint adds an additional layer of security to your WordPress website, working to check your plugins for signs of hack or exploit 5 Version: 1.0.15 Version: 2.1.0 6 6 Author: 34SP.com 7 7 Author URI: https://www.34SP.com … … 10 10 11 11 private $path; 12 private $runner; 12 public $runner; 13 private $db_version = 3; 13 14 14 15 public function __construct( ) … … 23 24 $this->runner = new WPFingerprint_Runner($this->path); 24 25 } 26 /* 27 * Runs our migrations for updates 28 */ 29 if($this->db_version > get_option('wpfingerprint_db_version',0)) 30 { 31 $this->runner->model_checksums->migrate($this->db_version); 32 $this->runner->model_diffs->migrate($this->db_version); 33 update_option('wpfingerprint_db_version',$this->db_version); 34 } 25 35 } 26 36 … … 28 38 { 29 39 //load additional additional actions 30 add_action( 'rest_api_init', array($this,'webhook'));31 40 add_action( 'admin_init', array($this,'admin_init')); 32 41 add_action( 'wpfingerprint_cron', array($this,'cron')); 42 add_action( 'wpfingerprint_run_now', array($this,'cron')); 33 43 } 34 44 35 45 function admin_init( ) 36 46 { 47 //Admin settings loading 48 $this->runner->load('settings'); 49 //No point adding the filters to a screen they wont see. 50 if( current_user_can('manage_options') ){ 51 //Only folks who can do something with plugins are alerted 52 add_action( 'admin_footer', array('WPFingerprint_Settings', 'admin_bar_footer_js') ); 53 add_action( 'wp_ajax_wp-fingerprint-recheck', array('WPFingerprint_Settings', 'recheck_callback') ); 54 add_action( 'admin_bar_menu', array('WPFingerprint_Settings', 'admin_bar_menu'),110 ); 55 } 37 56 /* Settings and hooks specific to Plugin Page */ 38 57 global $pagenow; 39 //No point adding the filters to a screen they wont see.40 58 if( current_user_can('activate_plugins') && $pagenow == 'plugins.php'){ 41 59 if ( ! function_exists( 'get_plugins' ) ) { … … 47 65 foreach($all_plugins as $key => $value){ 48 66 $hook = 'after_plugin_row_'.$key; 49 add_action( $hook , array( $this,'notices'), 10,3);67 add_action( $hook , array('WPFingerprint_Settings','notices'), 10,3); 50 68 } 51 //add_action( 'admin_notices', array($this,'after_activation_notice') );52 69 } 53 70 } … … 55 72 function runner( ) 56 73 { 57 $this->runner->run();74 return $this->runner->run(); 58 75 } 76 59 77 //Trigger the cron 60 78 function cron( ) … … 63 81 $this->runner(); 64 82 } 65 return;66 83 } 67 84 68 function webhook( )69 {70 $api = new WPFingerprint_API;71 register_rest_route( 'wpfingerprint/v1', '/webhook',72 array(73 'method' => 'POST',74 'callback' => array($api, 'webhook'),75 )76 );77 register_rest_route( 'wpfingerprint/v1', '/webhook',78 array(79 'method' => 'GET',80 'callback' => array($api, 'webhook')81 )82 );83 }84 85 function notices ($plugin_file, $plugin_data, $status )86 {87 $plugins = $this->runner->plugins;88 $invalid_plugins = get_option( 'wpfingerprint_invalid' );89 if(!is_array($invalid_plugins))90 {91 $invalid_plugins = array('results' => array());92 }93 $slug = $plugins->get_plugin_name( $plugin_data['Name'] );94 if( !in_array( $slug, array_keys( $invalid_plugins['results'] ) ) ) return;95 $return;96 $return .= '<tr id="wpfingerprint-warning">';97 $return .= '<td colspan="3" class="plugin-update colspanchange">';98 if($this->notice_type())99 {100 $return .= '<div class="warning inline warning-error warning-alt">';101 $return .= '<p><strong>WARNING</strong> - WP Fingerprint has detected that the following files may have been tampered with:</p>';102 }103 else{104 $return .= '<div class="notice inline notice-error notice-alt">';105 $return .= '<p>WP Fingerprint has detected that the following files may have been tampered with:</p>';106 }107 108 foreach( $invalid_plugins['results'][$slug] as $path => $result)109 {110 $return .= '<p>';111 $return .= esc_html($path);112 if($result['score'] != '100' && $result['type'] == 'modified')113 {114 $return .= sprintf( esc_html__( "does not match %s percent of installs seen.", "wpfingerprint"), $result['score'] );115 }116 if($result['type'] == 'malicious')117 {118 $return .= esc_html__(" is known to be malicious", 'wpfingerprint');119 }120 if($result['type'] == 'added')121 {122 $return .= esc_html__('has been added since download', 'wpfingerprint');123 }124 $return .= '</p>';125 }126 $return .= sprintf( esc_html__( "Last Check: %s", 'wpfingerint'), date('Y-m-d H:i:s', $invalid_plugins['timestamp']) );127 $return .= '</div></td></tr>';128 129 $return = apply_filters('wpfingerprint_output', $return, $path, $result);130 echo $return;131 }132 function notice_type($results)133 {134 $return = false;135 if(array_search('malicious', array_column($results,'type'))) $return = true;136 return $return;137 }138 139 function after_activation_notice( )140 {141 if( get_transient( 'wpfingerprint-first-run' ) ){142 $return = ' <div class="updated notice is-dismissible"><p>';143 $return .= esc_html("WP Fingerprint is currently validating your plugin files, this process may take up to an hour. Go enjoy the internet for a bit!", 'wpfingerprint');144 $return .= '</p></div>';145 echo $return;146 }147 }148 85 public function activation_hook() { 149 set_transient( 'wpfingerprint-first-run', true, 2 * HOUR_IN_SECONDS ); 86 add_option('wpfingerprint_last_run', time()); 87 add_option('wpfingerprint_mode', 'cron'); 88 add_option('wpfingerprint_fails', 0); 150 89 if (! wp_next_scheduled ( 'wpfingerprint_cron' )) { 151 90 wp_schedule_event(time(), 'hourly', 'wpfingerprint_cron'); 152 91 } 92 153 93 } 154 94 public function deactivation_hook() { … … 157 97 delete_option('wpfingerprint_invalid'); 158 98 delete_option('wpfingerprint_checksum'); 159 delete_transient('wpfingerprint-first-run'); 99 delete_option('wpfingerprint_db_version'); 100 delete_option('wpfingerprint_last_run'); 101 delete_option('wpfingerprint_fails', 0); 160 102 } 161 103 } -
wp-fingerprint/trunk/README.txt
r1853983 r1970866 3 3 Tags: security, plugins, checksums 4 4 Requires at least: 4.9 5 Tested up to: 4.9. 45 Tested up to: 4.9.8 6 6 Requires PHP: 5.4 7 7 License: GPLv3 8 Stable tag: 1.0.18 Stable tag: 2.1.0 9 9 == Description == 10 10 WP Fingerprint adds an additional layer of security to your WordPress website, working to check your plugins for signs of hack or exploit. WP Fingerprint works by collecting checksums of your plugins and comparing it with the checksums collected by WP Fingerprint. If the plugin detects any abnormalities it will let you know so you can take immediate action. 11 This plugin transmits and stores checksums on WP Fingerprint servers (all hosted in EU and run by 34SP.com)to work for details see https://wpfingerprint.com/how-it-works/ for the data we collect and store.11 This plugin transmits and stores checksums on WP Fingerprint servers(all hosted in EU and run by 34SP.com) & WordPress.org to work for details see https://wpfingerprint.com/how-it-works/ for the data we collect and store. 12 12 == Installation == 13 13 As normal click activate to activate the plugin and go make a cup of tea while it works away in the background. Allow an hour after installation for WPFingerprint to complete its first set of checks. … … 26 26 27 27 == Changelog == 28 2.1 - Remove notice in admin section, refactored the primary checker, added ability to diff files if source allows, added WP-CLI commands for report and Diff - 16th October 2018 29 2.0.4 - Show the source with a human friendly name, clear down logs so they are not showing spurious data 30 2.0.3 - Added a "what does this mean notification", created clear logs wp-cli command - 26th September 2018 31 2.0.2 - Fixed issue where incorrectly announcing that checksums did not match - 24th September 2018 32 2.0.1 - Fixed bug where Notification counter wasn't showing 33 2.0.0 - Rewritten WP Fingerprint see blog post for full details - 19th September 2018 28 34 1.0.1 - 6th April 2018 29 35 Bug fix - Removed notice, when plugin is not in the results array -
wp-fingerprint/trunk/fingerprint-command.php
r1829106 r1970866 2 2 class Fingerprint_Command extends WP_CLI_Command { 3 3 4 function run( $args, $assoc_args ) 4 /* 5 * Check Plugin against Checksums 6 * @alias run 7 */ 8 function check( $args, $assoc_args ) 5 9 { 6 $path = plugin_dir_path( __FILE__ ); 7 $runner = new WPFingerprint_Runner($path); 8 if(array_key_exists('force',$assoc_args)) 10 $wpfingerprint = new WPFingerprint_Plugin(); 11 if(isset( $wpfingerprint )) 9 12 { 13 $plugin = false; 14 if(isset($args[0])){ 15 $plugin = $args[0]; 16 } 17 $diffs = $wpfingerprint->runner($plugin); 18 $message = 'WP Fingerprint has found: '; 19 $message .= strval($diffs['new_issues']).' New Issues '; 20 $message .= 'has updated '.strval($diffs['updated_issues']).' issues'; 21 $message .= ' and removed '.strval($diffs['removed_isues']).' issues'; 22 WP_CLI::success( $message ); 23 }else{ 24 WP_CLI::error( 'Error Running WP Fingerprint' ); 25 } 10 26 11 $runner->plugins->remove_plugin_checksums(); 27 } 28 /* 29 * Create Checksum for a given plugin 30 * 31 */ 32 function generate( $args, $assoc_args ) 33 { 34 $wpfingerprint = new WPFingerprint_Plugin(); 35 if(empty($args)) $args = array(); 36 $generate = $wpfingerprint->generate($args[0]); 37 if(!empty($generate) || !is_array($generate)){ 38 WP_CLI::error( 'Generate Plugin failed' ); 12 39 } 13 $runner->run(); 40 else{ 41 return json_encode($generate); 42 } 43 } 44 /* 45 * Show any plugins that have currently failed. 46 * 47 */ 48 function report( $args, $assoc_args ) 49 { 50 $format = 'table'; 51 if(isset($assoc_args['format'])) 52 { 53 $format_options = array( 54 'table','csv','yaml','json' 55 ); 56 if(in_array( $assoc_args['format'], $format_options )) 57 { 58 $format = $assoc_args['format']; 59 } 60 } 61 $plugin = false; 62 if(!empty($args)) 63 { 64 $plugin = $args[0]; 65 } 66 $wpfingerprint = new WPFingerprint_Plugin(); 67 $report = $wpfingerprint->runner->report($plugin); 68 WP_CLI\Utils\format_items( $format, $report, 'plugin,file,checksum_local, checksum_remote, last_checked' ); 14 69 } 15 70 71 function diff( $args, $assoc_args ) 72 { 73 if(!empty($args)) 74 { 75 $plugin = $args[0]; 76 $file = $args[1]; 77 } 78 if(!isset($plugin)) 79 { 80 WP_CLI::error( 'Need to specify a plugin' ); 81 die; 82 } 83 if(!isset($file)) 84 { 85 WP_CLI::error( 'Need to specify a file' ); 86 die; 87 } 88 $wpfingerprint = new WPFingerprint_Plugin(); 89 $version = $wpfingerprint->runner->plugins->get_plugin_version($plugin); 90 $wpfingerprint->runner->load('transport-wporg'); 91 $transport_name = 'WPFingerprint_Transport_Wporg'; 92 $transport_name = new $transport_name; 93 $local_file = $wpfingerprint->runner->plugins->read_file_contents($plugin,$file); 94 if(!$local_file) 95 { 96 WP_CLI::error( 'Local file could not be found' ); 97 die; 98 } 99 $remote_file = $transport_name->get_plugin_file($plugin,$version,$file); 100 if(!$remote_file) 101 { 102 WP_CLI::error( 'Remote file could not be found' ); 103 die; 104 } 105 $diff_file = $wpfingerprint->runner->diff->check_file_diff($local_file,$remote_file); 106 $format = 'table'; 107 if(isset($assoc_args['format'])) 108 { 109 $format_options = array( 110 'table','csv','yaml','json' 111 ); 112 if(in_array( $assoc_args['format'], $format_options )) 113 { 114 $format = $assoc_args['format']; 115 } 116 } 117 $report = $wpfingerprint->runner->diff->show_diffs($diff_file); 118 WP_CLI\Utils\format_items( $format, $report, 'line,local,remote' ); 119 } 120 /* 121 * Delete the logs 122 * 123 */ 124 function clear( $args, $assoc_args ) 125 { 126 $wpfingerprint = new WPFingerprint_Plugin(); 127 $wpfingerprint->runner->model_checksums->clear(); 128 WP_CLI::success( 'Wiped Logs' ); 129 } 16 130 } 17 131 WP_CLI::add_command( 'fingerprint', 'Fingerprint_Command' ); -
wp-fingerprint/trunk/inc/class-wpfingerprint-plugins.php
r1829106 r1970866 1 1 <?php 2 2 class WPFingerprint_Plugins{ 3 3 4 4 5 private $plugins; … … 6 7 function __construct() 7 8 { 8 $this->plugins = array(); 9 9 10 } 10 11 11 12 function get() 12 13 { 13 if(empty($this->plugins)) { 14 15 if(empty($this->plugins) && !is_array($this->plugins)) { 14 16 if ( ! function_exists( 'get_plugins' ) ) { 15 17 require_once ABSPATH . 'wp-admin/includes/plugin.php'; … … 54 56 } 55 57 58 function get_all_plugin_names() { 59 $names = array(); 60 foreach ( get_plugins() as $file => $details ) { 61 $names[] = $this->get_plugin_name( $file ); 62 } 63 return $names; 64 } 65 66 function get_some_plugins_names($plugin) 67 { 68 $plugins = array(); 69 if(!is_array($plugin)) 70 { 71 return $plugins[] = $plugin; 72 } 73 else{ 74 //Explicit assumption this is a flat array 75 return $plugin; 76 } 77 } 78 79 function get_plugin_files( $path ) { 80 $folder = dirname( $this->get_absolute_path( $path ) ); 81 if ( WP_PLUGIN_DIR === $folder ) { 82 return (array) $path; 83 } 84 return $this->get_files( trailingslashit( $folder ) ); 85 } 86 87 function get_files( $path ) { 88 $filtered_files = array(); 89 try { 90 $files = new RecursiveIteratorIterator( 91 new RecursiveDirectoryIterator( $path, 92 RecursiveDirectoryIterator::SKIP_DOTS ), 93 RecursiveIteratorIterator::CHILD_FIRST 94 ); 95 foreach ( $files as $file_info ) { 96 $pathname = self::normalize_directory_separators( substr( $file_info->getPathname(), strlen( $path ) ) ); 97 if ( $file_info->isFile()) { 98 $filtered_files[] = $pathname; 99 } 100 } 101 } catch ( Exception $e ) { 102 103 } 104 return $filtered_files; 105 } 106 107 public static function normalize_directory_separators( $path ) { 108 return str_replace( '\\', '/', $path ); 109 } 110 111 function get_plugin_version($slug) 112 { 113 $plugins = $this->get(); 114 foreach($plugins as $plugin) 115 { 116 if($plugin['TextDomain'] == $slug) 117 { 118 return $plugin['Version']; 119 } 120 } 121 } 122 56 123 function get_plugin_checksum($slug) 57 124 { … … 68 135 return update_option('wpfingerprint_checksum', $checksums); 69 136 } 137 70 138 function remove_plugin_checksums() 71 139 { … … 73 141 } 74 142 143 function read_file_contents($plugin, $file) 144 { 145 146 return file_get_contents($this->path($plugin).'/'.$file); 147 } 148 149 function filter_files($slug) 150 { 151 $no_check = array( 152 'readme.txt', 153 'readme.md', 154 'README.md', 155 'README.txt' 156 ); 157 if( in_array($slug, $no_check)) return true; 158 return false; 159 } 160 75 161 } -
wp-fingerprint/trunk/inc/class-wpfingerprint-runner.php
r1829106 r1970866 1 1 <?php 2 2 class WPFingerprint_Runner{ 3 public $plugins; 4 public $hash; 5 public $model_checksums; 6 public $diff; 7 public $model_diffs; 8 public $transport; 3 9 private $path; 4 public $plugins; 5 function __construct($path) 6 { 7 $this->path = $path; 8 $this->load('plugins'); 10 11 function __construct() 12 { 13 if(isset($this->path)){ 14 $this->path = plugin_dir_path( __FILE__ ); 15 } 9 16 if(empty($this->plugins)){ 17 $this->load('plugins'); 10 18 $this->plugins = new WPFingerprint_Plugins; 11 19 } 12 $this->load('api'); 13 } 14 function run() 15 { 16 17 $check_plugins = $this->plugins->all(); 18 $return_plugins = array(); 19 var_dump($check_plugins); 20 foreach ( $check_plugins as $check => $att ) 21 { 22 if(is_dir($att['path'])){ 23 $files = new RecursiveIteratorIterator( 24 new RecursiveDirectoryIterator( $att['path'], 25 RecursiveDirectoryIterator::SKIP_DOTS ), 26 RecursiveIteratorIterator::CHILD_FIRST 27 ); 28 $dir_files =array(); 29 30 foreach($files as $file_info){ 31 $pathname = substr( $file_info->getPathname(), strlen($att['path']) ); 32 $dir_files[$pathname] = hash_file( 'md5', $file_info->getPathname() ); 33 } 34 $att['files'] = $dir_files; 35 unset($dir_files); 36 } 37 else{ 38 $att['files'][$att['path']] = hash_file( 'md5', $att['path']); 39 } 40 $result_checksums = $this->validate_checksums($att['files']); 41 if($att['checksum'] == $result_checksums) 42 { 43 //Don't send the data as its the same as last time 44 unset( $check_plugins[$check]); 45 } 46 else{ 47 $this->plugins->update_plugin_checksums($att['slug'], $result_checksums); 48 $check_plugins[$check] = $att; 49 } 50 51 } 52 var_dump($check_plugins); 53 if( !empty($check_plugins)){ 54 $api = new WPFingerprint_API; 55 $api->fire($check_plugins); 56 } 57 return; 58 } 59 60 function validate_checksums($files){ 61 $string = ''; 62 foreach($files as $file) 63 { 64 $string .= $file; 65 } 66 return hash( 'md5', $string); 67 } 68 20 21 if(empty($this->hash)){ 22 $this->load('hash'); 23 $this->hash = new WPFingerprint_Hash; 24 } 25 if(empty($this->model_checksums)){ 26 $this->load('model-checksums'); 27 $this->model_checksums = new WPFingerprint_Model_Checksums; 28 } 29 if(empty($this->diff)){ 30 31 $this->load('diff'); 32 $this->diff = new WPFingerprint_Diff; 33 } 34 if(empty($this->model_diffs)){ 35 $this->load('model-diffs'); 36 $this->model_diffs = new WPFingerprint_Model_Diffs; 37 } 38 } 69 39 //poormans autoloader 70 40 function load($class) 71 41 { 72 73 return require_once $this->path . 'inc/class-wpfingerprint-'.$class.'.php'; 42 return require_once $this->path . 'class-wpfingerprint-'.$class.'.php'; 43 } 44 45 46 /* 47 * Run command, Compares Checksums 48 * 49 */ 50 51 function run( $plugin = false ) 52 { 53 $time = time(); 54 /* 55 * Step 1 - Get plugins to check 56 */ 57 $plugins_list = array(); 58 $plugin_info = array(); 59 if(!isset($plugin) || !$plugin) 60 { 61 62 //Get all plugins 63 $plugins_list = $this->plugins->get_all_plugin_names(); 64 } 65 else{ 66 $plugins_list = $this->plugins->get_some_plugins_names($plugin); 67 } 68 //We have nothing to do. 69 if(empty($plugins_list)) return false; 70 /* 71 * Step 2 - Get local checksums. 72 */ 73 $local_checksums = array(); 74 foreach($plugins_list as $plugin_name) 75 { 76 $local_checksums[$plugin_name] = $this->generate_plugin_checksum( $plugin_name ); 77 } 78 79 //We have nothing to do. 80 if(empty($local_checksums)) return false; 81 82 /* 83 * Step 3, 4 - Get Remote Checksums 84 */ 85 86 $remote_checksums = array(); 87 foreach($plugins_list as $plugin_name) 88 { 89 $remote_get = array(); 90 $remote_get = $this->generate_remote_plugin_checksum( $plugin_name ); 91 if(!empty($remote_get)){ 92 $remote_checksums[$plugin_name] = $remote_get['checksums']; 93 $plugin_info[$plugin_name] = array( 94 'source' => $remote_get['source'], 95 'version' => $remote_get['version'], 96 ); 97 } 98 } 99 100 //We have nothing to do. 101 if(empty($remote_checksums)) return false; 102 103 /* 104 * Step 5,6,7 - Do comparison 105 */ 106 $added_files = array(); 107 $not_valid_checksums = array(); 108 $file_diffs = array(); 109 110 foreach($plugins_list as $plugin_name) 111 { 112 $compare = array(); 113 if(!empty($remote_checksums[$plugin_name]) && is_array($remote_checksums[$plugin_name])) 114 { 115 $compare = $this->compare_checksums( $local_checksums[$plugin_name], $remote_checksums[$plugin_name]); 116 } 117 if(!empty($compare['added'])) 118 { 119 $added_files[$plugin_name] = $compare['added']; 120 } 121 if(!empty($compare['invalid'])) 122 { 123 /* 124 * Step 8 - Check if Support Diff 125 */ 126 if($this->diff_available($plugin_info[$plugin_name]['source'])) 127 { 128 $version = $plugin_info[$plugin_name]['version']; 129 $files = array_keys($compare['invalid']); 130 $file_diffs[$plugin_name] = $this->generate_plugin_diff($plugin_name, $version, $files, $plugin_info[$plugin_name]['source']); 131 if(is_array($file_diffs) && !empty($file_diffs)) 132 { 133 foreach($file_diffs as $file_diff_file => $file_diff_contents) 134 { 135 if(empty($file_diff_file) || !is_array($file_diff_file)) 136 { 137 //Remove from file Diffs and $not_valid_checksums 138 unset($compare['invalid'][$file_diff_file]); 139 unset($file_diffs[$file_diff_file]); 140 } 141 } 142 } 143 } 144 $not_valid_checksums[$plugin_name] = $compare['invalid']; 145 } 146 } 147 /* 148 * Step 10 & 11 Get Previous Fails, Store new ones 149 */ 150 151 $previous_fails = $this->get_previous_fails(); 152 $update_ids = array(); 153 $remove_ids = array(); 154 if(!empty($previous_fails)) 155 { 156 foreach($previous_fails as $plugin => $versions) 157 { 158 if(array_key_exists($plugin, $not_valid_checksums)) 159 { 160 foreach($versions as $version => $content) 161 { 162 if($plugin_info[$plugin]['version'] == $version ) 163 { 164 foreach($content as $file => $file_checksums) 165 { 166 if(array_key_exists($file, $not_valid_checksums[$plugin])) 167 { 168 //ok compare the Local Checksums 169 if($file_checksums['local_checksum'] == $not_valid_checksums[$plugin][$file][0]) 170 { 171 //Update the time sheet 172 $update_ids[] = $file_checksums['id']; 173 //unset 174 unset($not_valid_checksums[$plugin][$file]); 175 } 176 } 177 else{ 178 //Remove ID 179 $remove_ids[] = $file_checksums['id']; 180 } 181 } 182 } 183 else{ 184 foreach($content as $file) 185 { 186 //Remove no longer seen checksums by Version 187 $remove_ids[] = $file['id']; 188 } 189 } 190 } 191 192 } 193 } 194 195 } 196 //Remove IDs 197 if(!empty($remove_ids) && is_array($remove_ids)) 198 { 199 $this->remove_checksums($remove_ids); 200 } 201 //Update IDs 202 if(!empty($update_ids) && is_array($update_ids)) 203 { 204 $this->update_checksums($update_ids); 205 } 206 //Add New Checksums 207 if(!empty($not_valid_checksums) && is_array($not_valid_checksums)) 208 { 209 foreach($not_valid_checksums as $plugin => $files) 210 { 211 if(is_array($files) && !empty($files)) 212 { 213 $this->add_checksums($plugin, $plugin_info[$plugin]['version'],$files, $plugin_info[$plugin]['source']); 214 } 215 else{ 216 //Clean it up 217 unset($not_valid_checksums[$plugin]); 218 } 219 } 220 } 221 222 /* 223 * Tidy up 224 */ 225 //Finished 226 update_option( 'wpfingerprint_last_run', time() ); 227 $return = array( 228 'time_taken' => time() - $time, 229 'new_issues' => count($not_valid_checksums,1), 230 'removed_isues' => count($remove_ids,0), 231 'updated_issues' => count($update_ids,0) 232 ); 233 do_action('wp_fingerprint_runner',array($not_valid_checksums,$return)); 234 return $return; 235 } 236 237 function compare_checksums($local_checksums, $remote_checksums) 238 { 239 $added_file = array(); 240 $invalid_checksum = array(); 241 foreach($local_checksums as $file => $checksum) 242 { 243 /* 244 * Step 5/6 - filter files 245 */ 246 if(!$this->plugins->filter_files($file)){ 247 /* 248 * Step 7 - Look for added and invalid checksums 249 */ 250 if(!isset($remote_checksums[$file])) 251 { 252 $added_file[$file] = $checksum; 253 } 254 elseif($remote_checksums[$file] != $checksum) 255 { 256 if(!is_array($remote_checksums[$file]) || !in_array($checksum, $remote_checksums[$file])) 257 { 258 $invalid_checksum[$file] = array($checksum,$remote_checksums[$file]); 259 } 260 } 261 } 262 } 263 return array( 264 'added' => $added_file, 265 'invalid' => $invalid_checksum 266 ); 267 } 268 269 function diff_available( $transport ) 270 { 271 $transport_object = 'transport-'.$transport; 272 return $this->$transport_object->get_option('diff'); 273 } 274 275 function generate_plugin_checksum( $plugin = false ) 276 { 277 if(!isset($plugin) || $plugin == false) return; 278 279 $path = $this->plugins->path($plugin); 280 $files = $this->plugins->get_files($path); 281 $checksums = array(); 282 283 foreach ( $files as $file ) 284 { 285 $checksums[ltrim($file,'/')] = $this->hash->get_sha256( $path.$file ); 286 } 287 return $checksums; 288 } 289 290 function generate_remote_plugin_checksum( $plugin = false ) 291 { 292 if(!isset($plugin) || $plugin == false) return; 293 $transports = $this->select_transport( $plugin ); 294 $version = $this->plugins->get_plugin_version( $plugin ); 295 foreach($transports as $transport) 296 { 297 $transport_object = 'transport-'.$transport; 298 $remote_checksums = $this->$transport_object->get_plugin_checksums( $plugin, $version ); 299 if(!empty($remote_checksums) ) 300 { 301 return array( 302 'source' => $transport, 303 'version' => $version, 304 'checksums' => $remote_checksums, 305 ); 306 } 307 } 308 return false; 309 310 } 311 312 function generate_plugin_diff( $plugin, $version, $files, $transport) 313 { 314 if(!isset($plugin) || $plugin == false) return false; 315 if(!isset($version) || $version == false) return false; 316 $transport_object = 'transport-'.$transport; 317 $diffs = array(); 318 foreach ( $files as $file ) 319 { 320 $local_file = $this->plugins->read_file_contents($plugin,$file); 321 $remote_file = $this->$transport_object->get_plugin_file($plugin,$version,$file); 322 323 $diff_file = $this->diff->check_file_diff($local_file,$remote_file); 324 $diffs[$file] = $this->diff->show_diffs($diff_file); 325 } 326 return $diffs; 327 } 328 329 function get_previous_fails() 330 { 331 $get_fails = $this->model_checksums->get(); 332 if( empty($get_fails) ) return false; 333 334 $failed_plugins = array(); 335 foreach($get_fails as $fail) 336 { 337 $checksum = array( 338 'id' => $fail->id, 339 'local_checksum' => $fail->checksum_local, 340 'remote_checksum' => $fail->checksum_remote 341 ); 342 $failed_plugins[$fail->plugin][$fail->version][$fail->filename] = $checksum; 343 } 344 return $failed_plugins; 345 } 346 347 function remove_checksums($ids) 348 { 349 foreach($ids as $id) 350 { 351 $this->model_checksums->remove($id); 352 } 353 } 354 355 function update_checksums($ids) 356 { 357 foreach($ids as $id) 358 { 359 $this->model_checksums->update_last_checked($id); 360 } 361 } 362 function add_checksums($plugin, $version, $files, $source) 363 { 364 foreach($files as $filename => $data) 365 { 366 $checksums = array( 367 'local' => $data[0], 368 'remote' => $data[1], 369 'source' => $source 370 ); 371 $this->model_checksums->set($plugin, $version,$filename,$checksums); 372 } 373 } 374 375 private function select_transport( $plugin = false ) 376 { 377 $transports = array( 378 'wporg', 379 'local', 380 'wpfingerprint' 381 ); 382 //Add and load our transports 383 foreach( $transports as $transport ) 384 { 385 $transport_object = 'transport-'.$transport; 386 $this->load( $transport_object ); 387 $transport_name = 'WPFingerprint_Transport_'.ucwords( $transport ); 388 $this->$transport_object = new $transport_name; 389 } 390 //Add your own transport such as a remote repository, need to load your own handler 391 return apply_filters( 'wp_fingerprint_transports', $transports, $plugin ); 392 } 393 394 function report( $plugin = false) 395 { 396 $report = array(); 397 $files = $this->model_checksums->get($plugin); 398 foreach($files as $file) 399 { 400 $report[] = array( 401 'plugin' => $file->plugin, 402 'file' => $file->filename, 403 'checksum_local' => $file->checksum_local, 404 'checksum_remote' => $file->checksum_remote, 405 'last_checked' => $file->last_checked 406 ); 407 } 408 return $report; 74 409 } 75 410 } -
wp-fingerprint/trunk/wp-fingerprint.php
r1853983 r1970866 3 3 Plugin Name: WP Fingerprint 4 4 Description: WP Fingerprint adds an additional layer of security to your WordPress website, working to check your plugins for signs of hack or exploit 5 Version: 1.0.15 Version: 2.1.0 6 6 Author: 34SP.com 7 7 Author URI: https://www.34SP.com … … 10 10 11 11 private $path; 12 private $runner; 12 public $runner; 13 private $db_version = 3; 13 14 14 15 public function __construct( ) … … 23 24 $this->runner = new WPFingerprint_Runner($this->path); 24 25 } 26 /* 27 * Runs our migrations for updates 28 */ 29 if($this->db_version > get_option('wpfingerprint_db_version',0)) 30 { 31 $this->runner->model_checksums->migrate($this->db_version); 32 $this->runner->model_diffs->migrate($this->db_version); 33 update_option('wpfingerprint_db_version',$this->db_version); 34 } 25 35 } 26 36 … … 28 38 { 29 39 //load additional additional actions 30 add_action( 'rest_api_init', array($this,'webhook'));31 40 add_action( 'admin_init', array($this,'admin_init')); 32 41 add_action( 'wpfingerprint_cron', array($this,'cron')); 42 add_action( 'wpfingerprint_run_now', array($this,'cron')); 33 43 } 34 44 35 45 function admin_init( ) 36 46 { 47 //Admin settings loading 48 $this->runner->load('settings'); 49 //No point adding the filters to a screen they wont see. 50 if( current_user_can('manage_options') ){ 51 //Only folks who can do something with plugins are alerted 52 add_action( 'admin_footer', array('WPFingerprint_Settings', 'admin_bar_footer_js') ); 53 add_action( 'wp_ajax_wp-fingerprint-recheck', array('WPFingerprint_Settings', 'recheck_callback') ); 54 add_action( 'admin_bar_menu', array('WPFingerprint_Settings', 'admin_bar_menu'),110 ); 55 } 37 56 /* Settings and hooks specific to Plugin Page */ 38 57 global $pagenow; 39 //No point adding the filters to a screen they wont see.40 58 if( current_user_can('activate_plugins') && $pagenow == 'plugins.php'){ 41 59 if ( ! function_exists( 'get_plugins' ) ) { … … 47 65 foreach($all_plugins as $key => $value){ 48 66 $hook = 'after_plugin_row_'.$key; 49 add_action( $hook , array( $this,'notices'), 10,3);67 add_action( $hook , array('WPFingerprint_Settings','notices'), 10,3); 50 68 } 51 //add_action( 'admin_notices', array($this,'after_activation_notice') );52 69 } 53 70 } … … 55 72 function runner( ) 56 73 { 57 $this->runner->run();74 return $this->runner->run(); 58 75 } 76 59 77 //Trigger the cron 60 78 function cron( ) … … 63 81 $this->runner(); 64 82 } 65 return;66 83 } 67 84 68 function webhook( )69 {70 $api = new WPFingerprint_API;71 register_rest_route( 'wpfingerprint/v1', '/webhook',72 array(73 'method' => 'POST',74 'callback' => array($api, 'webhook'),75 )76 );77 register_rest_route( 'wpfingerprint/v1', '/webhook',78 array(79 'method' => 'GET',80 'callback' => array($api, 'webhook')81 )82 );83 }84 85 function notices ($plugin_file, $plugin_data, $status )86 {87 $plugins = $this->runner->plugins;88 $invalid_plugins = get_option( 'wpfingerprint_invalid' );89 if(!is_array($invalid_plugins))90 {91 $invalid_plugins = array('results' => array());92 }93 $slug = $plugins->get_plugin_name( $plugin_data['Name'] );94 if( !in_array( $slug, array_keys( $invalid_plugins['results'] ) ) ) return;95 $return;96 $return .= '<tr id="wpfingerprint-warning">';97 $return .= '<td colspan="3" class="plugin-update colspanchange">';98 if($this->notice_type())99 {100 $return .= '<div class="warning inline warning-error warning-alt">';101 $return .= '<p><strong>WARNING</strong> - WP Fingerprint has detected that the following files may have been tampered with:</p>';102 }103 else{104 $return .= '<div class="notice inline notice-error notice-alt">';105 $return .= '<p>WP Fingerprint has detected that the following files may have been tampered with:</p>';106 }107 108 foreach( $invalid_plugins['results'][$slug] as $path => $result)109 {110 $return .= '<p>';111 $return .= esc_html($path);112 if($result['score'] != '100' && $result['type'] == 'modified')113 {114 $return .= sprintf( esc_html__( "does not match %s percent of installs seen.", "wpfingerprint"), $result['score'] );115 }116 if($result['type'] == 'malicious')117 {118 $return .= esc_html__(" is known to be malicious", 'wpfingerprint');119 }120 if($result['type'] == 'added')121 {122 $return .= esc_html__('has been added since download', 'wpfingerprint');123 }124 $return .= '</p>';125 }126 $return .= sprintf( esc_html__( "Last Check: %s", 'wpfingerint'), date('Y-m-d H:i:s', $invalid_plugins['timestamp']) );127 $return .= '</div></td></tr>';128 129 $return = apply_filters('wpfingerprint_output', $return, $path, $result);130 echo $return;131 }132 function notice_type($results)133 {134 $return = false;135 if(array_search('malicious', array_column($results,'type'))) $return = true;136 return $return;137 }138 139 function after_activation_notice( )140 {141 if( get_transient( 'wpfingerprint-first-run' ) ){142 $return = ' <div class="updated notice is-dismissible"><p>';143 $return .= esc_html("WP Fingerprint is currently validating your plugin files, this process may take up to an hour. Go enjoy the internet for a bit!", 'wpfingerprint');144 $return .= '</p></div>';145 echo $return;146 }147 }148 85 public function activation_hook() { 149 set_transient( 'wpfingerprint-first-run', true, 2 * HOUR_IN_SECONDS ); 86 add_option('wpfingerprint_last_run', time()); 87 add_option('wpfingerprint_mode', 'cron'); 88 add_option('wpfingerprint_fails', 0); 150 89 if (! wp_next_scheduled ( 'wpfingerprint_cron' )) { 151 90 wp_schedule_event(time(), 'hourly', 'wpfingerprint_cron'); 152 91 } 92 153 93 } 154 94 public function deactivation_hook() { … … 157 97 delete_option('wpfingerprint_invalid'); 158 98 delete_option('wpfingerprint_checksum'); 159 delete_transient('wpfingerprint-first-run'); 99 delete_option('wpfingerprint_db_version'); 100 delete_option('wpfingerprint_last_run'); 101 delete_option('wpfingerprint_fails', 0); 160 102 } 161 103 }
Note: See TracChangeset
for help on using the changeset viewer.