find_issues(); $items = array(); foreach ( $issues as $issue ) { $pass = 'ok' === $issue['status']; $items[] = array( 'id' => $issue['id'], 'label' => $issue['label'], 'pass' => $pass, 'fix' => $issue['fix'], 'optional' => ! empty( $issue['optional'] ), 'needs_dev' => ! empty( $issue['needs_dev'] ), 'severity' => $issue['severity'], ); } $passed = 0; $failed = 0; foreach ( $items as $item ) { if ( $item['pass'] ) { ++$passed; } elseif ( empty( $item['optional'] ) ) { ++$failed; } } return array( 'passed' => $passed, 'failed' => $failed, 'items' => $items, 'issues' => $issues, 'ready' => 0 === $failed, ); } /** * Detailed security issues. * * @return array */ public function find_issues() { $issues = array(); $home = home_url( '/' ); if ( 0 === strpos( $home, 'https://' ) ) { $issues[] = $this->issue( 'https', __( 'Site address uses HTTPS', '7labs-debug-branding-for-commerce7' ), 'ok', '', false, false ); } else { $issues[] = $this->issue( 'https', __( 'Site address uses HTTPS', '7labs-debug-branding-for-commerce7' ), 'error', __( 'Switch WordPress Address and Site Address to https:// in Settings → General, then enforce SSL at your host or with a security plugin.', '7labs-debug-branding-for-commerce7' ), false, true ); } if ( defined( 'WP_DEBUG' ) && WP_DEBUG && defined( 'WP_DEBUG_DISPLAY' ) && WP_DEBUG_DISPLAY ) { $issues[] = $this->issue( 'debug_display', __( 'Debug errors hidden from visitors', '7labs-debug-branding-for-commerce7' ), 'warn', __( 'Turn off WP_DEBUG_DISPLAY on production — errors can leak paths and confuse shoppers.', '7labs-debug-branding-for-commerce7' ), false, true ); } else { $issues[] = $this->issue( 'debug_display', __( 'Debug errors hidden from visitors', '7labs-debug-branding-for-commerce7' ), 'ok', '', false, false ); } if ( version_compare( PHP_VERSION, '8.0', '<' ) ) { $issues[] = $this->issue( 'php_version', __( 'PHP 8.0 or newer', '7labs-debug-branding-for-commerce7' ), 'warn', sprintf( /* translators: %s: PHP version */ __( 'This server runs PHP %s. Ask your host to upgrade — older PHP versions stop receiving security updates.', '7labs-debug-branding-for-commerce7' ), PHP_VERSION ), true, true ); } else { $issues[] = $this->issue( 'php_version', __( 'PHP 8.0 or newer', '7labs-debug-branding-for-commerce7' ), 'ok', '', true, false ); } $admin_user = get_user_by( 'login', 'admin' ); if ( $admin_user ) { $issues[] = $this->issue( 'admin_username', __( 'No default “admin” login username', '7labs-debug-branding-for-commerce7' ), 'warn', __( 'Create a new administrator account, log in with it, then delete or demote the “admin” user.', '7labs-debug-branding-for-commerce7' ), true, false ); } else { $issues[] = $this->issue( 'admin_username', __( 'No default “admin” login username', '7labs-debug-branding-for-commerce7' ), 'ok', '', true, false ); } if ( ! defined( 'DISALLOW_FILE_EDIT' ) || ! DISALLOW_FILE_EDIT ) { $issues[] = $this->issue( 'file_editor', __( 'Theme/plugin file editor disabled', '7labs-debug-branding-for-commerce7' ), 'warn', __( 'Add define( \'DISALLOW_FILE_EDIT\', true ); to wp-config.php so a compromised login cannot edit code from wp-admin.', '7labs-debug-branding-for-commerce7' ), true, true ); } else { $issues[] = $this->issue( 'file_editor', __( 'Theme/plugin file editor disabled', '7labs-debug-branding-for-commerce7' ), 'ok', '', true, false ); } $core_update = $this->pending_core_update(); if ( $core_update ) { $issues[] = $this->issue( 'core_updates', __( 'WordPress core is up to date', '7labs-debug-branding-for-commerce7' ), 'warn', sprintf( /* translators: %s: version */ __( 'WordPress %s is available — update from Dashboard → Updates.', '7labs-debug-branding-for-commerce7' ), $core_update ), true, false ); } else { $issues[] = $this->issue( 'core_updates', __( 'WordPress core is up to date', '7labs-debug-branding-for-commerce7' ), 'ok', '', true, false ); } $plugin_updates = $this->pending_plugin_update_count(); if ( $plugin_updates > 0 ) { $issues[] = $this->issue( 'plugin_updates', __( 'Plugins are up to date', '7labs-debug-branding-for-commerce7' ), 'warn', sprintf( /* translators: %d: count */ _n( '%d plugin update is waiting — review Dashboard → Updates before campaign traffic.', '%d plugin updates are waiting — review Dashboard → Updates before campaign traffic.', $plugin_updates, '7labs-debug-branding-for-commerce7' ), $plugin_updates ), true, false ); } else { $issues[] = $this->issue( 'plugin_updates', __( 'Plugins are up to date', '7labs-debug-branding-for-commerce7' ), 'ok', '', true, false ); } $log_path = WP_CONTENT_DIR . '/debug.log'; if ( file_exists( $log_path ) && filesize( $log_path ) > 500000 ) { $issues[] = $this->issue( 'debug_log', __( 'No large public debug log', '7labs-debug-branding-for-commerce7' ), 'warn', __( 'wp-content/debug.log is large — disable WP_DEBUG logging on production or rotate the file so errors are not exposed if the server misconfigures access.', '7labs-debug-branding-for-commerce7' ), true, true ); } else { $issues[] = $this->issue( 'debug_log', __( 'No large public debug log', '7labs-debug-branding-for-commerce7' ), 'ok', '', true, false ); } if ( $this->xmlrpc_responds() ) { $issues[] = $this->issue( 'xmlrpc', __( 'XML-RPC not publicly usable', '7labs-debug-branding-for-commerce7' ), 'warn', __( 'xmlrpc.php responded to a probe — disable or restrict it at the host/firewall if you do not need remote publishing.', '7labs-debug-branding-for-commerce7' ), true, true ); } else { $issues[] = $this->issue( 'xmlrpc', __( 'XML-RPC not publicly usable', '7labs-debug-branding-for-commerce7' ), 'ok', '', true, false ); } return $issues; } /** * Build one issue row. * * @param string $id ID. * @param string $label Label. * @param string $status ok|warn|error. * @param string $fix Fix hint. * @param bool $optional Optional checklist item. * @param bool $needs_dev Usually needs developer/host help. * @return array */ private function issue( $id, $label, $status, $fix, $optional, $needs_dev ) { return array( 'id' => $id, 'label' => $label, 'status' => $status, 'severity' => 'ok' === $status ? 'ok' : ( 'error' === $status ? 'error' : 'warn' ), 'message' => 'ok' === $status ? $label : $label, 'fix' => $fix, 'optional' => (bool) $optional, 'needs_dev' => (bool) $needs_dev, ); } /** * Pending core version string or empty. * * @return string */ private function pending_core_update() { if ( ! function_exists( 'get_core_updates' ) ) { require_once ABSPATH . 'wp-admin/includes/update.php'; } $updates = get_core_updates(); if ( ! empty( $updates ) && isset( $updates[0]->response ) && 'upgrade' === $updates[0]->response ) { return isset( $updates[0]->version ) ? (string) $updates[0]->version : 'upgrade'; } return ''; } /** * Count plugin updates available. * * @return int */ private function pending_plugin_update_count() { if ( ! function_exists( 'get_plugin_updates' ) ) { require_once ABSPATH . 'wp-admin/includes/update.php'; } $updates = get_plugin_updates(); return is_array( $updates ) ? count( $updates ) : 0; } /** * Probe xmlrpc.php (lightweight; skipped on loopback failure). * * @return bool True when endpoint appears open. */ private function xmlrpc_responds() { $url = site_url( '/xmlrpc.php' ); $response = wp_remote_post( $url, array( 'timeout' => 8, 'sslverify' => apply_filters( 'c7dbt_qa_sslverify', true ), 'headers' => array( 'User-Agent' => 'C7DBT-Security/' . C7DBT_VERSION, ), 'body' => 'system.listMethods', ) ); if ( is_wp_error( $response ) ) { return false; } $code = (int) wp_remote_retrieve_response_code( $response ); $body = (string) wp_remote_retrieve_body( $response ); return $code >= 200 && $code < 500 && false !== strpos( $body, 'methodResponse' ); } /** * Dashboard / health status. * * @return array */ public function status() { $report = $this->run(); $errors = array_filter( $report['issues'], static function ( $issue ) { return 'error' === $issue['severity']; } ); $warns = array_filter( $report['issues'], static function ( $issue ) { return 'warn' === $issue['severity']; } ); if ( $report['failed'] > 0 || count( $errors ) > 0 ) { return array( 'status' => 'error', 'label' => __( 'Security basics need attention', '7labs-debug-branding-for-commerce7' ), 'detail' => sprintf( /* translators: %d: count */ _n( '%d required security check failed.', '%d required security checks failed.', max( $report['failed'], count( $errors ) ), '7labs-debug-branding-for-commerce7' ), max( $report['failed'], count( $errors ) ) ), 'count' => $report['failed'], ); } if ( count( $warns ) > 0 ) { return array( 'status' => 'warn', 'label' => __( 'Security hardening recommended', '7labs-debug-branding-for-commerce7' ), 'detail' => sprintf( /* translators: %d: count */ _n( '%d optional hardening item.', '%d optional hardening items.', count( $warns ), '7labs-debug-branding-for-commerce7' ), count( $warns ) ), 'count' => count( $warns ), ); } return array( 'status' => 'ok', 'label' => __( 'Security basics look good', '7labs-debug-branding-for-commerce7' ), 'detail' => __( 'HTTPS, updates, and common hardening checks passed.', '7labs-debug-branding-for-commerce7' ), 'count' => 0, ); } }